diff options
Diffstat (limited to 'library')
520 files changed, 8479 insertions, 5056 deletions
diff --git a/library/Cargo.lock b/library/Cargo.lock index 7b9081d46a0..930db2ecd57 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.143" +version = "0.1.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85ba2077e3eab3dd81be4ece6b7fb2ad0887c1fb813e9a45400baf75c6c7c29" +checksum = "a97117b1434b79833f39a5fabdf82f890bd98c1988334dea1cb67f7e627fa311" dependencies = [ "cc", "rustc-std-workspace-core", @@ -72,6 +72,10 @@ dependencies = [ [[package]] name = "core" version = "0.0.0" + +[[package]] +name = "coretests" +version = "0.0.0" dependencies = [ "rand", "rand_xorshift", diff --git a/library/Cargo.toml b/library/Cargo.toml index e59aa518804..1205f7c9ed6 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -3,6 +3,7 @@ resolver = "1" members = [ "std", "sysroot", + "coretests", ] exclude = [ diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 96caac890a3..6b0b5761391 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "=0.1.143", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "=0.1.146", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/alloc/benches/btree/map.rs b/library/alloc/benches/btree/map.rs index b8119c9f0eb..5b15aaeddbc 100644 --- a/library/alloc/benches/btree/map.rs +++ b/library/alloc/benches/btree/map.rs @@ -353,6 +353,7 @@ pub fn iter_10k(b: &mut Bencher) { } #[bench] +#[cfg_attr(target_os = "emscripten", ignore)] // hits an OOM pub fn iter_1m(b: &mut Bencher) { bench_iter(b, 1_000, 1_000_000); } diff --git a/library/alloc/benches/slice.rs b/library/alloc/benches/slice.rs index 48c74c4491d..c45c3722712 100644 --- a/library/alloc/benches/slice.rs +++ b/library/alloc/benches/slice.rs @@ -366,14 +366,25 @@ rotate!(rotate_medium_half, gen_random, 9158, 9158 / 2); rotate!(rotate_medium_half_plus_one, gen_random, 9158, 9158 / 2 + 1); // Intended to use more RAM than the machine has cache +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1, gen_random, 5 * 1024 * 1024, 1); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_u64, gen_random, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_bytes, gen_random_bytes, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_strings, gen_strings, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_big, gen_big_random, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_u64, gen_random, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_bytes, gen_random_bytes, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_strings, gen_strings, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_big, gen_big_random, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_half, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_half_plus_one, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2 + 1); diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs index d29ffae9d70..a725ad6894b 100644 --- a/library/alloc/benches/vec.rs +++ b/library/alloc/benches/vec.rs @@ -547,6 +547,11 @@ fn bench_in_place_collect_droppable(b: &mut Bencher) { }) } +// node.js gives out of memory error to use with length 1_100_000 +#[cfg(target_os = "emscripten")] +const LEN: usize = 4096; + +#[cfg(not(target_os = "emscripten"))] const LEN: usize = 16384; #[bench] diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index e9b7f985667..e686a02f29b 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -10,7 +10,7 @@ use core::hint; #[cfg(not(test))] use core::ptr::{self, NonNull}; -extern "Rust" { +unsafe extern "Rust" { // These are the magic symbols to call the global allocator. rustc generates // them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute // (the code expanding that attribute macro generates those functions), or to call @@ -355,7 +355,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { // # Allocation error handler #[cfg(not(no_global_oom_handling))] -extern "Rust" { +unsafe extern "Rust" { // This is the magic symbol to call the global alloc error handler. rustc generates // it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the // default implementations below (`__rdl_oom`) otherwise. @@ -426,7 +426,7 @@ pub mod __alloc_error_handler { // `#[alloc_error_handler]`. #[rustc_std_internal_symbol] pub unsafe fn __rdl_oom(size: usize, _align: usize) -> ! { - extern "Rust" { + unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. static __rust_alloc_error_handler_should_panic: u8; diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 1b5e44a9134..8b38e6fc259 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1115,6 +1115,8 @@ impl<T: ?Sized> Box<T> { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same `NonNull` pointer. /// + /// The non-null pointer must point to a block of memory allocated by the global allocator. + /// /// The safety conditions are described in the [memory layout] section. /// /// # Examples @@ -1170,7 +1172,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The raw pointer must point to a block of memory allocated by `alloc` + /// The raw pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// @@ -1225,6 +1227,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// + /// The non-null pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// diff --git a/library/alloc/src/bstr.rs b/library/alloc/src/bstr.rs new file mode 100644 index 00000000000..61e61019b50 --- /dev/null +++ b/library/alloc/src/bstr.rs @@ -0,0 +1,702 @@ +//! The `ByteStr` and `ByteString` types and trait implementations. + +// This could be more fine-grained. +#![cfg(not(no_global_oom_handling))] + +use core::borrow::{Borrow, BorrowMut}; +#[unstable(feature = "bstr", issue = "134915")] +pub use core::bstr::ByteStr; +use core::bstr::{impl_partial_eq, impl_partial_eq_n, impl_partial_eq_ord}; +use core::cmp::Ordering; +use core::ops::{ + Deref, DerefMut, DerefPure, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, + RangeTo, RangeToInclusive, +}; +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +use core::str::FromStr; +use core::{fmt, hash}; + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +use crate::borrow::{Cow, ToOwned}; +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +use crate::boxed::Box; +#[cfg(not(no_rc))] +use crate::rc::Rc; +use crate::string::String; +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +use crate::sync::Arc; +use crate::vec::Vec; + +/// A wrapper for `Vec<u8>` representing a human-readable string that's conventionally, but not +/// always, UTF-8. +/// +/// Unlike `String`, this type permits non-UTF-8 contents, making it suitable for user input, +/// non-native filenames (as `Path` only supports native filenames), and other applications that +/// need to round-trip whatever data the user provides. +/// +/// A `ByteString` owns its contents and can grow and shrink, like a `Vec` or `String`. For a +/// borrowed byte string, see [`ByteStr`](../../std/bstr/struct.ByteStr.html). +/// +/// `ByteString` implements `Deref` to `&Vec<u8>`, so all methods available on `&Vec<u8>` are +/// available on `ByteString`. Similarly, `ByteString` implements `DerefMut` to `&mut Vec<u8>`, +/// so you can modify a `ByteString` using any method available on `&mut Vec<u8>`. +/// +/// The `Debug` and `Display` implementations for `ByteString` are the same as those for `ByteStr`, +/// showing invalid UTF-8 as hex escapes or the Unicode replacement character, respectively. +#[unstable(feature = "bstr", issue = "134915")] +#[repr(transparent)] +#[derive(Clone)] +#[doc(alias = "BString")] +pub struct ByteString(pub Vec<u8>); + +impl ByteString { + #[inline] + pub(crate) fn as_bytes(&self) -> &[u8] { + &self.0 + } + + #[inline] + pub(crate) fn as_bytestr(&self) -> &ByteStr { + ByteStr::new(&self.0) + } + + #[inline] + pub(crate) fn as_mut_bytestr(&mut self) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Deref for ByteString { + type Target = Vec<u8>; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl DerefMut for ByteString { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for ByteString {} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Debug for ByteString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_bytestr(), f) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Display for ByteString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_bytestr(), f) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<[u8]> for ByteString { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<ByteStr> for ByteString { + #[inline] + fn as_ref(&self) -> &ByteStr { + self.as_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut<[u8]> for ByteString { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut<ByteStr> for ByteString { + #[inline] + fn as_mut(&mut self) -> &mut ByteStr { + self.as_mut_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow<[u8]> for ByteString { + #[inline] + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow<ByteStr> for ByteString { + #[inline] + fn borrow(&self) -> &ByteStr { + self.as_bytestr() + } +} + +// `impl Borrow<ByteStr> for Vec<u8>` omitted to avoid inference failures +// `impl Borrow<ByteStr> for String` omitted to avoid inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut<[u8]> for ByteString { + #[inline] + fn borrow_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut<ByteStr> for ByteString { + #[inline] + fn borrow_mut(&mut self) -> &mut ByteStr { + self.as_mut_bytestr() + } +} + +// `impl BorrowMut<ByteStr> for Vec<u8>` omitted to avoid inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl Default for ByteString { + fn default() -> Self { + ByteString(Vec::new()) + } +} + +// Omitted due to inference failures +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a, const N: usize> From<&'a [u8; N]> for ByteString { +// #[inline] +// fn from(s: &'a [u8; N]) -> Self { +// ByteString(s.as_slice().to_vec()) +// } +// } +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<const N: usize> From<[u8; N]> for ByteString { +// #[inline] +// fn from(s: [u8; N]) -> Self { +// ByteString(s.as_slice().to_vec()) +// } +// } +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a [u8]> for ByteString { +// #[inline] +// fn from(s: &'a [u8]) -> Self { +// ByteString(s.to_vec()) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl From<Vec<u8>> for ByteString { +// #[inline] +// fn from(s: Vec<u8>) -> Self { +// ByteString(s) +// } +// } + +#[unstable(feature = "bstr", issue = "134915")] +impl From<ByteString> for Vec<u8> { + #[inline] + fn from(s: ByteString) -> Self { + s.0 + } +} + +// Omitted due to inference failures +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a str> for ByteString { +// #[inline] +// fn from(s: &'a str) -> Self { +// ByteString(s.as_bytes().to_vec()) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl From<String> for ByteString { +// #[inline] +// fn from(s: String) -> Self { +// ByteString(s.into_bytes()) +// } +// } + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<&'a ByteStr> for ByteString { + #[inline] + fn from(s: &'a ByteStr) -> Self { + ByteString(s.0.to_vec()) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<ByteString> for Cow<'a, ByteStr> { + #[inline] + fn from(s: ByteString) -> Self { + Cow::Owned(s) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<&'a ByteString> for Cow<'a, ByteStr> { + #[inline] + fn from(s: &'a ByteString) -> Self { + Cow::Borrowed(s.as_bytestr()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl FromIterator<char> for ByteString { + #[inline] + fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self { + ByteString(iter.into_iter().collect::<String>().into_bytes()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl FromIterator<u8> for ByteString { + #[inline] + fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self { + ByteString(iter.into_iter().collect()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> FromIterator<&'a str> for ByteString { + #[inline] + fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self { + ByteString(iter.into_iter().collect::<String>().into_bytes()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> FromIterator<&'a [u8]> for ByteString { + #[inline] + fn from_iter<T: IntoIterator<Item = &'a [u8]>>(iter: T) -> Self { + let mut buf = Vec::new(); + for b in iter { + buf.extend_from_slice(b); + } + ByteString(buf) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> FromIterator<&'a ByteStr> for ByteString { + #[inline] + fn from_iter<T: IntoIterator<Item = &'a ByteStr>>(iter: T) -> Self { + let mut buf = Vec::new(); + for b in iter { + buf.extend_from_slice(&b.0); + } + ByteString(buf) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl FromIterator<ByteString> for ByteString { + #[inline] + fn from_iter<T: IntoIterator<Item = ByteString>>(iter: T) -> Self { + let mut buf = Vec::new(); + for mut b in iter { + buf.append(&mut b.0); + } + ByteString(buf) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl FromStr for ByteString { + type Err = core::convert::Infallible; + + #[inline] + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(ByteString(s.as_bytes().to_vec())) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<usize> for ByteString { + type Output = u8; + + #[inline] + fn index(&self, idx: usize) -> &u8 { + &self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeFull> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, _: RangeFull) -> &ByteStr { + self.as_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<Range<usize>> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: Range<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeInclusive<usize>> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeInclusive<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeFrom<usize>> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeFrom<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeTo<usize>> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeTo<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeToInclusive<usize>> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeToInclusive<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<usize> for ByteString { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut u8 { + &mut self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeFull> for ByteString { + #[inline] + fn index_mut(&mut self, _: RangeFull) -> &mut ByteStr { + self.as_mut_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<Range<usize>> for ByteString { + #[inline] + fn index_mut(&mut self, r: Range<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeInclusive<usize>> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeInclusive<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeFrom<usize>> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeFrom<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeTo<usize>> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeTo<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeToInclusive<usize>> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeToInclusive<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl hash::Hash for ByteString { + #[inline] + fn hash<H: hash::Hasher>(&self, state: &mut H) { + self.0.hash(state); + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Eq for ByteString {} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialEq for ByteString { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + self.0 == other.0 + } +} + +macro_rules! impl_partial_eq_ord_cow { + ($lhs:ty, $rhs:ty) => { + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = (&**other).as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = (&**self).as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> { + let other: &[u8] = (&**other).as_ref(); + PartialOrd::partial_cmp(self.as_bytes(), other) + } + } + + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> { + let this: &[u8] = (&**self).as_ref(); + PartialOrd::partial_cmp(this, other.as_bytes()) + } + } + }; +} + +// PartialOrd with `Vec<u8>` omitted to avoid inference failures +impl_partial_eq!(ByteString, Vec<u8>); +// PartialOrd with `[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteString, [u8]); +// PartialOrd with `&[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteString, &[u8]); +// PartialOrd with `String` omitted to avoid inference failures +impl_partial_eq!(ByteString, String); +// PartialOrd with `str` omitted to avoid inference failures +impl_partial_eq!(ByteString, str); +// PartialOrd with `&str` omitted to avoid inference failures +impl_partial_eq!(ByteString, &str); +impl_partial_eq_ord!(ByteString, ByteStr); +impl_partial_eq_ord!(ByteString, &ByteStr); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteString, [u8; N]); +// PartialOrd with `&[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteString, &[u8; N]); +impl_partial_eq_ord_cow!(ByteString, Cow<'_, ByteStr>); +impl_partial_eq_ord_cow!(ByteString, Cow<'_, str>); +impl_partial_eq_ord_cow!(ByteString, Cow<'_, [u8]>); + +#[unstable(feature = "bstr", issue = "134915")] +impl Ord for ByteString { + #[inline] + fn cmp(&self, other: &ByteString) -> Ordering { + Ord::cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialOrd for ByteString { + #[inline] + fn partial_cmp(&self, other: &ByteString) -> Option<Ordering> { + PartialOrd::partial_cmp(&self.0, &other.0) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl ToOwned for ByteStr { + type Owned = ByteString; + + #[inline] + fn to_owned(&self) -> ByteString { + ByteString(self.0.to_vec()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl TryFrom<ByteString> for String { + type Error = crate::string::FromUtf8Error; + + #[inline] + fn try_from(s: ByteString) -> Result<Self, Self::Error> { + String::from_utf8(s.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteString> for &'a str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteString) -> Result<Self, Self::Error> { + crate::str::from_utf8(s.0.as_slice()) + } +} + +// Additional impls for `ByteStr` that require types from `alloc`: + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl Clone for Box<ByteStr> { + #[inline] + fn clone(&self) -> Self { + Self::from(Box::<[u8]>::from(&self.0)) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<&'a ByteStr> for Cow<'a, ByteStr> { + #[inline] + fn from(s: &'a ByteStr) -> Self { + Cow::Borrowed(s) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl From<Box<[u8]>> for Box<ByteStr> { + #[inline] + fn from(s: Box<[u8]>) -> Box<ByteStr> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Box::from_raw(Box::into_raw(s) as _) } + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl From<Box<ByteStr>> for Box<[u8]> { + #[inline] + fn from(s: Box<ByteStr>) -> Box<[u8]> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Box::from_raw(Box::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(not(no_rc))] +impl From<Rc<[u8]>> for Rc<ByteStr> { + #[inline] + fn from(s: Rc<[u8]>) -> Rc<ByteStr> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Rc::from_raw(Rc::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(not(no_rc))] +impl From<Rc<ByteStr>> for Rc<[u8]> { + #[inline] + fn from(s: Rc<ByteStr>) -> Rc<[u8]> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Rc::from_raw(Rc::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +impl From<Arc<[u8]>> for Arc<ByteStr> { + #[inline] + fn from(s: Arc<[u8]>) -> Arc<ByteStr> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Arc::from_raw(Arc::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +impl From<Arc<ByteStr>> for Arc<[u8]> { + #[inline] + fn from(s: Arc<ByteStr>) -> Arc<[u8]> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Arc::from_raw(Arc::into_raw(s) as _) } + } +} + +// PartialOrd with `Vec<u8>` omitted to avoid inference failures +impl_partial_eq!(ByteStr, Vec<u8>); +// PartialOrd with `String` omitted to avoid inference failures +impl_partial_eq!(ByteStr, String); +impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, ByteStr>); +impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, str>); +impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, [u8]>); + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteStr> for String { + type Error = core::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteStr) -> Result<Self, Self::Error> { + Ok(core::str::from_utf8(&s.0)?.into()) + } +} diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs index d137d2721ee..091376d5d68 100644 --- a/library/alloc/src/collections/btree/append.rs +++ b/library/alloc/src/collections/btree/append.rs @@ -16,7 +16,7 @@ impl<K, V> Root<K, V> { /// a `BTreeMap`, both iterators should produce keys in strictly ascending /// order, each greater than all keys in the tree, including any keys /// already in the tree upon entry. - pub fn append_from_sorted_iters<I, A: Allocator + Clone>( + pub(super) fn append_from_sorted_iters<I, A: Allocator + Clone>( &mut self, left: I, right: I, @@ -36,8 +36,12 @@ impl<K, V> Root<K, V> { /// Pushes all key-value pairs to the end of the tree, incrementing a /// `length` variable along the way. The latter makes it easier for the /// caller to avoid a leak when the iterator panicks. - pub fn bulk_push<I, A: Allocator + Clone>(&mut self, iter: I, length: &mut usize, alloc: A) - where + pub(super) fn bulk_push<I, A: Allocator + Clone>( + &mut self, + iter: I, + length: &mut usize, + alloc: A, + ) where I: Iterator<Item = (K, V)>, { let mut cur_node = self.borrow_mut().last_leaf_edge().into_node(); diff --git a/library/alloc/src/collections/btree/borrow.rs b/library/alloc/src/collections/btree/borrow.rs index 000b9bd0fab..e848ac3f2d1 100644 --- a/library/alloc/src/collections/btree/borrow.rs +++ b/library/alloc/src/collections/btree/borrow.rs @@ -11,7 +11,7 @@ use core::ptr::NonNull; /// the compiler to follow. A `DormantMutRef` allows you to check borrowing /// yourself, while still expressing its stacked nature, and encapsulating /// the raw pointer code needed to do this without undefined behavior. -pub struct DormantMutRef<'a, T> { +pub(super) struct DormantMutRef<'a, T> { ptr: NonNull<T>, _marker: PhantomData<&'a mut T>, } @@ -23,7 +23,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// Capture a unique borrow, and immediately reborrow it. For the compiler, /// the lifetime of the new reference is the same as the lifetime of the /// original reference, but you promise to use it for a shorter period. - pub fn new(t: &'a mut T) -> (&'a mut T, Self) { + pub(super) fn new(t: &'a mut T) -> (&'a mut T, Self) { let ptr = NonNull::from(t); // SAFETY: we hold the borrow throughout 'a via `_marker`, and we expose // only this reference, so it is unique. @@ -37,7 +37,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken(self) -> &'a mut T { + pub(super) unsafe fn awaken(self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -48,7 +48,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow(&mut self) -> &'a mut T { + pub(super) unsafe fn reborrow(&mut self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -59,7 +59,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow_shared(&self) -> &'a T { + pub(super) unsafe fn reborrow_shared(&self) -> &'a T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &*self.ptr.as_ptr() } } diff --git a/library/alloc/src/collections/btree/dedup_sorted_iter.rs b/library/alloc/src/collections/btree/dedup_sorted_iter.rs index cd6a88f3291..6bcf0bca519 100644 --- a/library/alloc/src/collections/btree/dedup_sorted_iter.rs +++ b/library/alloc/src/collections/btree/dedup_sorted_iter.rs @@ -6,7 +6,7 @@ use core::iter::Peekable; /// Used by [`BTreeMap::bulk_build_from_sorted_iter`][1]. /// /// [1]: crate::collections::BTreeMap::bulk_build_from_sorted_iter -pub struct DedupSortedIter<K, V, I> +pub(super) struct DedupSortedIter<K, V, I> where I: Iterator<Item = (K, V)>, { @@ -17,7 +17,7 @@ impl<K, V, I> DedupSortedIter<K, V, I> where I: Iterator<Item = (K, V)>, { - pub fn new(iter: I) -> Self { + pub(super) fn new(iter: I) -> Self { Self { iter: iter.peekable() } } } diff --git a/library/alloc/src/collections/btree/fix.rs b/library/alloc/src/collections/btree/fix.rs index 09edea3555a..b0c67597946 100644 --- a/library/alloc/src/collections/btree/fix.rs +++ b/library/alloc/src/collections/btree/fix.rs @@ -57,7 +57,10 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { /// /// This method does not expect ancestors to already be underfull upon entry /// and panics if it encounters an empty ancestor. - pub fn fix_node_and_affected_ancestors<A: Allocator + Clone>(mut self, alloc: A) -> bool { + pub(super) fn fix_node_and_affected_ancestors<A: Allocator + Clone>( + mut self, + alloc: A, + ) -> bool { loop { match self.fix_node_through_parent(alloc.clone()) { Ok(Some(parent)) => self = parent.forget_type(), @@ -70,7 +73,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { impl<K, V> Root<K, V> { /// Removes empty levels on the top, but keeps an empty leaf if the entire tree is empty. - pub fn fix_top<A: Allocator + Clone>(&mut self, alloc: A) { + pub(super) fn fix_top<A: Allocator + Clone>(&mut self, alloc: A) { while self.height() > 0 && self.len() == 0 { self.pop_internal_level(alloc.clone()); } @@ -79,7 +82,7 @@ impl<K, V> Root<K, V> { /// Stocks up or merge away any underfull nodes on the right border of the /// tree. The other nodes, those that are not the root nor a rightmost edge, /// must already have at least MIN_LEN elements. - pub fn fix_right_border<A: Allocator + Clone>(&mut self, alloc: A) { + pub(super) fn fix_right_border<A: Allocator + Clone>(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().last_kv().fix_right_border_of_right_edge(alloc.clone()); @@ -88,7 +91,7 @@ impl<K, V> Root<K, V> { } /// The symmetric clone of `fix_right_border`. - pub fn fix_left_border<A: Allocator + Clone>(&mut self, alloc: A) { + pub(super) fn fix_left_border<A: Allocator + Clone>(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().first_kv().fix_left_border_of_left_edge(alloc.clone()); @@ -99,7 +102,7 @@ impl<K, V> Root<K, V> { /// Stocks up any underfull nodes on the right border of the tree. /// The other nodes, those that are neither the root nor a rightmost edge, /// must be prepared to have up to MIN_LEN elements stolen. - pub fn fix_right_border_of_plentiful(&mut self) { + pub(super) fn fix_right_border_of_plentiful(&mut self) { let mut cur_node = self.borrow_mut(); while let Internal(internal) = cur_node.force() { // Check if rightmost child is underfull. diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index d738c5c47b4..4643c4133d5 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -6,7 +6,7 @@ use core::{intrinsics, mem, ptr}; /// If a panic occurs in the `change` closure, the entire process will be aborted. #[allow(dead_code)] // keep as illustration and for future use #[inline] -pub fn take_mut<T>(v: &mut T, change: impl FnOnce(T) -> T) { +pub(super) fn take_mut<T>(v: &mut T, change: impl FnOnce(T) -> T) { replace(v, |value| (change(value), ())) } @@ -15,7 +15,7 @@ pub fn take_mut<T>(v: &mut T, change: impl FnOnce(T) -> T) { /// /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] -pub fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { +pub(super) fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { struct PanicGuard; impl Drop for PanicGuard { fn drop(&mut self) { diff --git a/library/alloc/src/collections/btree/merge_iter.rs b/library/alloc/src/collections/btree/merge_iter.rs index 7f23d93b990..5077062e25d 100644 --- a/library/alloc/src/collections/btree/merge_iter.rs +++ b/library/alloc/src/collections/btree/merge_iter.rs @@ -4,7 +4,7 @@ use core::iter::FusedIterator; /// Core of an iterator that merges the output of two strictly ascending iterators, /// for instance a union or a symmetric difference. -pub struct MergeIterInner<I: Iterator> { +pub(super) struct MergeIterInner<I: Iterator> { a: I, b: I, peeked: Option<Peeked<I>>, @@ -40,7 +40,7 @@ where impl<I: Iterator> MergeIterInner<I> { /// Creates a new core for an iterator merging a pair of sources. - pub fn new(a: I, b: I) -> Self { + pub(super) fn new(a: I, b: I) -> Self { MergeIterInner { a, b, peeked: None } } @@ -51,7 +51,7 @@ impl<I: Iterator> MergeIterInner<I> { /// the sources are not strictly ascending). If neither returned option /// contains a value, iteration has finished and subsequent calls will /// return the same empty pair. - pub fn nexts<Cmp: Fn(&I::Item, &I::Item) -> Ordering>( + pub(super) fn nexts<Cmp: Fn(&I::Item, &I::Item) -> Ordering>( &mut self, cmp: Cmp, ) -> (Option<I::Item>, Option<I::Item>) @@ -74,7 +74,7 @@ impl<I: Iterator> MergeIterInner<I> { b_next = self.b.next(); } } - if let (Some(ref a1), Some(ref b1)) = (&a_next, &b_next) { + if let (Some(a1), Some(b1)) = (&a_next, &b_next) { match cmp(a1, b1) { Ordering::Less => self.peeked = b_next.take().map(Peeked::B), Ordering::Greater => self.peeked = a_next.take().map(Peeked::A), @@ -85,7 +85,7 @@ impl<I: Iterator> MergeIterInner<I> { } /// Returns a pair of upper bounds for the `size_hint` of the final iterator. - pub fn lens(&self) -> (usize, usize) + pub(super) fn lens(&self) -> (usize, usize) where I: ExactSizeIterator, { diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index b8667d09c33..66514806673 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -2,13 +2,13 @@ mod append; mod borrow; mod dedup_sorted_iter; mod fix; -pub mod map; +pub(super) mod map; mod mem; mod merge_iter; mod navigate; mod node; mod remove; mod search; -pub mod set; +pub(super) mod set; mod set_val; mod split; diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index 14b7d4ad71f..b2a7de74875 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -7,7 +7,7 @@ use super::node::{Handle, NodeRef, marker}; use super::search::SearchBound; use crate::alloc::Allocator; // `front` and `back` are always both `None` or both `Some`. -pub struct LeafRange<BorrowType, K, V> { +pub(super) struct LeafRange<BorrowType, K, V> { front: Option<Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge>>, back: Option<Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge>>, } @@ -25,7 +25,7 @@ impl<B, K, V> Default for LeafRange<B, K, V> { } impl<BorrowType, K, V> LeafRange<BorrowType, K, V> { - pub fn none() -> Self { + pub(super) fn none() -> Self { LeafRange { front: None, back: None } } @@ -34,7 +34,7 @@ impl<BorrowType, K, V> LeafRange<BorrowType, K, V> { } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LeafRange<marker::Immut<'_>, K, V> { + pub(super) fn reborrow(&self) -> LeafRange<marker::Immut<'_>, K, V> { LeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -44,24 +44,24 @@ impl<BorrowType, K, V> LeafRange<BorrowType, K, V> { impl<'a, K, V> LeafRange<marker::Immut<'a>, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_checked(|kv| kv.into_kv()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_back_checked(|kv| kv.into_kv()) } } impl<'a, K, V> LeafRange<marker::ValMut<'a>, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_back_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } } @@ -124,7 +124,7 @@ impl<BorrowType, K, V> LazyLeafHandle<BorrowType, K, V> { } // `front` and `back` are always both `None` or both `Some`. -pub struct LazyLeafRange<BorrowType, K, V> { +pub(super) struct LazyLeafRange<BorrowType, K, V> { front: Option<LazyLeafHandle<BorrowType, K, V>>, back: Option<LazyLeafHandle<BorrowType, K, V>>, } @@ -142,12 +142,12 @@ impl<'a, K: 'a, V: 'a> Clone for LazyLeafRange<marker::Immut<'a>, K, V> { } impl<BorrowType, K, V> LazyLeafRange<BorrowType, K, V> { - pub fn none() -> Self { + pub(super) fn none() -> Self { LazyLeafRange { front: None, back: None } } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LazyLeafRange<marker::Immut<'_>, K, V> { + pub(super) fn reborrow(&self) -> LazyLeafRange<marker::Immut<'_>, K, V> { LazyLeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -157,24 +157,24 @@ impl<BorrowType, K, V> LazyLeafRange<BorrowType, K, V> { impl<'a, K, V> LazyLeafRange<marker::Immut<'a>, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } impl<'a, K, V> LazyLeafRange<marker::ValMut<'a>, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } @@ -190,7 +190,7 @@ impl<K, V> LazyLeafRange<marker::Dying, K, V> { } #[inline] - pub unsafe fn deallocating_next_unchecked<A: Allocator + Clone>( + pub(super) unsafe fn deallocating_next_unchecked<A: Allocator + Clone>( &mut self, alloc: A, ) -> Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV> { @@ -200,7 +200,7 @@ impl<K, V> LazyLeafRange<marker::Dying, K, V> { } #[inline] - pub unsafe fn deallocating_next_back_unchecked<A: Allocator + Clone>( + pub(super) unsafe fn deallocating_next_back_unchecked<A: Allocator + Clone>( &mut self, alloc: A, ) -> Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV> { @@ -210,7 +210,7 @@ impl<K, V> LazyLeafRange<marker::Dying, K, V> { } #[inline] - pub fn deallocating_end<A: Allocator + Clone>(&mut self, alloc: A) { + pub(super) fn deallocating_end<A: Allocator + Clone>(&mut self, alloc: A) { if let Some(front) = self.take_front() { front.deallocating_end(alloc) } @@ -313,7 +313,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn range_search<Q, R>(self, range: R) -> LeafRange<marker::Immut<'a>, K, V> + pub(super) fn range_search<Q, R>(self, range: R) -> LeafRange<marker::Immut<'a>, K, V> where Q: ?Sized + Ord, K: Borrow<Q>, @@ -324,7 +324,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> } /// Finds the pair of leaf edges delimiting an entire tree. - pub fn full_range(self) -> LazyLeafRange<marker::Immut<'a>, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange<marker::Immut<'a>, K, V> { full_range(self, self) } } @@ -339,7 +339,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::ValMut<'a>, K, V, marker::LeafOrInternal> /// /// # Safety /// Do not use the duplicate handles to visit the same KV twice. - pub fn range_search<Q, R>(self, range: R) -> LeafRange<marker::ValMut<'a>, K, V> + pub(super) fn range_search<Q, R>(self, range: R) -> LeafRange<marker::ValMut<'a>, K, V> where Q: ?Sized + Ord, K: Borrow<Q>, @@ -351,7 +351,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::ValMut<'a>, K, V, marker::LeafOrInternal> /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing mutation (of values only), so must be used /// with care. - pub fn full_range(self) -> LazyLeafRange<marker::ValMut<'a>, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange<marker::ValMut<'a>, K, V> { // We duplicate the root NodeRef here -- we will never visit the same KV // twice, and never end up with overlapping value references. let self2 = unsafe { ptr::read(&self) }; @@ -363,7 +363,7 @@ impl<K, V> NodeRef<marker::Dying, K, V, marker::LeafOrInternal> { /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing massively destructive mutation, so must be /// used with the utmost care. - pub fn full_range(self) -> LazyLeafRange<marker::Dying, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange<marker::Dying, K, V> { // We duplicate the root NodeRef here -- we will never access it in a way // that overlaps references obtained from the root. let self2 = unsafe { ptr::read(&self) }; @@ -377,7 +377,7 @@ impl<BorrowType: marker::BorrowType, K, V> /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the right side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the last one in the tree, returns [`Result::Err`] with the root node. - pub fn next_kv( + pub(super) fn next_kv( self, ) -> Result< Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, marker::KV>, @@ -398,7 +398,7 @@ impl<BorrowType: marker::BorrowType, K, V> /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the left side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the first one in the tree, returns [`Result::Err`] with the root node. - pub fn next_back_kv( + pub(super) fn next_back_kv( self, ) -> Result< Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, marker::KV>, @@ -627,7 +627,9 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea /// Returns the leftmost leaf edge in or underneath a node - in other words, the edge /// you need first when navigating forward (or last when navigating backward). #[inline] - pub fn first_leaf_edge(self) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> { + pub(super) fn first_leaf_edge( + self, + ) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> { let mut node = self; loop { match node.force() { @@ -640,7 +642,9 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea /// Returns the rightmost leaf edge in or underneath a node - in other words, the edge /// you need last when navigating forward (or first when navigating backward). #[inline] - pub fn last_leaf_edge(self) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> { + pub(super) fn last_leaf_edge( + self, + ) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> { let mut node = self; loop { match node.force() { @@ -651,7 +655,7 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea } } -pub enum Position<BorrowType, K, V> { +pub(super) enum Position<BorrowType, K, V> { Leaf(NodeRef<BorrowType, K, V, marker::Leaf>), Internal(NodeRef<BorrowType, K, V, marker::Internal>), InternalKV, @@ -661,7 +665,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> /// Visits leaf nodes and internal KVs in order of ascending keys, and also /// visits internal nodes as a whole in a depth first order, meaning that /// internal nodes precede their individual KVs and their child nodes. - pub fn visit_nodes_in_order<F>(self, mut visit: F) + pub(super) fn visit_nodes_in_order<F>(self, mut visit: F) where F: FnMut(Position<marker::Immut<'a>, K, V>), { @@ -693,7 +697,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> } /// Calculates the number of elements in a (sub)tree. - pub fn calc_length(self) -> usize { + pub(super) fn calc_length(self) -> usize { let mut result = 0; self.visit_nodes_in_order(|pos| match pos { Position::Leaf(node) => result += node.len(), @@ -708,7 +712,9 @@ impl<BorrowType: marker::BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, marker::KV> { /// Returns the leaf edge closest to a KV for forward navigation. - pub fn next_leaf_edge(self) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> { + pub(super) fn next_leaf_edge( + self, + ) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> { match self.force() { Leaf(leaf_kv) => leaf_kv.right_edge(), Internal(internal_kv) => { @@ -719,7 +725,7 @@ impl<BorrowType: marker::BorrowType, K, V> } /// Returns the leaf edge closest to a KV for backward navigation. - pub fn next_back_leaf_edge( + pub(super) fn next_back_leaf_edge( self, ) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> { match self.force() { @@ -735,7 +741,7 @@ impl<BorrowType: marker::BorrowType, K, V> impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> { /// Returns the leaf edge corresponding to the first point at which the /// given bound is true. - pub fn lower_bound<Q: ?Sized>( + pub(super) fn lower_bound<Q: ?Sized>( self, mut bound: SearchBound<&Q>, ) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> @@ -758,7 +764,7 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea /// Returns the leaf edge corresponding to the last point at which the /// given bound is true. - pub fn upper_bound<Q: ?Sized>( + pub(super) fn upper_bound<Q: ?Sized>( self, mut bound: SearchBound<&Q>, ) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 4057657632b..37f784a322c 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -40,8 +40,8 @@ use crate::alloc::{Allocator, Layout}; use crate::boxed::Box; const B: usize = 6; -pub const CAPACITY: usize = 2 * B - 1; -pub const MIN_LEN_AFTER_SPLIT: usize = B - 1; +pub(super) const CAPACITY: usize = 2 * B - 1; +pub(super) const MIN_LEN_AFTER_SPLIT: usize = B - 1; const KV_IDX_CENTER: usize = B - 1; const EDGE_IDX_LEFT_OF_CENTER: usize = B - 1; const EDGE_IDX_RIGHT_OF_CENTER: usize = B; @@ -179,7 +179,7 @@ type BoxedNode<K, V> = NonNull<LeafNode<K, V>>; /// as the returned reference is used. /// The methods supporting insert bend this rule by returning a raw pointer, /// i.e., a reference without any lifetime. -pub struct NodeRef<BorrowType, K, V, Type> { +pub(super) struct NodeRef<BorrowType, K, V, Type> { /// The number of levels that the node and the level of leaves are apart, a /// constant of the node that cannot be entirely described by `Type`, and that /// the node itself does not store. We only need to store the height of the root @@ -195,7 +195,7 @@ pub struct NodeRef<BorrowType, K, V, Type> { /// The root node of an owned tree. /// /// Note that this does not have a destructor, and must be cleaned up manually. -pub type Root<K, V> = NodeRef<marker::Owned, K, V, marker::LeafOrInternal>; +pub(super) type Root<K, V> = NodeRef<marker::Owned, K, V, marker::LeafOrInternal>; impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef<marker::Immut<'a>, K, V, Type> {} impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef<marker::Immut<'a>, K, V, Type> { @@ -213,7 +213,7 @@ unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Owned, K, V, Type> unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Dying, K, V, Type> {} impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> { - pub fn new_leaf<A: Allocator + Clone>(alloc: A) -> Self { + pub(super) fn new_leaf<A: Allocator + Clone>(alloc: A) -> Self { Self::from_new_leaf(LeafNode::new(alloc)) } @@ -274,7 +274,7 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> { /// The number of edges is `len() + 1`. /// Note that, despite being safe, calling this function can have the side effect /// of invalidating mutable references that unsafe code has created. - pub fn len(&self) -> usize { + pub(super) fn len(&self) -> usize { // Crucially, we only access the `len` field here. If BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. unsafe { usize::from((*Self::as_leaf_ptr(self)).len) } @@ -285,12 +285,12 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> { /// root on top, the number says at which elevation the node appears. /// If you picture trees with leaves on top, the number says how high /// the tree extends above the node. - pub fn height(&self) -> usize { + pub(super) fn height(&self) -> usize { self.height } /// Temporarily takes out another, immutable reference to the same node. - pub fn reborrow(&self) -> NodeRef<marker::Immut<'_>, K, V, Type> { + pub(super) fn reborrow(&self) -> NodeRef<marker::Immut<'_>, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -315,7 +315,7 @@ impl<BorrowType: marker::BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn ascend( + pub(super) fn ascend( self, ) -> Result<Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::Edge>, Self> { const { @@ -335,24 +335,24 @@ impl<BorrowType: marker::BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> .ok_or(self) } - pub fn first_edge(self) -> Handle<Self, marker::Edge> { + pub(super) fn first_edge(self) -> Handle<Self, marker::Edge> { unsafe { Handle::new_edge(self, 0) } } - pub fn last_edge(self) -> Handle<Self, marker::Edge> { + pub(super) fn last_edge(self) -> Handle<Self, marker::Edge> { let len = self.len(); unsafe { Handle::new_edge(self, len) } } /// Note that `self` must be nonempty. - pub fn first_kv(self) -> Handle<Self, marker::KV> { + pub(super) fn first_kv(self) -> Handle<Self, marker::KV> { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, 0) } } /// Note that `self` must be nonempty. - pub fn last_kv(self) -> Handle<Self, marker::KV> { + pub(super) fn last_kv(self) -> Handle<Self, marker::KV> { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, len - 1) } @@ -381,7 +381,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> { } /// Borrows a view into the keys stored in the node. - pub fn keys(&self) -> &[K] { + pub(super) fn keys(&self) -> &[K] { let leaf = self.into_leaf(); unsafe { leaf.keys.get_unchecked(..usize::from(leaf.len)).assume_init_ref() } } @@ -391,7 +391,7 @@ impl<K, V> NodeRef<marker::Dying, K, V, marker::LeafOrInternal> { /// Similar to `ascend`, gets a reference to a node's parent node, but also /// deallocates the current node in the process. This is unsafe because the /// current node will still be accessible despite being deallocated. - pub unsafe fn deallocate_and_ascend<A: Allocator + Clone>( + pub(super) unsafe fn deallocate_and_ascend<A: Allocator + Clone>( self, alloc: A, ) -> Option<Handle<NodeRef<marker::Dying, K, V, marker::Internal>, marker::Edge>> { @@ -443,7 +443,7 @@ impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> { /// Returns a dormant copy of this node with its lifetime erased which can /// be reawakened later. - pub fn dormant(&self) -> NodeRef<marker::DormantMut, K, V, Type> { + pub(super) fn dormant(&self) -> NodeRef<marker::DormantMut, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -455,7 +455,7 @@ impl<K, V, Type> NodeRef<marker::DormantMut, K, V, Type> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken<'a>(self) -> NodeRef<marker::Mut<'a>, K, V, Type> { + pub(super) unsafe fn awaken<'a>(self) -> NodeRef<marker::Mut<'a>, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -536,7 +536,7 @@ impl<'a, K, V, Type> NodeRef<marker::ValMut<'a>, K, V, Type> { impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> { /// Borrows exclusive access to the length of the node. - pub fn len_mut(&mut self) -> &mut u16 { + pub(super) fn len_mut(&mut self) -> &mut u16 { &mut self.as_leaf_mut().len } } @@ -578,14 +578,14 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> { impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> { /// Returns a new owned tree, with its own root node that is initially empty. - pub fn new<A: Allocator + Clone>(alloc: A) -> Self { + pub(super) fn new<A: Allocator + Clone>(alloc: A) -> Self { NodeRef::new_leaf(alloc).forget_type() } /// Adds a new internal node with a single edge pointing to the previous root node, /// make that new node the root node, and return it. This increases the height by 1 /// and is the opposite of `pop_internal_level`. - pub fn push_internal_level<A: Allocator + Clone>( + pub(super) fn push_internal_level<A: Allocator + Clone>( &mut self, alloc: A, ) -> NodeRef<marker::Mut<'_>, K, V, marker::Internal> { @@ -600,11 +600,11 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> { /// no cleanup is done on any of the keys, values and other children. /// This decreases the height by 1 and is the opposite of `push_internal_level`. /// - /// Requires exclusive access to the `NodeRef` object but not to the root node; - /// it will not invalidate other handles or references to the root node. + /// Does not invalidate any handles or references pointing into the subtree + /// rooted at the first child of `self`. /// /// Panics if there is no internal level, i.e., if the root node is a leaf. - pub fn pop_internal_level<A: Allocator + Clone>(&mut self, alloc: A) { + pub(super) fn pop_internal_level<A: Allocator + Clone>(&mut self, alloc: A) { assert!(self.height > 0); let top = self.node; @@ -628,18 +628,18 @@ impl<K, V, Type> NodeRef<marker::Owned, K, V, Type> { /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe /// because the return value cannot be used to destroy the root, and there /// cannot be other references to the tree. - pub fn borrow_mut(&mut self) -> NodeRef<marker::Mut<'_>, K, V, Type> { + pub(super) fn borrow_mut(&mut self) -> NodeRef<marker::Mut<'_>, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Slightly mutably borrows the owned root node. - pub fn borrow_valmut(&mut self) -> NodeRef<marker::ValMut<'_>, K, V, Type> { + pub(super) fn borrow_valmut(&mut self) -> NodeRef<marker::ValMut<'_>, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Irreversibly transitions to a reference that permits traversal and offers /// destructive methods and little else. - pub fn into_dying(self) -> NodeRef<marker::Dying, K, V, Type> { + pub(super) fn into_dying(self) -> NodeRef<marker::Dying, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -651,7 +651,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> { /// # Safety /// /// The returned handle has an unbound lifetime. - pub unsafe fn push_with_handle<'b>( + pub(super) unsafe fn push_with_handle<'b>( &mut self, key: K, val: V, @@ -672,7 +672,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> { /// Adds a key-value pair to the end of the node, and returns /// the mutable reference of the inserted value. - pub fn push(&mut self, key: K, val: V) -> *mut V { + pub(super) fn push(&mut self, key: K, val: V) -> *mut V { // SAFETY: The unbound handle is no longer accessible. unsafe { self.push_with_handle(key, val).into_val_mut() } } @@ -681,7 +681,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> { impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> { /// Adds a key-value pair, and an edge to go to the right of that pair, /// to the end of the node. - pub fn push(&mut self, key: K, val: V, edge: Root<K, V>) { + pub(super) fn push(&mut self, key: K, val: V, edge: Root<K, V>) { assert!(edge.height == self.height - 1); let len = self.len_mut(); @@ -699,21 +699,21 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> { impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Leaf> { /// Removes any static information asserting that this node is a `Leaf` node. - pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> { + pub(super) fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> { /// Removes any static information asserting that this node is an `Internal` node. - pub fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> { + pub(super) fn forget_type(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> { /// Checks whether a node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< NodeRef<BorrowType, K, V, marker::Leaf>, @@ -737,7 +737,9 @@ impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> { impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { /// Unsafely asserts to the compiler the static information that this node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked(self) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> { + pub(super) unsafe fn cast_to_leaf_unchecked( + self, + ) -> NodeRef<marker::Mut<'a>, K, V, marker::Leaf> { debug_assert!(self.height == 0); NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -757,7 +759,7 @@ impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { /// a child node, these represent the spaces where child pointers would go between the key-value /// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one /// to the left of the node, one between the two pairs, and one at the right of the node. -pub struct Handle<Node, Type> { +pub(super) struct Handle<Node, Type> { node: Node, idx: usize, _marker: PhantomData<Type>, @@ -774,12 +776,12 @@ impl<Node: Copy, Type> Clone for Handle<Node, Type> { impl<Node, Type> Handle<Node, Type> { /// Retrieves the node that contains the edge or key-value pair this handle points to. - pub fn into_node(self) -> Node { + pub(super) fn into_node(self) -> Node { self.node } /// Returns the position of this handle in the node. - pub fn idx(&self) -> usize { + pub(super) fn idx(&self) -> usize { self.idx } } @@ -787,17 +789,17 @@ impl<Node, Type> Handle<Node, Type> { impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::KV> { /// Creates a new handle to a key-value pair in `node`. /// Unsafe because the caller must ensure that `idx < node.len()`. - pub unsafe fn new_kv(node: NodeRef<BorrowType, K, V, NodeType>, idx: usize) -> Self { + pub(super) unsafe fn new_kv(node: NodeRef<BorrowType, K, V, NodeType>, idx: usize) -> Self { debug_assert!(idx < node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_edge(self) -> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::Edge> { + pub(super) fn left_edge(self) -> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx) } } - pub fn right_edge(self) -> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::Edge> { + pub(super) fn right_edge(self) -> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx + 1) } } } @@ -815,7 +817,9 @@ impl<BorrowType, K, V, NodeType, HandleType> Handle<NodeRef<BorrowType, K, V, NodeType>, HandleType> { /// Temporarily takes out another immutable handle on the same location. - pub fn reborrow(&self) -> Handle<NodeRef<marker::Immut<'_>, K, V, NodeType>, HandleType> { + pub(super) fn reborrow( + &self, + ) -> Handle<NodeRef<marker::Immut<'_>, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type Handle { node: self.node.reborrow(), idx: self.idx, _marker: PhantomData } } @@ -827,7 +831,7 @@ impl<'a, K, V, NodeType, HandleType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeT /// dangerous. /// /// For details, see `NodeRef::reborrow_mut`. - pub unsafe fn reborrow_mut( + pub(super) unsafe fn reborrow_mut( &mut self, ) -> Handle<NodeRef<marker::Mut<'_>, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type @@ -837,7 +841,9 @@ impl<'a, K, V, NodeType, HandleType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeT /// Returns a dormant copy of this handle which can be reawakened later. /// /// See `DormantMutRef` for more details. - pub fn dormant(&self) -> Handle<NodeRef<marker::DormantMut, K, V, NodeType>, HandleType> { + pub(super) fn dormant( + &self, + ) -> Handle<NodeRef<marker::DormantMut, K, V, NodeType>, HandleType> { Handle { node: self.node.dormant(), idx: self.idx, _marker: PhantomData } } } @@ -849,7 +855,9 @@ impl<K, V, NodeType, HandleType> Handle<NodeRef<marker::DormantMut, K, V, NodeTy /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken<'a>(self) -> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, HandleType> { + pub(super) unsafe fn awaken<'a>( + self, + ) -> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, HandleType> { Handle { node: unsafe { self.node.awaken() }, idx: self.idx, _marker: PhantomData } } } @@ -857,13 +865,15 @@ impl<K, V, NodeType, HandleType> Handle<NodeRef<marker::DormantMut, K, V, NodeTy impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, marker::Edge> { /// Creates a new handle to an edge in `node`. /// Unsafe because the caller must ensure that `idx <= node.len()`. - pub unsafe fn new_edge(node: NodeRef<BorrowType, K, V, NodeType>, idx: usize) -> Self { + pub(super) unsafe fn new_edge(node: NodeRef<BorrowType, K, V, NodeType>, idx: usize) -> Self { debug_assert!(idx <= node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_kv(self) -> Result<Handle<NodeRef<BorrowType, K, V, NodeType>, marker::KV>, Self> { + pub(super) fn left_kv( + self, + ) -> Result<Handle<NodeRef<BorrowType, K, V, NodeType>, marker::KV>, Self> { if self.idx > 0 { Ok(unsafe { Handle::new_kv(self.node, self.idx - 1) }) } else { @@ -871,7 +881,9 @@ impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, mar } } - pub fn right_kv(self) -> Result<Handle<NodeRef<BorrowType, K, V, NodeType>, marker::KV>, Self> { + pub(super) fn right_kv( + self, + ) -> Result<Handle<NodeRef<BorrowType, K, V, NodeType>, marker::KV>, Self> { if self.idx < self.node.len() { Ok(unsafe { Handle::new_kv(self.node, self.idx) }) } else { @@ -880,7 +892,7 @@ impl<BorrowType, K, V, NodeType> Handle<NodeRef<BorrowType, K, V, NodeType>, mar } } -pub enum LeftOrRight<T> { +pub(super) enum LeftOrRight<T> { Left(T), Right(T), } @@ -1034,7 +1046,7 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, mark /// If the returned result is some `SplitResult`, the `left` field will be the root node. /// The returned pointer points to the inserted value, which in the case of `SplitResult` /// is in the `left` or `right` tree. - pub fn insert_recursing<A: Allocator + Clone>( + pub(super) fn insert_recursing<A: Allocator + Clone>( self, key: K, value: V, @@ -1078,7 +1090,7 @@ impl<BorrowType: marker::BorrowType, K, V> /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn descend(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> { + pub(super) fn descend(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> { const { assert!(BorrowType::TRAVERSAL_PERMIT); } @@ -1097,7 +1109,7 @@ impl<BorrowType: marker::BorrowType, K, V> } impl<'a, K: 'a, V: 'a, NodeType> Handle<NodeRef<marker::Immut<'a>, K, V, NodeType>, marker::KV> { - pub fn into_kv(self) -> (&'a K, &'a V) { + pub(super) fn into_kv(self) -> (&'a K, &'a V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf(); let k = unsafe { leaf.keys.get_unchecked(self.idx).assume_init_ref() }; @@ -1107,17 +1119,17 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle<NodeRef<marker::Immut<'a>, K, V, NodeTyp } impl<'a, K: 'a, V: 'a, NodeType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, marker::KV> { - pub fn key_mut(&mut self) -> &mut K { + pub(super) fn key_mut(&mut self) -> &mut K { unsafe { self.node.key_area_mut(self.idx).assume_init_mut() } } - pub fn into_val_mut(self) -> &'a mut V { + pub(super) fn into_val_mut(self) -> &'a mut V { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() } } - pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { + pub(super) fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); let k = unsafe { leaf.keys.get_unchecked_mut(self.idx).assume_init_mut() }; @@ -1127,13 +1139,13 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType> } impl<'a, K, V, NodeType> Handle<NodeRef<marker::ValMut<'a>, K, V, NodeType>, marker::KV> { - pub fn into_kv_valmut(self) -> (&'a K, &'a mut V) { + pub(super) fn into_kv_valmut(self) -> (&'a K, &'a mut V) { unsafe { self.node.into_key_val_mut_at(self.idx) } } } impl<'a, K: 'a, V: 'a, NodeType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType>, marker::KV> { - pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + pub(super) fn kv_mut(&mut self) -> (&mut K, &mut V) { debug_assert!(self.idx < self.node.len()); // We cannot call separate key and value methods, because calling the second one // invalidates the reference returned by the first. @@ -1146,7 +1158,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType> } /// Replaces the key and value that the KV handle refers to. - pub fn replace_kv(&mut self, k: K, v: V) -> (K, V) { + pub(super) fn replace_kv(&mut self, k: K, v: V) -> (K, V) { let (key, val) = self.kv_mut(); (mem::replace(key, k), mem::replace(val, v)) } @@ -1156,7 +1168,7 @@ impl<K, V, NodeType> Handle<NodeRef<marker::Dying, K, V, NodeType>, marker::KV> /// Extracts the key and value that the KV handle refers to. /// # Safety /// The node that the handle refers to must not yet have been deallocated. - pub unsafe fn into_key_val(mut self) -> (K, V) { + pub(super) unsafe fn into_key_val(mut self) -> (K, V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); unsafe { @@ -1170,7 +1182,7 @@ impl<K, V, NodeType> Handle<NodeRef<marker::Dying, K, V, NodeType>, marker::KV> /// # Safety /// The node that the handle refers to must not yet have been deallocated. #[inline] - pub unsafe fn drop_key_val(mut self) { + pub(super) unsafe fn drop_key_val(mut self) { // Run the destructor of the value even if the destructor of the key panics. struct Dropper<'a, T>(&'a mut MaybeUninit<T>); impl<T> Drop for Dropper<'_, T> { @@ -1229,7 +1241,10 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, mark /// - The key and value pointed to by this handle are extracted. /// - All the key-value pairs to the right of this handle are put into a newly /// allocated node. - pub fn split<A: Allocator + Clone>(mut self, alloc: A) -> SplitResult<'a, K, V, marker::Leaf> { + pub(super) fn split<A: Allocator + Clone>( + mut self, + alloc: A, + ) -> SplitResult<'a, K, V, marker::Leaf> { let mut new_node = LeafNode::new(alloc); let kv = self.split_leaf_data(&mut new_node); @@ -1240,7 +1255,7 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, mark /// Removes the key-value pair pointed to by this handle and returns it, along with the edge /// that the key-value pair collapsed into. - pub fn remove( + pub(super) fn remove( mut self, ) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>) { let old_len = self.node.len(); @@ -1261,7 +1276,7 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, /// - The key and value pointed to by this handle are extracted. /// - All the edges and key-value pairs to the right of this handle are put into /// a newly allocated node. - pub fn split<A: Allocator + Clone>( + pub(super) fn split<A: Allocator + Clone>( mut self, alloc: A, ) -> SplitResult<'a, K, V, marker::Internal> { @@ -1285,14 +1300,14 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, /// Represents a session for evaluating and performing a balancing operation /// around an internal key-value pair. -pub struct BalancingContext<'a, K, V> { +pub(super) struct BalancingContext<'a, K, V> { parent: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::KV>, left_child: NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, right_child: NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, } impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::KV> { - pub fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { + pub(super) fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { let self1 = unsafe { ptr::read(&self) }; let self2 = unsafe { ptr::read(&self) }; BalancingContext { @@ -1318,7 +1333,7 @@ impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { /// typically faster, since we only need to shift the node's N elements to /// the right, instead of shifting at least N of the sibling's elements to /// the left. - pub fn choose_parent_kv(self) -> Result<LeftOrRight<BalancingContext<'a, K, V>>, Self> { + pub(super) fn choose_parent_kv(self) -> Result<LeftOrRight<BalancingContext<'a, K, V>>, Self> { match unsafe { ptr::read(&self) }.ascend() { Ok(parent_edge) => match parent_edge.left_kv() { Ok(left_parent_kv) => Ok(LeftOrRight::Left(BalancingContext { @@ -1341,25 +1356,25 @@ impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { } impl<'a, K, V> BalancingContext<'a, K, V> { - pub fn left_child_len(&self) -> usize { + pub(super) fn left_child_len(&self) -> usize { self.left_child.len() } - pub fn right_child_len(&self) -> usize { + pub(super) fn right_child_len(&self) -> usize { self.right_child.len() } - pub fn into_left_child(self) -> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { + pub(super) fn into_left_child(self) -> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { self.left_child } - pub fn into_right_child(self) -> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { + pub(super) fn into_right_child(self) -> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { self.right_child } /// Returns whether merging is possible, i.e., whether there is enough room /// in a node to combine the central KV with both adjacent child nodes. - pub fn can_merge(&self) -> bool { + pub(super) fn can_merge(&self) -> bool { self.left_child.len() + 1 + self.right_child.len() <= CAPACITY } } @@ -1433,7 +1448,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns the shrunk parent node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_parent<A: Allocator + Clone>( + pub(super) fn merge_tracking_parent<A: Allocator + Clone>( self, alloc: A, ) -> NodeRef<marker::Mut<'a>, K, V, marker::Internal> { @@ -1444,7 +1459,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns that child node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child<A: Allocator + Clone>( + pub(super) fn merge_tracking_child<A: Allocator + Clone>( self, alloc: A, ) -> NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal> { @@ -1456,7 +1471,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// where the tracked child edge ended up, /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child_edge<A: Allocator + Clone>( + pub(super) fn merge_tracking_child_edge<A: Allocator + Clone>( self, track_edge_idx: LeftOrRight<usize>, alloc: A, @@ -1479,7 +1494,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair into the right child. /// Returns a handle to the edge in the right child corresponding to where the original /// edge specified by `track_right_edge_idx` ended up. - pub fn steal_left( + pub(super) fn steal_left( mut self, track_right_edge_idx: usize, ) -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1491,7 +1506,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair onto the left child. /// Returns a handle to the edge in the left child specified by `track_left_edge_idx`, /// which didn't move. - pub fn steal_right( + pub(super) fn steal_right( mut self, track_left_edge_idx: usize, ) -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1500,7 +1515,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// This does stealing similar to `steal_left` but steals multiple elements at once. - pub fn bulk_steal_left(&mut self, count: usize) { + pub(super) fn bulk_steal_left(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1563,7 +1578,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// The symmetric clone of `bulk_steal_left`. - pub fn bulk_steal_right(&mut self, count: usize) { + pub(super) fn bulk_steal_right(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1628,7 +1643,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1636,7 +1651,7 @@ impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::E } impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1644,7 +1659,7 @@ impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Internal>, marke } impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::KV> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, marker::KV> { unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } @@ -1653,7 +1668,7 @@ impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::K impl<BorrowType, K, V, Type> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, Type> { /// Checks whether the underlying node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< Handle<NodeRef<BorrowType, K, V, marker::Leaf>, Type>, @@ -1672,7 +1687,7 @@ impl<BorrowType, K, V, Type> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInte impl<'a, K, V, Type> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, Type> { /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked( + pub(super) unsafe fn cast_to_leaf_unchecked( self, ) -> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, Type> { let node = unsafe { self.node.cast_to_leaf_unchecked() }; @@ -1683,7 +1698,7 @@ impl<'a, K, V, Type> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInterna impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::Edge> { /// Move the suffix after `self` from one node to another one. `right` must be empty. /// The first edge of `right` remains unchanged. - pub fn move_suffix( + pub(super) fn move_suffix( &mut self, right: &mut NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, ) { @@ -1726,13 +1741,13 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, ma } } -pub enum ForceResult<Leaf, Internal> { +pub(super) enum ForceResult<Leaf, Internal> { Leaf(Leaf), Internal(Internal), } /// Result of insertion, when a node needed to expand beyond its capacity. -pub struct SplitResult<'a, K, V, NodeType> { +pub(super) struct SplitResult<'a, K, V, NodeType> { // Altered node in existing tree with elements and edges that belong to the left of `kv`. pub left: NodeRef<marker::Mut<'a>, K, V, NodeType>, // Some key and value that existed before and were split off, to be inserted elsewhere. @@ -1742,32 +1757,32 @@ pub struct SplitResult<'a, K, V, NodeType> { } impl<'a, K, V> SplitResult<'a, K, V, marker::Leaf> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } impl<'a, K, V> SplitResult<'a, K, V, marker::Internal> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } -pub mod marker { +pub(super) mod marker { use core::marker::PhantomData; - pub enum Leaf {} - pub enum Internal {} - pub enum LeafOrInternal {} + pub(crate) enum Leaf {} + pub(crate) enum Internal {} + pub(crate) enum LeafOrInternal {} - pub enum Owned {} - pub enum Dying {} - pub enum DormantMut {} - pub struct Immut<'a>(PhantomData<&'a ()>); - pub struct Mut<'a>(PhantomData<&'a mut ()>); - pub struct ValMut<'a>(PhantomData<&'a mut ()>); + pub(crate) enum Owned {} + pub(crate) enum Dying {} + pub(crate) enum DormantMut {} + pub(crate) struct Immut<'a>(PhantomData<&'a ()>); + pub(crate) struct Mut<'a>(PhantomData<&'a mut ()>); + pub(crate) struct ValMut<'a>(PhantomData<&'a mut ()>); - pub trait BorrowType { + pub(crate) trait BorrowType { /// If node references of this borrow type allow traversing to other /// nodes in the tree, this constant is set to `true`. It can be used /// for a compile-time assertion. @@ -1786,8 +1801,8 @@ pub mod marker { impl<'a> BorrowType for ValMut<'a> {} impl BorrowType for DormantMut {} - pub enum KV {} - pub enum Edge {} + pub(crate) enum KV {} + pub(crate) enum Edge {} } /// Inserts a value into a slice of initialized elements followed by one uninitialized element. diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 4d2fa0f0941..ecd009f11c7 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -6,7 +6,7 @@ use crate::string::String; impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> { // Asserts that the back pointer in each reachable node points to its parent. - pub fn assert_back_pointers(self) { + pub(crate) fn assert_back_pointers(self) { if let ForceResult::Internal(node) = self.force() { for idx in 0..=node.len() { let edge = unsafe { Handle::new_edge(node, idx) }; @@ -20,7 +20,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> // Renders a multi-line display of the keys in order and in tree hierarchy, // picturing the tree growing sideways from its root on the left to its // leaves on the right. - pub fn dump_keys(self) -> String + pub(crate) fn dump_keys(self) -> String where K: Debug, { diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs index 56f2824b782..9d870b86f34 100644 --- a/library/alloc/src/collections/btree/remove.rs +++ b/library/alloc/src/collections/btree/remove.rs @@ -10,7 +10,7 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInter /// the leaf edge corresponding to that former pair. It's possible this empties /// a root node that is internal, which the caller should pop from the map /// holding the tree. The caller should also decrement the map's length. - pub fn remove_kv_tracking<F: FnOnce(), A: Allocator + Clone>( + pub(super) fn remove_kv_tracking<F: FnOnce(), A: Allocator + Clone>( self, handle_emptied_internal_root: F, alloc: A, diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index 22e015edac3..96e5bf10802 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -8,7 +8,7 @@ use SearchResult::*; use super::node::ForceResult::*; use super::node::{Handle, NodeRef, marker}; -pub enum SearchBound<T> { +pub(super) enum SearchBound<T> { /// An inclusive bound to look for, just like `Bound::Included(T)`. Included(T), /// An exclusive bound to look for, just like `Bound::Excluded(T)`. @@ -20,7 +20,7 @@ pub enum SearchBound<T> { } impl<T> SearchBound<T> { - pub fn from_range(range_bound: Bound<T>) -> Self { + pub(super) fn from_range(range_bound: Bound<T>) -> Self { match range_bound { Bound::Included(t) => Included(t), Bound::Excluded(t) => Excluded(t), @@ -29,12 +29,12 @@ impl<T> SearchBound<T> { } } -pub enum SearchResult<BorrowType, K, V, FoundType, GoDownType> { +pub(super) enum SearchResult<BorrowType, K, V, FoundType, GoDownType> { Found(Handle<NodeRef<BorrowType, K, V, FoundType>, marker::KV>), GoDown(Handle<NodeRef<BorrowType, K, V, GoDownType>, marker::Edge>), } -pub enum IndexResult { +pub(super) enum IndexResult { KV(usize), Edge(usize), } @@ -46,7 +46,7 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn search_tree<Q: ?Sized>( + pub(super) fn search_tree<Q: ?Sized>( mut self, key: &Q, ) -> SearchResult<BorrowType, K, V, marker::LeafOrInternal, marker::Leaf> @@ -80,7 +80,7 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea /// As a diagnostic service, panics if the range specifies impossible bounds. /// /// The result is meaningful only if the tree is ordered by key. - pub fn search_tree_for_bifurcation<'r, Q: ?Sized, R>( + pub(super) fn search_tree_for_bifurcation<'r, Q: ?Sized, R>( mut self, range: &'r R, ) -> Result< @@ -156,7 +156,7 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea /// the matching child node, if `self` is an internal node. /// /// The result is meaningful only if the tree is ordered by key. - pub fn find_lower_bound_edge<'r, Q>( + pub(super) fn find_lower_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle<Self, marker::Edge>, SearchBound<&'r Q>) @@ -170,7 +170,7 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea } /// Clone of `find_lower_bound_edge` for the upper bound. - pub fn find_upper_bound_edge<'r, Q>( + pub(super) fn find_upper_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle<Self, marker::Edge>, SearchBound<&'r Q>) @@ -192,7 +192,10 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> { /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn search_node<Q: ?Sized>(self, key: &Q) -> SearchResult<BorrowType, K, V, Type, Type> + pub(super) fn search_node<Q: ?Sized>( + self, + key: &Q, + ) -> SearchResult<BorrowType, K, V, Type, Type> where Q: Ord, K: Borrow<Q>, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 9660023d694..041f80c1f2c 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1442,20 +1442,20 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> { /// /// let mut set = BTreeSet::from([1, 2, 3, 4]); /// - /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Included(&3)) }; + /// let mut cursor = set.upper_bound_mut(Bound::Included(&3)); /// assert_eq!(cursor.peek_prev(), Some(&3)); /// assert_eq!(cursor.peek_next(), Some(&4)); /// - /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Excluded(&3)) }; + /// let mut cursor = set.upper_bound_mut(Bound::Excluded(&3)); /// assert_eq!(cursor.peek_prev(), Some(&2)); /// assert_eq!(cursor.peek_next(), Some(&3)); /// - /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Unbounded) }; + /// let mut cursor = set.upper_bound_mut(Bound::Unbounded); /// assert_eq!(cursor.peek_prev(), Some(&4)); /// assert_eq!(cursor.peek_next(), None); /// ``` #[unstable(feature = "btree_cursors", issue = "107540")] - pub unsafe fn upper_bound_mut<Q: ?Sized>(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> + pub fn upper_bound_mut<Q: ?Sized>(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> where T: Borrow<Q> + Ord, Q: Ord, diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 990044e069f..d538ef707eb 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -132,9 +132,11 @@ fn test_difference() { check_difference(&[1, 3, 5, 9, 11], &[3, 6, 9], &[1, 5, 11]); check_difference(&[1, 3, 5, 9, 11], &[0, 1], &[3, 5, 9, 11]); check_difference(&[1, 3, 5, 9, 11], &[11, 12], &[1, 3, 5, 9]); - check_difference(&[-5, 11, 22, 33, 40, 42], &[-12, -5, 14, 23, 34, 38, 39, 50], &[ - 11, 22, 33, 40, 42, - ]); + check_difference( + &[-5, 11, 22, 33, 40, 42], + &[-12, -5, 14, 23, 34, 38, 39, 50], + &[11, 22, 33, 40, 42], + ); if cfg!(miri) { // Miri is too slow @@ -250,9 +252,11 @@ fn test_union() { check_union(&[], &[], &[]); check_union(&[1, 2, 3], &[2], &[1, 2, 3]); check_union(&[2], &[1, 2, 3], &[1, 2, 3]); - check_union(&[1, 3, 5, 9, 11, 16, 19, 24], &[-2, 1, 5, 9, 13, 19], &[ - -2, 1, 3, 5, 9, 11, 13, 16, 19, 24, - ]); + check_union( + &[1, 3, 5, 9, 11, 16, 19, 24], + &[-2, 1, 5, 9, 13, 19], + &[-2, 1, 3, 5, 9, 11, 13, 16, 19, 24], + ); } #[test] diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs index c188ed1da61..87a79e6cf3f 100644 --- a/library/alloc/src/collections/btree/split.rs +++ b/library/alloc/src/collections/btree/split.rs @@ -8,7 +8,7 @@ use super::search::SearchResult::*; impl<K, V> Root<K, V> { /// Calculates the length of both trees that result from splitting up /// a given number of distinct key-value pairs. - pub fn calc_split_length( + pub(super) fn calc_split_length( total_num: usize, root_a: &Root<K, V>, root_b: &Root<K, V>, @@ -31,7 +31,11 @@ impl<K, V> Root<K, V> { /// and if the ordering of `Q` corresponds to that of `K`. /// If `self` respects all `BTreeMap` tree invariants, then both /// `self` and the returned tree will respect those invariants. - pub fn split_off<Q: ?Sized + Ord, A: Allocator + Clone>(&mut self, key: &Q, alloc: A) -> Self + pub(super) fn split_off<Q: ?Sized + Ord, A: Allocator + Clone>( + &mut self, + key: &Q, + alloc: A, + ) -> Self where K: Borrow<Q>, { diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index b7d4f8512a0..812fe229a0f 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -58,7 +58,7 @@ fn list_from<T: Clone>(v: &[T]) -> LinkedList<T> { v.iter().cloned().collect() } -pub fn check_links<T>(list: &LinkedList<T>) { +fn check_links<T>(list: &LinkedList<T>) { unsafe { let mut len = 0; let mut last_ptr: Option<&Node<T>> = None; @@ -696,9 +696,10 @@ fn test_cursor_mut_insert() { cursor.splice_after(p); cursor.splice_before(q); check_links(&m); - assert_eq!(m.iter().cloned().collect::<Vec<_>>(), &[ - 200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6 - ]); + assert_eq!( + m.iter().cloned().collect::<Vec<_>>(), + &[200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6] + ); let mut cursor = m.cursor_front_mut(); cursor.move_prev(); let tmp = cursor.split_before(); @@ -915,9 +916,10 @@ fn extract_if_complex() { assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(list.len(), 14); - assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![ - 1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39 - ]); + assert_eq!( + list.into_iter().collect::<Vec<_>>(), + vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] + ); } { @@ -932,9 +934,10 @@ fn extract_if_complex() { assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(list.len(), 13); - assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![ - 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39 - ]); + assert_eq!( + list.into_iter().collect::<Vec<_>>(), + vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] + ); } { @@ -949,9 +952,10 @@ fn extract_if_complex() { assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(list.len(), 11); - assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![ - 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35 - ]); + assert_eq!( + list.into_iter().collect::<Vec<_>>(), + vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35] + ); } { diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 0b6a55297e1..299c8b8679e 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -823,6 +823,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// assert!(buf.capacity() >= 11); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vecdeque_reserve")] #[track_caller] pub fn reserve(&mut self, additional: usize) { let new_cap = self.len.checked_add(additional).expect("capacity overflow"); @@ -1735,6 +1736,52 @@ impl<T, A: Allocator> VecDeque<T, A> { } } + /// Removes and returns the first element from the deque if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the deque + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_pop_if)] + /// use std::collections::VecDeque; + /// + /// let mut deque: VecDeque<i32> = vec![0, 1, 2, 3, 4].into(); + /// let pred = |x: &mut i32| *x % 2 == 0; + /// + /// assert_eq!(deque.pop_front_if(pred), Some(0)); + /// assert_eq!(deque, [1, 2, 3, 4]); + /// assert_eq!(deque.pop_front_if(pred), None); + /// ``` + #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + pub fn pop_front_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T> { + let first = self.front_mut()?; + if predicate(first) { self.pop_front() } else { None } + } + + /// Removes and returns the last element from the deque if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the deque + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_pop_if)] + /// use std::collections::VecDeque; + /// + /// let mut deque: VecDeque<i32> = vec![0, 1, 2, 3, 4].into(); + /// let pred = |x: &mut i32| *x % 2 == 0; + /// + /// assert_eq!(deque.pop_back_if(pred), Some(4)); + /// assert_eq!(deque, [0, 1, 2, 3]); + /// assert_eq!(deque.pop_back_if(pred), None); + /// ``` + #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + pub fn pop_back_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T> { + let first = self.back_mut()?; + if predicate(first) { self.pop_back() } else { None } + } + /// Prepends an element to the deque. /// /// # Examples diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index 6328b3b4db8..c90679f1797 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -562,9 +562,10 @@ fn make_contiguous_head_to_end() { tester.push_front(i as char); } - assert_eq!(tester, [ - 'P', 'O', 'N', 'M', 'L', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K' - ]); + assert_eq!( + tester, + ['P', 'O', 'N', 'M', 'L', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'] + ); // ABCDEFGHIJKPONML let expected_start = 0; diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index c7d6d8a55c2..fd93045a5ac 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -397,7 +397,7 @@ impl CString { // information about the size of the allocation is correct on Rust's // side. unsafe { - extern "C" { + unsafe extern "C" { /// Provided by libc or compiler_builtins. fn strlen(s: *const c_char) -> usize; } @@ -965,8 +965,9 @@ impl Default for Rc<CStr> { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - let c_str: &CStr = Default::default(); - Rc::from(c_str) + let rc = Rc::<[u8]>::from(*b"\0"); + // `[u8]` has the same layout as `CStr`, and it is `NUL` terminated. + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index b4f08debc93..1bb0f761064 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -88,6 +88,7 @@ #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] #![deny(ffi_unwind_calls)] +#![warn(unreachable_pub)] // // Library features: // tidy-alphabetical-start @@ -102,6 +103,8 @@ #![feature(async_fn_traits)] #![feature(async_iterator)] #![feature(box_uninit_write)] +#![feature(bstr)] +#![feature(bstr_internals)] #![feature(clone_to_uninit)] #![feature(coerce_unsized)] #![feature(const_eval_select)] @@ -153,7 +156,6 @@ #![feature(unicode_internals)] #![feature(unsize)] #![feature(unwrap_infallible)] -#![feature(vec_pop_if)] // tidy-alphabetical-end // // Language features: @@ -225,9 +227,11 @@ pub mod alloc; pub mod boxed; #[cfg(test)] mod boxed { - pub use std::boxed::Box; + pub(crate) use std::boxed::Box; } pub mod borrow; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod collections; #[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] pub mod ffi; diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index ad86bf4bf07..b80d1fc7889 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -97,7 +97,7 @@ impl<T> RawVec<T, Global> { /// `RawVec` with capacity `usize::MAX`. Useful for implementing /// delayed allocation. #[must_use] - pub const fn new() -> Self { + pub(crate) const fn new() -> Self { Self::new_in(Global) } @@ -120,7 +120,7 @@ impl<T> RawVec<T, Global> { #[must_use] #[inline] #[track_caller] - pub fn with_capacity(capacity: usize) -> Self { + pub(crate) fn with_capacity(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } @@ -129,7 +129,7 @@ impl<T> RawVec<T, Global> { #[must_use] #[inline] #[track_caller] - pub fn with_capacity_zeroed(capacity: usize) -> Self { + pub(crate) fn with_capacity_zeroed(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), _marker: PhantomData, @@ -172,7 +172,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. #[inline] - pub const fn new_in(alloc: A) -> Self { + pub(crate) const fn new_in(alloc: A) -> Self { Self { inner: RawVecInner::new_in(alloc, align_of::<T>()), _marker: PhantomData } } @@ -181,7 +181,7 @@ impl<T, A: Allocator> RawVec<T, A> { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -191,7 +191,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// Like `try_with_capacity`, but parameterized over the choice of /// allocator for the returned `RawVec`. #[inline] - pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> { + pub(crate) fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> { match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { Ok(inner) => Ok(Self { inner, _marker: PhantomData }), Err(e) => Err(e), @@ -203,7 +203,7 @@ impl<T, A: Allocator> RawVec<T, A> { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -222,7 +222,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// /// Note, that the requested capacity and `self.capacity()` could differ, as /// an allocator could overallocate and return a greater memory block than requested. - pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit<T>], A> { + pub(crate) unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit<T>], A> { // Sanity-check one half of the safety requirement (we cannot check the other half). debug_assert!( len <= self.capacity(), @@ -247,7 +247,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is /// guaranteed. #[inline] - pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); @@ -265,7 +265,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// /// See [`RawVec::from_raw_parts_in`]. #[inline] - pub unsafe fn from_nonnull_in(ptr: NonNull<T>, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_nonnull_in(ptr: NonNull<T>, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); @@ -278,12 +278,12 @@ impl<T, A: Allocator> RawVec<T, A> { /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. #[inline] - pub const fn ptr(&self) -> *mut T { + pub(crate) const fn ptr(&self) -> *mut T { self.inner.ptr() } #[inline] - pub fn non_null(&self) -> NonNull<T> { + pub(crate) fn non_null(&self) -> NonNull<T> { self.inner.non_null() } @@ -291,13 +291,13 @@ impl<T, A: Allocator> RawVec<T, A> { /// /// This will always be `usize::MAX` if `T` is zero-sized. #[inline] - pub const fn capacity(&self) -> usize { + pub(crate) const fn capacity(&self) -> usize { self.inner.capacity(size_of::<T>()) } /// Returns a shared reference to the allocator backing this `RawVec`. #[inline] - pub fn allocator(&self) -> &A { + pub(crate) fn allocator(&self) -> &A { self.inner.allocator() } @@ -323,7 +323,7 @@ impl<T, A: Allocator> RawVec<T, A> { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn reserve(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve(&mut self, len: usize, additional: usize) { self.inner.reserve(len, additional, T::LAYOUT) } @@ -332,12 +332,16 @@ impl<T, A: Allocator> RawVec<T, A> { #[cfg(not(no_global_oom_handling))] #[inline(never)] #[track_caller] - pub fn grow_one(&mut self) { + pub(crate) fn grow_one(&mut self) { self.inner.grow_one(T::LAYOUT) } /// The same as `reserve`, but returns on errors instead of panicking or aborting. - pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + pub(crate) fn try_reserve( + &mut self, + len: usize, + additional: usize, + ) -> Result<(), TryReserveError> { self.inner.try_reserve(len, additional, T::LAYOUT) } @@ -360,12 +364,12 @@ impl<T, A: Allocator> RawVec<T, A> { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] #[track_caller] - pub fn reserve_exact(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { self.inner.reserve_exact(len, additional, T::LAYOUT) } /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. - pub fn try_reserve_exact( + pub(crate) fn try_reserve_exact( &mut self, len: usize, additional: usize, @@ -386,7 +390,7 @@ impl<T, A: Allocator> RawVec<T, A> { #[cfg(not(no_global_oom_handling))] #[track_caller] #[inline] - pub fn shrink_to_fit(&mut self, cap: usize) { + pub(crate) fn shrink_to_fit(&mut self, cap: usize) { self.inner.shrink_to_fit(cap, T::LAYOUT) } } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index ae3318b839d..09206c2f8b2 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1462,18 +1462,18 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> { /// Provides a raw pointer to the data. /// /// The counts are not affected in any way and the `Rc` is not consumed. The pointer is valid - /// for as long there are strong counts in the `Rc`. + /// for as long as there are strong counts in the `Rc`. /// /// # Examples /// /// ``` /// use std::rc::Rc; /// - /// let x = Rc::new("hello".to_owned()); + /// let x = Rc::new(0); /// let y = Rc::clone(&x); /// let x_ptr = Rc::as_ptr(&x); /// assert_eq!(x_ptr, Rc::as_ptr(&y)); - /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// assert_eq!(unsafe { *x_ptr }, 0); /// ``` #[stable(feature = "weak_into_raw", since = "1.45.0")] #[rustc_never_returns_null_ptr] @@ -2350,11 +2350,10 @@ impl<T: Default> Default for Rc<T> { fn default() -> Rc<T> { unsafe { Self::from_inner( - Box::leak(Box::write(Box::new_uninit(), RcInner { - strong: Cell::new(1), - weak: Cell::new(1), - value: T::default(), - })) + Box::leak(Box::write( + Box::new_uninit(), + RcInner { strong: Cell::new(1), weak: Cell::new(1), value: T::default() }, + )) .into(), ) } @@ -2369,7 +2368,9 @@ impl Default for Rc<str> { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - Rc::from("") + let rc = Rc::<[u8]>::default(); + // `[u8]` has the same layout as `str`. + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const str) } } } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index edc8d99f2f9..1cedead7aa2 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -85,6 +85,7 @@ use crate::vec::Vec; // functions are actually methods that are in `impl [T]` but not in // `core::slice::SliceExt` - we need to supply these functions for the // `test_permutations` test +#[allow(unreachable_pub)] // cfg(test) pub above pub(crate) mod hack { use core::alloc::Allocator; diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 0c9535dfaa6..b29f740ef0f 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -712,8 +712,8 @@ impl String { } } - /// Decode a UTF-16–encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. + /// Decode a native endian UTF-16–encoded vector `v` into a `String`, + /// returning [`Err`] if `v` contains any invalid data. /// /// # Examples /// @@ -745,8 +745,8 @@ impl String { Ok(ret) } - /// Decode a UTF-16–encoded slice `v` into a `String`, replacing - /// invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. + /// Decode a native endian UTF-16–encoded slice `v` into a `String`, + /// replacing invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. /// /// Unlike [`from_utf8_lossy`] which returns a [`Cow<'a, str>`], /// `from_utf16_lossy` returns a `String` since the UTF-16 to UTF-8 @@ -777,8 +777,8 @@ impl String { .collect() } - /// Decode a UTF-16LE–encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. + /// Decode a UTF-16LE–encoded vector `v` into a `String`, + /// returning [`Err`] if `v` contains any invalid data. /// /// # Examples /// @@ -852,8 +852,8 @@ impl String { } } - /// Decode a UTF-16BE–encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. + /// Decode a UTF-16BE–encoded vector `v` into a `String`, + /// returning [`Err`] if `v` contains any invalid data. /// /// # Examples /// diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 8eee7cff208..dba1449347a 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -2740,7 +2740,7 @@ impl<T: ?Sized> Weak<T> { /// # Safety /// /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference. + /// weak reference, and must point to a block of memory allocated by global allocator. /// /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this /// takes ownership of one weak reference currently represented as a raw pointer (the weak @@ -3466,11 +3466,14 @@ impl<T: Default> Default for Arc<T> { fn default() -> Arc<T> { unsafe { Self::from_inner( - Box::leak(Box::write(Box::new_uninit(), ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data: T::default(), - })) + Box::leak(Box::write( + Box::new_uninit(), + ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data: T::default(), + }, + )) .into(), ) } diff --git a/library/alloc/src/testing/crash_test.rs b/library/alloc/src/testing/crash_test.rs index 684bac60d9a..8e00e4f41e5 100644 --- a/library/alloc/src/testing/crash_test.rs +++ b/library/alloc/src/testing/crash_test.rs @@ -11,7 +11,7 @@ use crate::fmt::Debug; // the `Debug` trait is the only thing we use from `crate /// Crash test dummies are identified and ordered by an id, so they can be used /// as keys in a BTreeMap. #[derive(Debug)] -pub struct CrashTestDummy { +pub(crate) struct CrashTestDummy { pub id: usize, cloned: AtomicUsize, dropped: AtomicUsize, @@ -20,7 +20,7 @@ pub struct CrashTestDummy { impl CrashTestDummy { /// Creates a crash test dummy design. The `id` determines order and equality of instances. - pub fn new(id: usize) -> CrashTestDummy { + pub(crate) fn new(id: usize) -> CrashTestDummy { CrashTestDummy { id, cloned: AtomicUsize::new(0), @@ -31,34 +31,34 @@ impl CrashTestDummy { /// Creates an instance of a crash test dummy that records what events it experiences /// and optionally panics. - pub fn spawn(&self, panic: Panic) -> Instance<'_> { + pub(crate) fn spawn(&self, panic: Panic) -> Instance<'_> { Instance { origin: self, panic } } /// Returns how many times instances of the dummy have been cloned. - pub fn cloned(&self) -> usize { + pub(crate) fn cloned(&self) -> usize { self.cloned.load(SeqCst) } /// Returns how many times instances of the dummy have been dropped. - pub fn dropped(&self) -> usize { + pub(crate) fn dropped(&self) -> usize { self.dropped.load(SeqCst) } /// Returns how many times instances of the dummy have had their `query` member invoked. - pub fn queried(&self) -> usize { + pub(crate) fn queried(&self) -> usize { self.queried.load(SeqCst) } } #[derive(Debug)] -pub struct Instance<'a> { +pub(crate) struct Instance<'a> { origin: &'a CrashTestDummy, panic: Panic, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Panic { +pub(crate) enum Panic { Never, InClone, InDrop, @@ -66,12 +66,12 @@ pub enum Panic { } impl Instance<'_> { - pub fn id(&self) -> usize { + pub(crate) fn id(&self) -> usize { self.origin.id } /// Some anonymous query, the result of which is already given. - pub fn query<R>(&self, result: R) -> R { + pub(crate) fn query<R>(&self, result: R) -> R { self.origin.queried.fetch_add(1, SeqCst); if self.panic == Panic::InQuery { panic!("panic in `query`"); diff --git a/library/alloc/src/testing/mod.rs b/library/alloc/src/testing/mod.rs index 7a094f8a595..c8457daf93e 100644 --- a/library/alloc/src/testing/mod.rs +++ b/library/alloc/src/testing/mod.rs @@ -1,3 +1,3 @@ -pub mod crash_test; -pub mod ord_chaos; -pub mod rng; +pub(crate) mod crash_test; +pub(crate) mod ord_chaos; +pub(crate) mod rng; diff --git a/library/alloc/src/testing/ord_chaos.rs b/library/alloc/src/testing/ord_chaos.rs index 96ce7c15790..55e1ae5e3de 100644 --- a/library/alloc/src/testing/ord_chaos.rs +++ b/library/alloc/src/testing/ord_chaos.rs @@ -4,7 +4,7 @@ use std::ptr; // Minimal type with an `Ord` implementation violating transitivity. #[derive(Debug)] -pub enum Cyclic3 { +pub(crate) enum Cyclic3 { A, B, C, @@ -37,16 +37,16 @@ impl Eq for Cyclic3 {} // Controls the ordering of values wrapped by `Governed`. #[derive(Debug)] -pub struct Governor { +pub(crate) struct Governor { flipped: Cell<bool>, } impl Governor { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Governor { flipped: Cell::new(false) } } - pub fn flip(&self) { + pub(crate) fn flip(&self) { self.flipped.set(!self.flipped.get()); } } @@ -55,7 +55,7 @@ impl Governor { // (assuming that `T` respects total order), but can suddenly be made to invert // that total order. #[derive(Debug)] -pub struct Governed<'a, T>(pub T, pub &'a Governor); +pub(crate) struct Governed<'a, T>(pub T, pub &'a Governor); impl<T: Ord> PartialOrd for Governed<'_, T> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { diff --git a/library/alloc/src/testing/rng.rs b/library/alloc/src/testing/rng.rs index ecf543bee03..77d3348f38a 100644 --- a/library/alloc/src/testing/rng.rs +++ b/library/alloc/src/testing/rng.rs @@ -1,5 +1,5 @@ /// XorShiftRng -pub struct DeterministicRng { +pub(crate) struct DeterministicRng { count: usize, x: u32, y: u32, @@ -8,12 +8,12 @@ pub struct DeterministicRng { } impl DeterministicRng { - pub fn new() -> Self { + pub(crate) fn new() -> Self { DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } } /// Guarantees that each returned number is unique. - pub fn next(&mut self) -> u32 { + pub(crate) fn next(&mut self) -> u32 { self.count += 1; assert!(self.count <= 70029); let x = self.x; diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index cd2afd7a473..55cd0569538 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1267,6 +1267,7 @@ impl<T, A: Allocator> Vec<T, A> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_reserve")] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); } @@ -2511,15 +2512,13 @@ impl<T, A: Allocator> Vec<T, A> { } } - /// Removes and returns the last element in a vector if the predicate + /// Removes and returns the last element from a vector if the predicate /// returns `true`, or [`None`] if the predicate returns false or the vector - /// is empty. + /// is empty (the predicate will not be called in that case). /// /// # Examples /// /// ``` - /// #![feature(vec_pop_if)] - /// /// let mut vec = vec![1, 2, 3, 4]; /// let pred = |x: &mut i32| *x % 2 == 0; /// @@ -2527,13 +2526,10 @@ impl<T, A: Allocator> Vec<T, A> { /// assert_eq!(vec, [1, 2, 3]); /// assert_eq!(vec.pop_if(pred), None); /// ``` - #[unstable(feature = "vec_pop_if", issue = "122741")] - pub fn pop_if<F>(&mut self, f: F) -> Option<T> - where - F: FnOnce(&mut T) -> bool, - { + #[stable(feature = "vec_pop_if", since = "CURRENT_RUSTC_VERSION")] + pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T> { let last = self.last_mut()?; - if f(last) { self.pop() } else { None } + if predicate(last) { self.pop() } else { None } } /// Moves all the elements of `other` into `self`, leaving `other` empty. @@ -2574,9 +2570,11 @@ impl<T, A: Allocator> Vec<T, A> { self.len += count; } - /// Removes the specified range from the vector in bulk, returning all - /// removed elements as an iterator. If the iterator is dropped before - /// being fully consumed, it drops the remaining removed elements. + /// Removes the subslice indicated by the given range from the vector, + /// returning a double-ended iterator over the removed subslice. + /// + /// If the iterator is dropped before being fully consumed, + /// it drops the remaining removed elements. /// /// The returned iterator keeps a mutable borrow on the vector to optimize /// its implementation. @@ -3016,10 +3014,9 @@ impl<T: Clone, A: Allocator> Vec<T, A> { /// Iterates over the slice `other`, clones each element, and then appends /// it to this `Vec`. The `other` slice is traversed in-order. /// - /// Note that this function is same as [`extend`] except that it is - /// specialized to work with slices instead. If and when Rust gets - /// specialization this function will likely be deprecated (but still - /// available). + /// Note that this function is the same as [`extend`], + /// except that it also works with slice elements that are Clone but not Copy. + /// If Rust gets specialization this function may be deprecated. /// /// # Examples /// diff --git a/library/alloc/tests/collections/binary_heap.rs b/library/alloc/tests/collections/binary_heap.rs index 55405ffe8c4..95f4c3e614f 100644 --- a/library/alloc/tests/collections/binary_heap.rs +++ b/library/alloc/tests/collections/binary_heap.rs @@ -502,9 +502,7 @@ fn test_retain_catch_unwind() { // even if the order might not be correct. // // Destructors must be called exactly once per element. -// FIXME: re-enable emscripten once it can unwind again #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_safe() { use std::cmp; diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 393bdfe48b7..1bcec4037cd 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -37,7 +37,7 @@ #![feature(local_waker)] #![feature(str_as_str)] #![feature(strict_provenance_lints)] -#![feature(vec_pop_if)] +#![feature(vec_deque_pop_if)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] #![allow(internal_features)] @@ -93,9 +93,6 @@ fn test_rng() -> rand_xorshift::XorShiftRng { rand::SeedableRng::from_seed(seed) } -// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten. -// See https://github.com/kripken/emscripten-fastcomp/issues/169 -#[cfg(not(target_os = "emscripten"))] #[test] fn test_boxed_hasher() { let ordinary_hash = hash(&5u32); diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index 9625e3d2b5e..f990a41b679 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1414,7 +1414,6 @@ fn test_box_slice_clone() { #[test] #[allow(unused_must_use)] // here, we care about the side effects of `.clone()` -#[cfg_attr(target_os = "emscripten", ignore)] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_box_slice_clone_panics() { use std::sync::Arc; diff --git a/library/alloc/tests/sort/tests.rs b/library/alloc/tests/sort/tests.rs index 4cc79010e8f..d321f8df518 100644 --- a/library/alloc/tests/sort/tests.rs +++ b/library/alloc/tests/sort/tests.rs @@ -11,7 +11,14 @@ use crate::sort::{Sort, known_good_stable_sort, patterns}; #[cfg(miri)] const TEST_LENGTHS: &[usize] = &[2, 3, 4, 7, 10, 15, 20, 24, 33, 50, 100, 171, 300]; -#[cfg(not(miri))] +// node.js gives out of memory error to use with length 1_100_000 +#[cfg(all(not(miri), target_os = "emscripten"))] +const TEST_LENGTHS: &[usize] = &[ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 20, 24, 30, 32, 33, 35, 50, 100, 200, 500, 1_000, + 2_048, 5_000, 10_000, 100_000, +]; + +#[cfg(all(not(miri), not(target_os = "emscripten")))] const TEST_LENGTHS: &[usize] = &[ 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 20, 24, 30, 32, 33, 35, 50, 100, 200, 500, 1_000, 2_048, 5_000, 10_000, 100_000, 1_100_000, diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 6f930ab0853..23a0e5e5256 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -1524,18 +1524,14 @@ fn test_lines() { t("bare\r", &["bare\r"]); t("bare\rcr", &["bare\rcr"]); t("Text\n\r", &["Text", "\r"]); - t("\nMäry häd ä little lämb\n\r\nLittle lämb\n", &[ - "", - "Märy häd ä little lämb", - "", - "Little lämb", - ]); - t("\r\nMäry häd ä little lämb\n\nLittle lämb", &[ - "", - "Märy häd ä little lämb", - "", - "Little lämb", - ]); + t( + "\nMäry häd ä little lämb\n\r\nLittle lämb\n", + &["", "Märy häd ä little lämb", "", "Little lämb"], + ); + t( + "\r\nMäry häd ä little lämb\n\nLittle lämb", + &["", "Märy häd ä little lämb", "", "Little lämb"], + ); } #[test] @@ -1978,73 +1974,88 @@ mod pattern { assert_eq!(v, right); } - make_test!(str_searcher_ascii_haystack, "bb", "abbcbbd", [ - Reject(0, 1), - Match(1, 3), - Reject(3, 4), - Match(4, 6), - Reject(6, 7), - ]); - make_test!(str_searcher_ascii_haystack_seq, "bb", "abbcbbbbd", [ - Reject(0, 1), - Match(1, 3), - Reject(3, 4), - Match(4, 6), - Match(6, 8), - Reject(8, 9), - ]); - make_test!(str_searcher_empty_needle_ascii_haystack, "", "abbcbbd", [ - Match(0, 0), - Reject(0, 1), - Match(1, 1), - Reject(1, 2), - Match(2, 2), - Reject(2, 3), - Match(3, 3), - Reject(3, 4), - Match(4, 4), - Reject(4, 5), - Match(5, 5), - Reject(5, 6), - Match(6, 6), - Reject(6, 7), - Match(7, 7), - ]); - make_test!(str_searcher_multibyte_haystack, " ", "├──", [ - Reject(0, 3), - Reject(3, 6), - Reject(6, 9), - ]); - make_test!(str_searcher_empty_needle_multibyte_haystack, "", "├──", [ - Match(0, 0), - Reject(0, 3), - Match(3, 3), - Reject(3, 6), - Match(6, 6), - Reject(6, 9), - Match(9, 9), - ]); + make_test!( + str_searcher_ascii_haystack, + "bb", + "abbcbbd", + [Reject(0, 1), Match(1, 3), Reject(3, 4), Match(4, 6), Reject(6, 7),] + ); + make_test!( + str_searcher_ascii_haystack_seq, + "bb", + "abbcbbbbd", + [Reject(0, 1), Match(1, 3), Reject(3, 4), Match(4, 6), Match(6, 8), Reject(8, 9),] + ); + make_test!( + str_searcher_empty_needle_ascii_haystack, + "", + "abbcbbd", + [ + Match(0, 0), + Reject(0, 1), + Match(1, 1), + Reject(1, 2), + Match(2, 2), + Reject(2, 3), + Match(3, 3), + Reject(3, 4), + Match(4, 4), + Reject(4, 5), + Match(5, 5), + Reject(5, 6), + Match(6, 6), + Reject(6, 7), + Match(7, 7), + ] + ); + make_test!( + str_searcher_multibyte_haystack, + " ", + "├──", + [Reject(0, 3), Reject(3, 6), Reject(6, 9),] + ); + make_test!( + str_searcher_empty_needle_multibyte_haystack, + "", + "├──", + [ + Match(0, 0), + Reject(0, 3), + Match(3, 3), + Reject(3, 6), + Match(6, 6), + Reject(6, 9), + Match(9, 9), + ] + ); make_test!(str_searcher_empty_needle_empty_haystack, "", "", [Match(0, 0),]); make_test!(str_searcher_nonempty_needle_empty_haystack, "├", "", []); - make_test!(char_searcher_ascii_haystack, 'b', "abbcbbd", [ - Reject(0, 1), - Match(1, 2), - Match(2, 3), - Reject(3, 4), - Match(4, 5), - Match(5, 6), - Reject(6, 7), - ]); - make_test!(char_searcher_multibyte_haystack, ' ', "├──", [ - Reject(0, 3), - Reject(3, 6), - Reject(6, 9), - ]); - make_test!(char_searcher_short_haystack, '\u{1F4A9}', "* \t", [ - Reject(0, 1), - Reject(1, 2), - Reject(2, 3), - ]); + make_test!( + char_searcher_ascii_haystack, + 'b', + "abbcbbd", + [ + Reject(0, 1), + Match(1, 2), + Match(2, 3), + Reject(3, 4), + Match(4, 5), + Match(5, 6), + Reject(6, 7), + ] + ); + make_test!( + char_searcher_multibyte_haystack, + ' ', + "├──", + [Reject(0, 3), Reject(3, 6), Reject(6, 9),] + ); + make_test!( + char_searcher_short_haystack, + '\u{1F4A9}', + "* \t", + [Reject(0, 1), Reject(1, 2), Reject(2, 3),] + ); // See #85462 #[test] @@ -2297,21 +2308,21 @@ fn utf8_chars() { assert_eq!(schs.len(), 4); assert_eq!(schs.iter().cloned().collect::<String>(), s); - assert!((from_utf8(s.as_bytes()).is_ok())); + assert!(from_utf8(s.as_bytes()).is_ok()); // invalid prefix - assert!((!from_utf8(&[0x80]).is_ok())); + assert!(!from_utf8(&[0x80]).is_ok()); // invalid 2 byte prefix - assert!((!from_utf8(&[0xc0]).is_ok())); - assert!((!from_utf8(&[0xc0, 0x10]).is_ok())); + assert!(!from_utf8(&[0xc0]).is_ok()); + assert!(!from_utf8(&[0xc0, 0x10]).is_ok()); // invalid 3 byte prefix - assert!((!from_utf8(&[0xe0]).is_ok())); - assert!((!from_utf8(&[0xe0, 0x10]).is_ok())); - assert!((!from_utf8(&[0xe0, 0xff, 0x10]).is_ok())); + assert!(!from_utf8(&[0xe0]).is_ok()); + assert!(!from_utf8(&[0xe0, 0x10]).is_ok()); + assert!(!from_utf8(&[0xe0, 0xff, 0x10]).is_ok()); // invalid 4 byte prefix - assert!((!from_utf8(&[0xf0]).is_ok())); - assert!((!from_utf8(&[0xf0, 0x10]).is_ok())); - assert!((!from_utf8(&[0xf0, 0xff, 0x10]).is_ok())); - assert!((!from_utf8(&[0xf0, 0xff, 0xff, 0x10]).is_ok())); + assert!(!from_utf8(&[0xf0]).is_ok()); + assert!(!from_utf8(&[0xf0, 0x10]).is_ok()); + assert!(!from_utf8(&[0xf0, 0xff, 0x10]).is_ok()); + assert!(!from_utf8(&[0xf0, 0xff, 0xff, 0x10]).is_ok()); } #[test] diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index 1c8bff1564d..d996c55f946 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -154,19 +154,28 @@ fn test_fromutf8error_into_lossy() { #[test] fn test_from_utf16() { let pairs = [ - (String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"), vec![ - 0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800, 0xdf39, 0xd800, - 0xdf3b, 0xd800, 0xdf30, 0x000a, - ]), - (String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"), vec![ - 0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801, 0xdc32, 0xd801, - 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801, 0xdc4d, 0x000a, - ]), - (String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"), vec![ - 0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800, 0xdf11, 0xd800, - 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800, 0xdf15, 0xd800, 0xdf04, 0xd800, - 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11, 0x000a, - ]), + ( + String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"), + vec![ + 0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800, 0xdf39, + 0xd800, 0xdf3b, 0xd800, 0xdf30, 0x000a, + ], + ), + ( + String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"), + vec![ + 0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801, 0xdc32, + 0xd801, 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801, 0xdc4d, 0x000a, + ], + ), + ( + String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"), + vec![ + 0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800, 0xdf11, + 0xd800, 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800, 0xdf15, 0xd800, + 0xdf04, 0xd800, 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11, 0x000a, + ], + ), ( String::from("𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n"), vec![ diff --git a/library/alloc/tests/sync.rs b/library/alloc/tests/sync.rs index 7a9a4abfdc6..6d3ab1b1d11 100644 --- a/library/alloc/tests/sync.rs +++ b/library/alloc/tests/sync.rs @@ -128,6 +128,7 @@ fn try_unwrap() { } #[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn into_inner() { for _ in 0..100 // ^ Increase chances of hitting potential race conditions diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index b24daec2968..fe1db56414e 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1587,9 +1587,7 @@ fn extract_if_complex() { } } -// FIXME: re-enable emscripten once it can unwind again #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn extract_if_consumed_panic() { use std::rc::Rc; @@ -1640,9 +1638,7 @@ fn extract_if_consumed_panic() { } } -// FIXME: Re-enable emscripten once it can catch panics #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn extract_if_unconsumed_panic() { use std::rc::Rc; diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 4b8d3c735f7..1b03c29e5bd 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -81,6 +81,45 @@ fn test_parameterized<T: Clone + PartialEq + Debug>(a: T, b: T, c: T, d: T) { } #[test] +fn test_pop_if() { + let mut deq: VecDeque<_> = vec![0, 1, 2, 3, 4].into(); + let pred = |x: &mut i32| *x % 2 == 0; + + assert_eq!(deq.pop_front_if(pred), Some(0)); + assert_eq!(deq, [1, 2, 3, 4]); + + assert_eq!(deq.pop_front_if(pred), None); + assert_eq!(deq, [1, 2, 3, 4]); + + assert_eq!(deq.pop_back_if(pred), Some(4)); + assert_eq!(deq, [1, 2, 3]); + + assert_eq!(deq.pop_back_if(pred), None); + assert_eq!(deq, [1, 2, 3]); +} + +#[test] +fn test_pop_if_empty() { + let mut deq = VecDeque::<i32>::new(); + assert_eq!(deq.pop_front_if(|_| true), None); + assert_eq!(deq.pop_back_if(|_| true), None); + assert!(deq.is_empty()); +} + +#[test] +fn test_pop_if_mutates() { + let mut v: VecDeque<_> = vec![-1, 1].into(); + let pred = |x: &mut i32| { + *x *= 2; + false + }; + assert_eq!(v.pop_front_if(pred), None); + assert_eq!(v, [-2, 1]); + assert_eq!(v.pop_back_if(pred), None); + assert_eq!(v, [-2, 2]); +} + +#[test] fn test_push_front_grow() { let mut deq = VecDeque::new(); for i in 0..66 { diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 46c55c437cc..b7c6db6c78d 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -15,19 +15,6 @@ edition = "2021" test = false bench = false -[[test]] -name = "coretests" -path = "tests/lib.rs" - -[[bench]] -name = "corebenches" -path = "benches/lib.rs" -test = true - -[dev-dependencies] -rand = { version = "0.8.5", default-features = false } -rand_xorshift = { version = "0.3.0", default-features = false } - [features] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = [] diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index aa841db045c..9805cee1c33 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -49,26 +49,26 @@ impl fmt::Display for AllocError { /// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of /// data described via [`Layout`][]. /// -/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers because having -/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the +/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers. +/// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the /// allocated memory. /// -/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `Allocator`. If an underlying -/// allocator does not support this (like jemalloc) or return a null pointer (such as -/// `libc::malloc`), this must be caught by the implementation. +/// In contrast to [`GlobalAlloc`][], `Allocator` allows zero-sized allocations. If an underlying +/// allocator does not support this (like jemalloc) or responds by returning a null pointer +/// (such as `libc::malloc`), this must be caught by the implementation. /// /// ### Currently allocated memory /// -/// Some of the methods require that a memory block be *currently allocated* via an allocator. This -/// means that: +/// Some of the methods require that a memory block is *currently allocated* by an allocator. +/// This means that: +/// * the starting address for that memory block was previously +/// returned by [`allocate`], [`grow`], or [`shrink`], and +/// * the memory block has not subsequently been deallocated. /// -/// * the starting address for that memory block was previously returned by [`allocate`], [`grow`], or -/// [`shrink`], and -/// -/// * the memory block has not been subsequently deallocated, where blocks are either deallocated -/// directly by being passed to [`deallocate`] or were changed by being passed to [`grow`] or -/// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer -/// remains valid. +/// A memory block is deallocated by a call to [`deallocate`], +/// or by a call to [`grow`] or [`shrink`] that returns `Ok`. +/// A call to `grow` or `shrink` that returns `Err`, +/// does not deallocate the memory block passed to it. /// /// [`allocate`]: Allocator::allocate /// [`grow`]: Allocator::grow @@ -77,32 +77,28 @@ impl fmt::Display for AllocError { /// /// ### Memory fitting /// -/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to -/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the +/// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the /// following conditions must hold: -/// -/// * The block must be allocated with the same alignment as [`layout.align()`], and -/// -/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where: -/// - `min` is the size of the layout most recently used to allocate the block, and -/// - `max` is the latest actual size returned from [`allocate`], [`grow`], or [`shrink`]. +/// * the memory block must be *currently allocated* with alignment of [`layout.align()`], and +/// * [`layout.size()`] must fall in the range `min ..= max`, where: +/// - `min` is the size of the layout used to allocate the block, and +/// - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`]. /// /// [`layout.align()`]: Layout::align /// [`layout.size()`]: Layout::size /// /// # Safety /// -/// * Memory blocks returned from an allocator that are [*currently allocated*] must point to -/// valid memory and retain their validity while they are [*currently allocated*] and the shorter -/// of: -/// - the borrow-checker lifetime of the allocator type itself. -/// - as long as at least one of the instance and all of its clones has not been dropped. +/// Memory blocks that are [*currently allocated*] by an allocator, +/// must point to valid memory, and retain their validity while until either: +/// - the memory block is deallocated, or +/// - the allocator is dropped. /// -/// * copying, cloning, or moving the allocator must not invalidate memory blocks returned from this -/// allocator. A copied or cloned allocator must behave like the same allocator, and +/// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it. +/// A copied or cloned allocator must behave like the original allocator. /// -/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other -/// method of the allocator. +/// A memory block which is [*currently allocated*] may be passed to +/// any method of the allocator that accepts such an argument. /// /// [*currently allocated*]: #currently-allocated-memory #[unstable(feature = "allocator_api", issue = "32838")] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index ba61679564a..28329bb0908 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -156,7 +156,6 @@ pub const fn from_mut<T>(s: &mut T) -> &mut [T; 1] { /// The error type returned when a conversion from a slice to an array fails. #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_allowed_through_unstable_modules] #[derive(Debug, Copy, Clone)] pub struct TryFromSliceError(()); diff --git a/library/core/src/bstr.rs b/library/core/src/bstr.rs new file mode 100644 index 00000000000..74e07f3d242 --- /dev/null +++ b/library/core/src/bstr.rs @@ -0,0 +1,581 @@ +//! The `ByteStr` type and trait implementations. + +use crate::borrow::{Borrow, BorrowMut}; +use crate::cmp::Ordering; +use crate::ops::{ + Deref, DerefMut, DerefPure, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, + RangeTo, RangeToInclusive, +}; +use crate::{fmt, hash}; + +/// A wrapper for `&[u8]` representing a human-readable string that's conventionally, but not +/// always, UTF-8. +/// +/// Unlike `&str`, this type permits non-UTF-8 contents, making it suitable for user input, +/// non-native filenames (as `Path` only supports native filenames), and other applications that +/// need to round-trip whatever data the user provides. +/// +/// For an owned, growable byte string buffer, use +/// [`ByteString`](../../std/bstr/struct.ByteString.html). +/// +/// `ByteStr` implements `Deref` to `[u8]`, so all methods available on `[u8]` are available on +/// `ByteStr`. +/// +/// # Representation +/// +/// A `&ByteStr` has the same representation as a `&str`. That is, a `&ByteStr` is a wide pointer +/// which includes a pointer to some bytes and a length. +/// +/// # Trait implementations +/// +/// The `ByteStr` type has a number of trait implementations, and in particular, defines equality +/// and comparisons between `&ByteStr`, `&str`, and `&[u8]`, for convenience. +/// +/// The `Debug` implementation for `ByteStr` shows its bytes as a normal string, with invalid UTF-8 +/// presented as hex escape sequences. +/// +/// The `Display` implementation behaves as if the `ByteStr` were first lossily converted to a +/// `str`, with invalid UTF-8 presented as the Unicode replacement character: � +/// +#[unstable(feature = "bstr", issue = "134915")] +#[repr(transparent)] +#[doc(alias = "BStr")] +pub struct ByteStr(pub [u8]); + +impl ByteStr { + /// Creates a `ByteStr` slice from anything that can be converted to a byte slice. + /// + /// This is a zero-cost conversion. + /// + /// # Example + /// + /// You can create a `ByteStr` from a byte array, a byte slice or a string slice: + /// + /// ``` + /// # #![feature(bstr)] + /// # use std::bstr::ByteStr; + /// let a = ByteStr::new(b"abc"); + /// let b = ByteStr::new(&b"abc"[..]); + /// let c = ByteStr::new("abc"); + /// + /// assert_eq!(a, b); + /// assert_eq!(a, c); + /// ``` + #[inline] + #[unstable(feature = "bstr", issue = "134915")] + pub fn new<B: ?Sized + AsRef<[u8]>>(bytes: &B) -> &Self { + ByteStr::from_bytes(bytes.as_ref()) + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn from_bytes(slice: &[u8]) -> &Self { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to + // the wrapped type into a reference to the wrapper type. + unsafe { &*(slice as *const [u8] as *const Self) } + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn from_bytes_mut(slice: &mut [u8]) -> &mut Self { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to + // the wrapped type into a reference to the wrapper type. + unsafe { &mut *(slice as *mut [u8] as *mut Self) } + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Deref for ByteStr { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl DerefMut for ByteStr { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for ByteStr {} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Debug for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\"")?; + for chunk in self.utf8_chunks() { + for c in chunk.valid().chars() { + match c { + '\0' => write!(f, "\\0")?, + '\x01'..='\x7f' => write!(f, "{}", (c as u8).escape_ascii())?, + _ => write!(f, "{}", c.escape_debug())?, + } + } + write!(f, "{}", chunk.invalid().escape_ascii())?; + } + write!(f, "\"")?; + Ok(()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Display for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt_nopad(this: &ByteStr, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for chunk in this.utf8_chunks() { + f.write_str(chunk.valid())?; + if !chunk.invalid().is_empty() { + f.write_str("\u{FFFD}")?; + } + } + Ok(()) + } + + let Some(align) = f.align() else { + return fmt_nopad(self, f); + }; + let nchars: usize = self + .utf8_chunks() + .map(|chunk| chunk.valid().len() + if chunk.invalid().is_empty() { 0 } else { 1 }) + .sum(); + let padding = f.width().unwrap_or(0).saturating_sub(nchars); + let fill = f.fill(); + let (lpad, rpad) = match align { + fmt::Alignment::Left => (0, padding), + fmt::Alignment::Right => (padding, 0), + fmt::Alignment::Center => { + let half = padding / 2; + (half, half + padding % 2) + } + }; + for _ in 0..lpad { + write!(f, "{fill}")?; + } + fmt_nopad(self, f)?; + for _ in 0..rpad { + write!(f, "{fill}")?; + } + + Ok(()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<[u8]> for ByteStr { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<ByteStr> for ByteStr { + #[inline] + fn as_ref(&self) -> &ByteStr { + self + } +} + +// `impl AsRef<ByteStr> for [u8]` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<ByteStr> for str { + #[inline] + fn as_ref(&self) -> &ByteStr { + ByteStr::new(self) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut<[u8]> for ByteStr { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +// `impl AsMut<ByteStr> for [u8]` omitted to avoid widespread inference failures + +// `impl Borrow<ByteStr> for [u8]` omitted to avoid widespread inference failures + +// `impl Borrow<ByteStr> for str` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow<[u8]> for ByteStr { + #[inline] + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +// `impl BorrowMut<ByteStr> for [u8]` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut<[u8]> for ByteStr { + #[inline] + fn borrow_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> Default for &'a ByteStr { + fn default() -> Self { + ByteStr::from_bytes(b"") + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> Default for &'a mut ByteStr { + fn default() -> Self { + ByteStr::from_bytes_mut(&mut []) + } +} + +// Omitted due to inference failures +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a, const N: usize> From<&'a [u8; N]> for &'a ByteStr { +// #[inline] +// fn from(s: &'a [u8; N]) -> Self { +// ByteStr::from_bytes(s) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a [u8]> for &'a ByteStr { +// #[inline] +// fn from(s: &'a [u8]) -> Self { +// ByteStr::from_bytes(s) +// } +// } + +// Omitted due to slice-from-array-issue-113238: +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a ByteStr> for &'a [u8] { +// #[inline] +// fn from(s: &'a ByteStr) -> Self { +// &s.0 +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a mut ByteStr> for &'a mut [u8] { +// #[inline] +// fn from(s: &'a mut ByteStr) -> Self { +// &mut s.0 +// } +// } + +// Omitted due to inference failures +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a str> for &'a ByteStr { +// #[inline] +// fn from(s: &'a str) -> Self { +// ByteStr::from_bytes(s.as_bytes()) +// } +// } + +#[unstable(feature = "bstr", issue = "134915")] +impl hash::Hash for ByteStr { + #[inline] + fn hash<H: hash::Hasher>(&self, state: &mut H) { + self.0.hash(state); + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<usize> for ByteStr { + type Output = u8; + + #[inline] + fn index(&self, idx: usize) -> &u8 { + &self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeFull> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, _: RangeFull) -> &ByteStr { + self + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<Range<usize>> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: Range<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeInclusive<usize>> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeInclusive<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeFrom<usize>> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeFrom<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeTo<usize>> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeTo<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index<RangeToInclusive<usize>> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeToInclusive<usize>) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<usize> for ByteStr { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut u8 { + &mut self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeFull> for ByteStr { + #[inline] + fn index_mut(&mut self, _: RangeFull) -> &mut ByteStr { + self + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<Range<usize>> for ByteStr { + #[inline] + fn index_mut(&mut self, r: Range<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeInclusive<usize>> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeInclusive<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeFrom<usize>> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeFrom<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeTo<usize>> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeTo<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut<RangeToInclusive<usize>> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeToInclusive<usize>) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Eq for ByteStr {} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialEq<ByteStr> for ByteStr { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + &self.0 == &other.0 + } +} + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq { + ($lhs:ty, $rhs:ty) => { + #[allow(unused_lifetimes)] + impl<'a> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = other.as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + impl<'a> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = self.as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq; + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq_ord { + ($lhs:ty, $rhs:ty) => { + $crate::bstr::impl_partial_eq!($lhs, $rhs); + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> { + let other: &[u8] = other.as_ref(); + PartialOrd::partial_cmp(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> { + let this: &[u8] = self.as_ref(); + PartialOrd::partial_cmp(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq_ord; + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq_n { + ($lhs:ty, $rhs:ty) => { + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<const N: usize> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = other.as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<const N: usize> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = self.as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq_n; + +// PartialOrd with `[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteStr, [u8]); +// PartialOrd with `&[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteStr, &[u8]); +// PartialOrd with `str` omitted to avoid inference failures +impl_partial_eq!(ByteStr, str); +// PartialOrd with `&str` omitted to avoid inference failures +impl_partial_eq!(ByteStr, &str); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteStr, [u8; N]); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteStr, &[u8; N]); + +#[unstable(feature = "bstr", issue = "134915")] +impl Ord for ByteStr { + #[inline] + fn cmp(&self, other: &ByteStr) -> Ordering { + Ord::cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialOrd for ByteStr { + #[inline] + fn partial_cmp(&self, other: &ByteStr) -> Option<Ordering> { + PartialOrd::partial_cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteStr> for &'a str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteStr) -> Result<Self, Self::Error> { + crate::str::from_utf8(&s.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a mut ByteStr> for &'a mut str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a mut ByteStr) -> Result<Self, Self::Error> { + crate::str::from_utf8_mut(&mut s.0) + } +} diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 20187e478aa..cbf00106c51 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1590,10 +1590,10 @@ impl<'b, T: ?Sized> Ref<'b, T> { { let (a, b) = f(&*orig); let borrow = orig.borrow.clone(); - (Ref { value: NonNull::from(a), borrow }, Ref { - value: NonNull::from(b), - borrow: orig.borrow, - }) + ( + Ref { value: NonNull::from(a), borrow }, + Ref { value: NonNull::from(b), borrow: orig.borrow }, + ) } /// Converts into a reference to the underlying data. @@ -1758,11 +1758,10 @@ impl<'b, T: ?Sized> RefMut<'b, T> { { let borrow = orig.borrow.clone(); let (a, b) = f(&mut *orig); - (RefMut { value: NonNull::from(a), borrow, marker: PhantomData }, RefMut { - value: NonNull::from(b), - borrow: orig.borrow, - marker: PhantomData, - }) + ( + RefMut { value: NonNull::from(a), borrow, marker: PhantomData }, + RefMut { value: NonNull::from(b), borrow: orig.borrow, marker: PhantomData }, + ) } /// Converts into a mutable reference to the underlying data. @@ -2118,6 +2117,35 @@ impl<T> UnsafeCell<T> { pub const fn into_inner(self) -> T { self.value } + + /// Replace the value in this `UnsafeCell` and return the old value. + /// + /// # Safety + /// + /// The caller must take care to avoid aliasing and data races. + /// + /// - It is Undefined Behavior to allow calls to race with + /// any other access to the wrapped value. + /// - It is Undefined Behavior to call this while any other + /// reference(s) to the wrapped value are alive. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_access)] + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// let old = unsafe { uc.replace(10) }; + /// assert_eq!(old, 5); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_access", issue = "136327")] + pub const unsafe fn replace(&self, value: T) -> T { + // SAFETY: pointer comes from `&self` so naturally satisfies invariants. + unsafe { ptr::replace(self.get(), value) } + } } impl<T: ?Sized> UnsafeCell<T> { @@ -2230,6 +2258,61 @@ impl<T: ?Sized> UnsafeCell<T> { // no guarantee for user code that this will work in future versions of the compiler! this as *const T as *mut T } + + /// Get a shared reference to the value within the `UnsafeCell`. + /// + /// # Safety + /// + /// - It is Undefined Behavior to call this while any mutable + /// reference to the wrapped value is alive. + /// - Mutating the wrapped value while the returned + /// reference is alive is Undefined Behavior. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_access)] + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// let val = unsafe { uc.as_ref_unchecked() }; + /// assert_eq!(val, &5); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_access", issue = "136327")] + pub const unsafe fn as_ref_unchecked(&self) -> &T { + // SAFETY: pointer comes from `&self` so naturally satisfies ptr-to-ref invariants. + unsafe { self.get().as_ref_unchecked() } + } + + /// Get an exclusive reference to the value within the `UnsafeCell`. + /// + /// # Safety + /// + /// - It is Undefined Behavior to call this while any other + /// reference(s) to the wrapped value are alive. + /// - Mutating the wrapped value through other means while the + /// returned reference is alive is Undefined Behavior. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_access)] + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// unsafe { *uc.as_mut_unchecked() += 1; } + /// assert_eq!(uc.into_inner(), 6); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_access", issue = "136327")] + #[allow(clippy::mut_from_ref)] + pub const unsafe fn as_mut_unchecked(&self) -> &mut T { + // SAFETY: pointer comes from `&self` so naturally satisfies ptr-to-ref invariants. + unsafe { self.get().as_mut_unchecked() } + } } #[stable(feature = "unsafe_cell_default", since = "1.10.0")] diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 6a85791916a..c6c96571d33 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -8,6 +8,9 @@ use crate::{fmt, mem}; /// only immutable references can be obtained unless one has a mutable reference to the cell /// itself. In the same vein, the cell can only be re-initialized with such a mutable reference. /// +/// A `OnceCell` can be thought of as a safe abstraction over uninitialized data that becomes +/// initialized once written. +/// /// For a thread-safe version of this struct, see [`std::sync::OnceLock`]. /// /// [`RefCell`]: crate::cell::RefCell @@ -35,7 +38,7 @@ pub struct OnceCell<T> { } impl<T> OnceCell<T> { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -46,7 +49,7 @@ impl<T> OnceCell<T> { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is empty. + /// Returns `None` if the cell is uninitialized. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -56,19 +59,19 @@ impl<T> OnceCell<T> { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. + /// Returns `None` if the cell is uninitialized. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { self.inner.get_mut().as_mut() } - /// Sets the contents of the cell to `value`. + /// Initializes the contents of the cell to `value`. /// /// # Errors /// - /// This method returns `Ok(())` if the cell was empty and `Err(value)` if - /// it was full. + /// This method returns `Ok(())` if the cell was uninitialized + /// and `Err(value)` if it was already initialized. /// /// # Examples /// @@ -92,13 +95,13 @@ impl<T> OnceCell<T> { } } - /// Sets the contents of the cell to `value` if the cell was empty, then - /// returns a reference to it. + /// Initializes the contents of the cell to `value` if the cell was + /// uninitialized, then returns a reference to it. /// /// # Errors /// - /// This method returns `Ok(&value)` if the cell was empty and - /// `Err(¤t_value, value)` if it was full. + /// This method returns `Ok(&value)` if the cell was uninitialized + /// and `Err((¤t_value, value))` if it was already initialized. /// /// # Examples /// @@ -130,12 +133,12 @@ impl<T> OnceCell<T> { Ok(slot.insert(value)) } - /// Gets the contents of the cell, initializing it with `f` - /// if the cell was empty. + /// Gets the contents of the cell, initializing it to `f()` + /// if the cell was uninitialized. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. Doing @@ -164,11 +167,11 @@ impl<T> OnceCell<T> { } /// Gets the mutable reference of the contents of the cell, - /// initializing it with `f` if the cell was empty. + /// initializing it to `f()` if the cell was uninitialized. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -199,13 +202,13 @@ impl<T> OnceCell<T> { } } - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. + /// Gets the contents of the cell, initializing it to `f()` if + /// the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. Doing @@ -239,12 +242,12 @@ impl<T> OnceCell<T> { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. If the cell was empty and `f` failed, - /// an error is returned. + /// it to `f()` if the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -256,7 +259,7 @@ impl<T> OnceCell<T> { /// /// let mut cell: OnceCell<u32> = OnceCell::new(); /// - /// // Failed initializers do not change the value + /// // Failed attempts to initialize the cell do not change its contents /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// @@ -295,7 +298,7 @@ impl<T> OnceCell<T> { /// Consumes the cell, returning the wrapped value. /// - /// Returns `None` if the cell was empty. + /// Returns `None` if the cell was uninitialized. /// /// # Examples /// @@ -321,7 +324,7 @@ impl<T> OnceCell<T> { /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. + /// Has no effect and returns `None` if the `OnceCell` is uninitialized. /// /// Safety is guaranteed by requiring a mutable reference. /// diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index fb8a740aced..ccfdbf0eb70 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -92,7 +92,7 @@ impl char { #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const UNICODE_VERSION: (u8, u8, u8) = crate::unicode::UNICODE_VERSION; - /// Creates an iterator over the UTF-16 encoded code points in `iter`, + /// Creates an iterator over the native endian UTF-16 encoded code points in `iter`, /// returning unpaired surrogates as `Err`s. /// /// # Examples @@ -704,7 +704,7 @@ impl char { unsafe { from_utf8_unchecked_mut(encode_utf8_raw(self as u32, dst)) } } - /// Encodes this character as UTF-16 into the provided `u16` buffer, + /// Encodes this character as native endian UTF-16 into the provided `u16` buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// /// # Panics @@ -1828,7 +1828,7 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) } } -/// Encodes a raw `u32` value as UTF-16 into the provided `u16` buffer, +/// Encodes a raw `u32` value as native endian UTF-16 into the provided `u16` buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// /// Unlike `char::encode_utf16`, this method also handles codepoints in the surrogate range. diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index ec1aed53eaf..00300328b64 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -311,6 +311,16 @@ unsafe impl CloneToUninit for crate::ffi::CStr { } } +#[unstable(feature = "bstr", issue = "134915")] +unsafe impl CloneToUninit for crate::bstr::ByteStr { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: ByteStr is a `#[repr(transparent)]` wrapper around `[u8]` + unsafe { self.as_bytes().clone_to_uninit(dst) } + } +} + /// Implementations of `Clone` for primitive types. /// /// Implementations that cannot be described in Rust diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 97974d195fe..594236cf1d9 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -973,6 +973,24 @@ pub trait Ord: Eq + PartialOrd<Self> { /// assert_eq!(1.max(2), 2); /// assert_eq!(2.max(2), 2); /// ``` + /// ``` + /// use std::cmp::Ordering; + /// + /// #[derive(Eq)] + /// struct Equal(&'static str); + /// + /// impl PartialEq for Equal { + /// fn eq(&self, other: &Self) -> bool { true } + /// } + /// impl PartialOrd for Equal { + /// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) } + /// } + /// impl Ord for Equal { + /// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } + /// } + /// + /// assert_eq!(Equal("self").max(Equal("other")).0, "other"); + /// ``` #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] @@ -981,7 +999,7 @@ pub trait Ord: Eq + PartialOrd<Self> { where Self: Sized, { - max_by(self, other, Ord::cmp) + if other < self { self } else { other } } /// Compares and returns the minimum of two values. @@ -994,6 +1012,24 @@ pub trait Ord: Eq + PartialOrd<Self> { /// assert_eq!(1.min(2), 1); /// assert_eq!(2.min(2), 2); /// ``` + /// ``` + /// use std::cmp::Ordering; + /// + /// #[derive(Eq)] + /// struct Equal(&'static str); + /// + /// impl PartialEq for Equal { + /// fn eq(&self, other: &Self) -> bool { true } + /// } + /// impl PartialOrd for Equal { + /// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) } + /// } + /// impl Ord for Equal { + /// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } + /// } + /// + /// assert_eq!(Equal("self").min(Equal("other")).0, "self"); + /// ``` #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] @@ -1002,7 +1038,7 @@ pub trait Ord: Eq + PartialOrd<Self> { where Self: Sized, { - min_by(self, other, Ord::cmp) + if other < self { other } else { self } } /// Restrict a value to a certain interval. @@ -1414,6 +1450,24 @@ pub macro PartialOrd($item:item) { /// assert_eq!(cmp::min(1, 2), 1); /// assert_eq!(cmp::min(2, 2), 2); /// ``` +/// ``` +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::min(Equal("v1"), Equal("v2")).0, "v1"); +/// ``` #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1431,20 +1485,22 @@ pub fn min<T: Ord>(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::min_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); -/// assert_eq!(result, 1); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); /// -/// let result = cmp::min_by(-2, 3, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); -/// assert_eq!(result, -2); +/// let result = cmp::min_by(2, -1, abs_cmp); +/// assert_eq!(result, -1); +/// +/// let result = cmp::min_by(2, -3, abs_cmp); +/// assert_eq!(result, 2); +/// +/// let result = cmp::min_by(1, -1, abs_cmp); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { - match compare(&v1, &v2) { - Ordering::Less | Ordering::Equal => v1, - Ordering::Greater => v2, - } + if compare(&v2, &v1).is_lt() { v2 } else { v1 } } /// Returns the element that gives the minimum value from the specified function. @@ -1456,17 +1512,20 @@ pub fn min_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::min_by_key(-2, 1, |x: &i32| x.abs()); -/// assert_eq!(result, 1); +/// let result = cmp::min_by_key(2, -1, |x: &i32| x.abs()); +/// assert_eq!(result, -1); /// -/// let result = cmp::min_by_key(-2, 2, |x: &i32| x.abs()); -/// assert_eq!(result, -2); +/// let result = cmp::min_by_key(2, -3, |x: &i32| x.abs()); +/// assert_eq!(result, 2); +/// +/// let result = cmp::min_by_key(1, -1, |x: &i32| x.abs()); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { v2 } else { v1 } } /// Compares and returns the maximum of two values. @@ -1483,6 +1542,24 @@ pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { /// assert_eq!(cmp::max(1, 2), 2); /// assert_eq!(cmp::max(2, 2), 2); /// ``` +/// ``` +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::max(Equal("v1"), Equal("v2")).0, "v2"); +/// ``` #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1500,20 +1577,22 @@ pub fn max<T: Ord>(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::max_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); +/// +/// let result = cmp::max_by(3, -2, abs_cmp) ; +/// assert_eq!(result, 3); +/// +/// let result = cmp::max_by(1, -2, abs_cmp); /// assert_eq!(result, -2); /// -/// let result = cmp::max_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())) ; -/// assert_eq!(result, 2); +/// let result = cmp::max_by(1, -1, abs_cmp); +/// assert_eq!(result, -1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { - match compare(&v1, &v2) { - Ordering::Less | Ordering::Equal => v2, - Ordering::Greater => v1, - } + if compare(&v2, &v1).is_lt() { v1 } else { v2 } } /// Returns the element that gives the maximum value from the specified function. @@ -1525,17 +1604,20 @@ pub fn max_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::max_by_key(-2, 1, |x: &i32| x.abs()); +/// let result = cmp::max_by_key(3, -2, |x: &i32| x.abs()); +/// assert_eq!(result, 3); +/// +/// let result = cmp::max_by_key(1, -2, |x: &i32| x.abs()); /// assert_eq!(result, -2); /// -/// let result = cmp::max_by_key(-2, 2, |x: &i32| x.abs()); -/// assert_eq!(result, 2); +/// let result = cmp::max_by_key(1, -1, |x: &i32| x.abs()); +/// assert_eq!(result, -1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { v1 } else { v2 } } /// Compares and sorts two values, returning minimum and maximum. @@ -1549,13 +1631,32 @@ pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { /// use std::cmp; /// /// assert_eq!(cmp::minmax(1, 2), [1, 2]); -/// assert_eq!(cmp::minmax(2, 2), [2, 2]); +/// assert_eq!(cmp::minmax(2, 1), [1, 2]); /// /// // You can destructure the result using array patterns /// let [min, max] = cmp::minmax(42, 17); /// assert_eq!(min, 17); /// assert_eq!(max, 42); /// ``` +/// ``` +/// #![feature(cmp_minmax)] +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::minmax(Equal("v1"), Equal("v2")).map(|v| v.0), ["v1", "v2"]); +/// ``` #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] @@ -1563,7 +1664,7 @@ pub fn minmax<T>(v1: T, v2: T) -> [T; 2] where T: Ord, { - if v1 <= v2 { [v1, v2] } else { [v2, v1] } + if v2 < v1 { [v2, v1] } else { [v1, v2] } } /// Returns minimum and maximum values with respect to the specified comparison function. @@ -1576,11 +1677,14 @@ where /// #![feature(cmp_minmax)] /// use std::cmp; /// -/// assert_eq!(cmp::minmax_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [1, -2]); -/// assert_eq!(cmp::minmax_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [-2, 2]); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); +/// +/// assert_eq!(cmp::minmax_by(-2, 1, abs_cmp), [1, -2]); +/// assert_eq!(cmp::minmax_by(-1, 2, abs_cmp), [-1, 2]); +/// assert_eq!(cmp::minmax_by(-2, 2, abs_cmp), [-2, 2]); /// /// // You can destructure the result using array patterns -/// let [min, max] = cmp::minmax_by(-42, 17, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); +/// let [min, max] = cmp::minmax_by(-42, 17, abs_cmp); /// assert_eq!(min, 17); /// assert_eq!(max, -42); /// ``` @@ -1591,7 +1695,7 @@ pub fn minmax_by<T, F>(v1: T, v2: T, compare: F) -> [T; 2] where F: FnOnce(&T, &T) -> Ordering, { - if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] } + if compare(&v2, &v1).is_lt() { [v2, v1] } else { [v1, v2] } } /// Returns minimum and maximum values with respect to the specified key function. @@ -1620,7 +1724,7 @@ where F: FnMut(&T) -> K, K: Ord, { - minmax_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { [v2, v1] } else { [v1, v2] } } // Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs new file mode 100644 index 00000000000..c769e219e4d --- /dev/null +++ b/library/core/src/contracts.rs @@ -0,0 +1,21 @@ +//! Unstable module containing the unstable contracts lang items and attribute macros. +#![cfg(not(bootstrap))] + +pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires}; + +/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` +/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` +/// (including the implicit return of the tail expression, if any). +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[lang = "contract_build_check_ensures"] +#[track_caller] +pub fn build_check_ensures<Ret, C>(cond: C) -> impl (Fn(Ret) -> Ret) + Copy +where + C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static, +{ + #[track_caller] + move |ret| { + crate::intrinsics::contract_check_ensures(&ret, cond); + ret + } +} diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 7180593edf0..75338e492ee 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -731,7 +731,7 @@ const unsafe fn strlen(ptr: *const c_char) -> usize { len } else { - extern "C" { + unsafe extern "C" { /// Provided by libc or compiler_builtins. fn strlen(s: *const c_char) -> usize; } diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 79d094556c4..9bae5fd466a 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -37,173 +37,14 @@ pub use self::va_list::{VaList, VaListImpl}; )] pub mod va_list; -macro_rules! type_alias { - { - $Docfile:tt, $Alias:ident = $Real:ty; - $( $Cfg:tt )* - } => { - #[doc = include_str!($Docfile)] - $( $Cfg )* - #[stable(feature = "core_ffi_c", since = "1.64.0")] - pub type $Alias = $Real; - } -} - -type_alias! { "c_char.md", c_char = c_char_definition::c_char; #[doc(cfg(all()))] } - -type_alias! { "c_schar.md", c_schar = i8; } -type_alias! { "c_uchar.md", c_uchar = u8; } -type_alias! { "c_short.md", c_short = i16; } -type_alias! { "c_ushort.md", c_ushort = u16; } - -type_alias! { "c_int.md", c_int = c_int_definition::c_int; #[doc(cfg(all()))] } -type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint; #[doc(cfg(all()))] } - -type_alias! { "c_long.md", c_long = c_long_definition::c_long; #[doc(cfg(all()))] } -type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong; #[doc(cfg(all()))] } - -type_alias! { "c_longlong.md", c_longlong = i64; } -type_alias! { "c_ulonglong.md", c_ulonglong = u64; } - -type_alias! { "c_float.md", c_float = f32; } -type_alias! { "c_double.md", c_double = f64; } - -/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++). -/// -/// This type is currently always [`usize`], however in the future there may be -/// platforms where this is not the case. -#[unstable(feature = "c_size_t", issue = "88345")] -pub type c_size_t = usize; - -/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++). -/// -/// This type is currently always [`isize`], however in the future there may be -/// platforms where this is not the case. +mod primitives; +#[stable(feature = "core_ffi_c", since = "1.64.0")] +pub use self::primitives::{ + c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, + c_ulong, c_ulonglong, c_ushort, +}; #[unstable(feature = "c_size_t", issue = "88345")] -pub type c_ptrdiff_t = isize; - -/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type. -/// -/// This type is currently always [`isize`], however in the future there may be -/// platforms where this is not the case. -#[unstable(feature = "c_size_t", issue = "88345")] -pub type c_ssize_t = isize; - -mod c_char_definition { - cfg_if! { - // These are the targets on which c_char is unsigned. Usually the - // signedness is the same for all target_os values on a given architecture - // but there are some exceptions (see isSignedCharDefault() in clang). - // - // aarch64: - // Section 10 "Arm C and C++ language mappings" in Procedure Call Standard for the Arm® - // 64-bit Architecture (AArch64) says C/C++ char is unsigned byte. - // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs64/aapcs64.rst#arm-c-and-c-language-mappings - // arm: - // Section 8 "Arm C and C++ Language Mappings" in Procedure Call Standard for the Arm® - // Architecture says C/C++ char is unsigned byte. - // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs32/aapcs32.rst#arm-c-and-c-language-mappings - // csky: - // Section 2.1.2 "Primary Data Type" in C-SKY V2 CPU Applications Binary Interface - // Standards Manual says ANSI C char is unsigned byte. - // https://github.com/c-sky/csky-doc/blob/9f7121f7d40970ba5cc0f15716da033db2bb9d07/C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf - // Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945). - // hexagon: - // Section 3.1 "Basic data type" in Qualcomm Hexagon™ Application - // Binary Interface User Guide says "By default, the `char` data type is unsigned." - // https://docs.qualcomm.com/bundle/publicresource/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf - // msp430: - // Section 2.1 "Basic Types" in MSP430 Embedded Application Binary - // Interface says "The char type is unsigned by default". - // https://www.ti.com/lit/an/slaa534a/slaa534a.pdf - // Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945). - // powerpc/powerpc64: - // - PPC32 SysV: "Table 3-1 Scalar Types" in System V Application Binary Interface PowerPC - // Processor Supplement says ANSI C char is unsigned byte - // https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf - // - PPC64 ELFv1: Section 3.1.4 "Fundamental Types" in 64-bit PowerPC ELF Application - // Binary Interface Supplement 1.9 says ANSI C is unsigned byte - // https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUND-TYPE - // - PPC64 ELFv2: Section 2.1.2.2 "Fundamental Types" in 64-Bit ELF V2 ABI Specification - // says char is unsigned byte - // https://openpowerfoundation.org/specifications/64bitelfabi/ - // - AIX: XL C for AIX Language Reference says "By default, char behaves like an unsigned char." - // https://www.ibm.com/docs/en/xl-c-aix/13.1.3?topic=specifiers-character-types - // riscv32/riscv64: - // C/C++ type representations section in RISC-V Calling Conventions - // page in RISC-V ELF psABI Document says "char is unsigned." - // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-cc.adoc#cc-type-representations - // s390x: - // - ELF: "Table 1.1.: Scalar types" in ELF Application Binary Interface s390x Supplement - // Version 1.6.1 categorize ISO C char in unsigned integer - // https://github.com/IBM/s390x-abi/releases/tag/v1.6.1 - // - z/OS: XL C/C++ Language Reference says: "By default, char behaves like an unsigned char." - // https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-character-types - // Xtensa: - // - "The char type is unsigned by default for Xtensa processors." - // - // On the following operating systems, c_char is signed by default, regardless of architecture. - // Darwin (macOS, iOS, etc.): - // Apple targets' c_char is signed by default even on arm - // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-data-types-and-data-alignment-properly - // Windows: - // Windows MSVC C++ Language Reference says "Microsoft-specific: Variables of type char - // are promoted to int as if from type signed char by default, unless the /J compilation - // option is used." - // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types) - // L4RE: - // The kernel builds with -funsigned-char on all targets (but useserspace follows the - // architecture defaults). As we only have a target for userspace apps so there are no - // special cases for L4RE below. - if #[cfg(all( - not(windows), - not(target_vendor = "apple"), - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "csky", - target_arch = "hexagon", - target_arch = "msp430", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "riscv64", - target_arch = "riscv32", - target_arch = "s390x", - target_arch = "xtensa", - ) - ))] { - pub(super) type c_char = u8; - } else { - // On every other target, c_char is signed. - pub(super) type c_char = i8; - } - } -} - -mod c_int_definition { - cfg_if! { - if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] { - pub(super) type c_int = i16; - pub(super) type c_uint = u16; - } else { - pub(super) type c_int = i32; - pub(super) type c_uint = u32; - } - } -} - -mod c_long_definition { - cfg_if! { - if #[cfg(all(target_pointer_width = "64", not(windows)))] { - pub(super) type c_long = i64; - pub(super) type c_ulong = u64; - } else { - // The minimal size of `long` in the C standard is 32 bits - pub(super) type c_long = i32; - pub(super) type c_ulong = u32; - } - } -} +pub use self::primitives::{c_ptrdiff_t, c_size_t, c_ssize_t}; // N.B., for LLVM to recognize the void pointer type and by extension // functions like malloc(), we need to have it represented as i8* in @@ -249,4 +90,4 @@ impl fmt::Debug for c_void { cfg(not(target_feature = "crt-static")) )] #[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))] -extern "C" {} +unsafe extern "C" {} diff --git a/library/core/src/ffi/primitives.rs b/library/core/src/ffi/primitives.rs new file mode 100644 index 00000000000..ece3c7538da --- /dev/null +++ b/library/core/src/ffi/primitives.rs @@ -0,0 +1,174 @@ +//! Defines primitive types that match C's type definitions for FFI compatibility. +//! +//! This module is intentionally standalone to facilitate parsing when retrieving +//! core C types. + +macro_rules! type_alias { + { + $Docfile:tt, $Alias:ident = $Real:ty; + $( $Cfg:tt )* + } => { + #[doc = include_str!($Docfile)] + $( $Cfg )* + #[stable(feature = "core_ffi_c", since = "1.64.0")] + pub type $Alias = $Real; + } +} + +type_alias! { "c_char.md", c_char = c_char_definition::c_char; #[doc(cfg(all()))] } + +type_alias! { "c_schar.md", c_schar = i8; } +type_alias! { "c_uchar.md", c_uchar = u8; } +type_alias! { "c_short.md", c_short = i16; } +type_alias! { "c_ushort.md", c_ushort = u16; } + +type_alias! { "c_int.md", c_int = c_int_definition::c_int; #[doc(cfg(all()))] } +type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint; #[doc(cfg(all()))] } + +type_alias! { "c_long.md", c_long = c_long_definition::c_long; #[doc(cfg(all()))] } +type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong; #[doc(cfg(all()))] } + +type_alias! { "c_longlong.md", c_longlong = i64; } +type_alias! { "c_ulonglong.md", c_ulonglong = u64; } + +type_alias! { "c_float.md", c_float = f32; } +type_alias! { "c_double.md", c_double = f64; } + +mod c_char_definition { + cfg_if! { + // These are the targets on which c_char is unsigned. Usually the + // signedness is the same for all target_os values on a given architecture + // but there are some exceptions (see isSignedCharDefault() in clang). + // + // aarch64: + // Section 10 "Arm C and C++ language mappings" in Procedure Call Standard for the Arm® + // 64-bit Architecture (AArch64) says C/C++ char is unsigned byte. + // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs64/aapcs64.rst#arm-c-and-c-language-mappings + // arm: + // Section 8 "Arm C and C++ Language Mappings" in Procedure Call Standard for the Arm® + // Architecture says C/C++ char is unsigned byte. + // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs32/aapcs32.rst#arm-c-and-c-language-mappings + // csky: + // Section 2.1.2 "Primary Data Type" in C-SKY V2 CPU Applications Binary Interface + // Standards Manual says ANSI C char is unsigned byte. + // https://github.com/c-sky/csky-doc/blob/9f7121f7d40970ba5cc0f15716da033db2bb9d07/C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf + // Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945). + // hexagon: + // Section 3.1 "Basic data type" in Qualcomm Hexagon™ Application + // Binary Interface User Guide says "By default, the `char` data type is unsigned." + // https://docs.qualcomm.com/bundle/publicresource/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf + // msp430: + // Section 2.1 "Basic Types" in MSP430 Embedded Application Binary + // Interface says "The char type is unsigned by default". + // https://www.ti.com/lit/an/slaa534a/slaa534a.pdf + // powerpc/powerpc64: + // - PPC32 SysV: "Table 3-1 Scalar Types" in System V Application Binary Interface PowerPC + // Processor Supplement says ANSI C char is unsigned byte + // https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf + // - PPC64 ELFv1: Section 3.1.4 "Fundamental Types" in 64-bit PowerPC ELF Application + // Binary Interface Supplement 1.9 says ANSI C is unsigned byte + // https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUND-TYPE + // - PPC64 ELFv2: Section 2.1.2.2 "Fundamental Types" in 64-Bit ELF V2 ABI Specification + // says char is unsigned byte + // https://openpowerfoundation.org/specifications/64bitelfabi/ + // - AIX: XL C for AIX Language Reference says "By default, char behaves like an unsigned char." + // https://www.ibm.com/docs/en/xl-c-aix/13.1.3?topic=specifiers-character-types + // riscv32/riscv64: + // C/C++ type representations section in RISC-V Calling Conventions + // page in RISC-V ELF psABI Document says "char is unsigned." + // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-cc.adoc#cc-type-representations + // s390x: + // - ELF: "Table 1.1.: Scalar types" in ELF Application Binary Interface s390x Supplement + // Version 1.6.1 categorize ISO C char in unsigned integer + // https://github.com/IBM/s390x-abi/releases/tag/v1.6.1 + // - z/OS: XL C/C++ Language Reference says: "By default, char behaves like an unsigned char." + // https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-character-types + // xtensa: + // Section 2.17.1 "Data Types and Alignment" of Xtensa LX Microprocessor Overview handbook + // says "`char` type is unsigned by default". + // https://loboris.eu/ESP32/Xtensa_lx%20Overview%20handbook.pdf + // + // On the following operating systems, c_char is signed by default, regardless of architecture. + // Darwin (macOS, iOS, etc.): + // Apple targets' c_char is signed by default even on arm + // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-data-types-and-data-alignment-properly + // Windows: + // Windows MSVC C++ Language Reference says "Microsoft-specific: Variables of type char + // are promoted to int as if from type signed char by default, unless the /J compilation + // option is used." + // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types + // L4Re: + // The kernel builds with -funsigned-char on all targets (but useserspace follows the + // architecture defaults). As we only have a target for userspace apps so there are no + // special cases for L4Re below. + // https://github.com/rust-lang/rust/pull/132975#issuecomment-2484645240 + if #[cfg(all( + not(windows), + not(target_vendor = "apple"), + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "csky", + target_arch = "hexagon", + target_arch = "msp430", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "xtensa", + ) + ))] { + pub(super) type c_char = u8; + } else { + // On every other target, c_char is signed. + pub(super) type c_char = i8; + } + } +} + +mod c_long_definition { + cfg_if! { + if #[cfg(all(target_pointer_width = "64", not(windows)))] { + pub(super) type c_long = i64; + pub(super) type c_ulong = u64; + } else { + // The minimal size of `long` in the C standard is 32 bits + pub(super) type c_long = i32; + pub(super) type c_ulong = u32; + } + } +} + +/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++). +/// +/// This type is currently always [`usize`], however in the future there may be +/// platforms where this is not the case. +#[unstable(feature = "c_size_t", issue = "88345")] +pub type c_size_t = usize; + +/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++). +/// +/// This type is currently always [`isize`], however in the future there may be +/// platforms where this is not the case. +#[unstable(feature = "c_size_t", issue = "88345")] +pub type c_ptrdiff_t = isize; + +/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type. +/// +/// This type is currently always [`isize`], however in the future there may be +/// platforms where this is not the case. +#[unstable(feature = "c_size_t", issue = "88345")] +pub type c_ssize_t = isize; + +mod c_int_definition { + cfg_if! { + if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] { + pub(super) type c_int = i16; + pub(super) type c_uint = u16; + } else { + pub(super) type c_int = i32; + pub(super) type c_uint = u32; + } + } +} diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index f67c592d8d8..cceb186b31e 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -302,18 +302,28 @@ impl<'f> Drop for VaListImpl<'f> { } } -extern "rust-intrinsic" { - /// Destroy the arglist `ap` after initialization with `va_start` or - /// `va_copy`. - #[rustc_nounwind] - fn va_end(ap: &mut VaListImpl<'_>); +/// Destroy the arglist `ap` after initialization with `va_start` or +/// `va_copy`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +unsafe fn va_end(_ap: &mut VaListImpl<'_>) { + unreachable!() +} - /// Copies the current location of arglist `src` to the arglist `dst`. - #[rustc_nounwind] - fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); +/// Copies the current location of arglist `src` to the arglist `dst`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +unsafe fn va_copy<'f>(_dest: *mut VaListImpl<'f>, _src: &VaListImpl<'f>) { + unreachable!() +} - /// Loads an argument of type `T` from the `va_list` `ap` and increment the - /// argument `ap` points to. - #[rustc_nounwind] - fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T; +/// Loads an argument of type `T` from the `va_list` `ap` and increment the +/// argument `ap` points to. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +unsafe fn va_arg<T: sealed_trait::VaArgSafe>(_ap: &mut VaListImpl<'_>) -> T { + unreachable!() } diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 1862be0e86c..665b05b12ec 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1228,6 +1228,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] +#[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] pub fn from_fn<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result>(f: F) -> FromFn<F> { FromFn(f) } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index a033b8bd305..a1bf3a4d7a7 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -288,7 +288,7 @@ pub enum DebugAsHex { /// /// `FormattingOptions` is a [`Formatter`] without an attached [`Write`] trait. /// It is mainly used to construct `Formatter` instances. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[unstable(feature = "formatting_options", issue = "118117")] pub struct FormattingOptions { flags: u32, @@ -508,6 +508,15 @@ impl FormattingOptions { } } +#[unstable(feature = "formatting_options", issue = "118117")] +impl Default for FormattingOptions { + /// Same as [`FormattingOptions::new()`]. + fn default() -> Self { + // The `#[derive(Default)]` implementation would set `fill` to `\0` instead of space. + Self::new() + } +} + /// Configuration for formatting. /// /// A `Formatter` represents various options related to formatting. Users do not diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 683e45b35f7..4467b37bd45 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -192,7 +192,8 @@ macro_rules! impl_Debug { } // 2 digit decimal look up table -static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ +static DEC_DIGITS_LUT: &[u8; 200] = b"\ + 0001020304050607080910111213141516171819\ 2021222324252627282930313233343536373839\ 4041424344454647484950515253545556575859\ 6061626364656667686970717273747576777879\ @@ -232,83 +233,89 @@ macro_rules! impl_Display { #[cfg(not(feature = "optimize_for_size"))] impl $unsigned { - fn _fmt(mut self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1; - let mut buf = [MaybeUninit::<u8>::uninit(); SIZE]; - let mut curr = SIZE; - let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - - // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we - // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show - // that it's OK to copy into `buf_ptr`, notice that at the beginning - // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at - // each step this is kept the same as `n` is divided. Since `n` is always - // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` - // is safe to access. - unsafe { - // need at least 16 bits for the 4-characters-at-a-time to work. - #[allow(overflowing_literals)] - #[allow(unused_comparisons)] - // This block will be removed for smaller types at compile time and in the worst - // case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`. - if core::mem::size_of::<$unsigned>() >= 2 { - // eagerly decode 4 characters at a time - while self >= 10000 { - let rem = (self % 10000) as usize; - self /= 10000; - - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - - // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since - // otherwise `curr < 0`. But then `n` was originally at least `10000^10` - // which is `10^40 > 2^128 > n`. - ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2); - } - } - - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = self as usize; // possibly reduce 64bit math + fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; + // Buffer decimals for $unsigned with right alignment. + let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N]; + // Count the number of bytes in buf that are not initialized. + let mut offset = buf.len(); + // Consume the least-significant decimals from a working copy. + let mut remain = self; + + // Format per four digits from the lookup table. + // Four digits need a 16-bit $unsigned or wider. + while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") { + // SAFETY: All of the decimals fit in buf due to MAX_DEC_N + // and the while condition ensures at least 4 more decimals. + unsafe { core::hint::assert_unchecked(offset >= 4) } + // SAFETY: The offset counts down from its initial buf.len() + // without underflow due to the previous precondition. + unsafe { core::hint::assert_unchecked(offset <= buf.len()) } + offset -= 4; + + // pull two pairs + let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)"); + let quad = remain % scale; + remain /= scale; + let pair1 = (quad / 100) as usize; + let pair2 = (quad % 100) as usize; + buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]); + buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]); + buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]); + buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]); + } - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - } + // Format per two digits from the lookup table. + if remain > 9 { + // SAFETY: All of the decimals fit in buf due to MAX_DEC_N + // and the while condition ensures at least 2 more decimals. + unsafe { core::hint::assert_unchecked(offset >= 2) } + // SAFETY: The offset counts down from its initial buf.len() + // without underflow due to the previous precondition. + unsafe { core::hint::assert_unchecked(offset <= buf.len()) } + offset -= 2; + + let pair = (remain % 100) as usize; + remain /= 100; + buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]); + buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]); + } - // if we reach here numbers are <= 100, so at most 2 chars long - // The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough. - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - *buf_ptr.add(curr) = (n as u8) + b'0'; - } else { - let d1 = n << 1; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - } + // Format the last remaining digit, if any. + if remain != 0 || self == 0 { + // SAFETY: All of the decimals fit in buf due to MAX_DEC_N + // and the if condition ensures (at least) 1 more decimals. + unsafe { core::hint::assert_unchecked(offset >= 1) } + // SAFETY: The offset counts down from its initial buf.len() + // without underflow due to the previous precondition. + unsafe { core::hint::assert_unchecked(offset <= buf.len()) } + offset -= 1; + + // Either the compiler sees that remain < 10, or it prevents + // a boundary check up next. + let last = (remain & 15) as usize; + buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]); + // not used: remain = 0; } - // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid - // UTF-8 since `DEC_DIGITS_LUT` is - let buf_slice = unsafe { - str::from_utf8_unchecked( - slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) + // SAFETY: All buf content since offset is set. + let written = unsafe { buf.get_unchecked(offset..) }; + // SAFETY: Writes use ASCII from the lookup table exclusively. + let as_str = unsafe { + str::from_utf8_unchecked(slice::from_raw_parts( + MaybeUninit::slice_as_ptr(written), + written.len(), + )) }; - f.pad_integral(is_nonnegative, "", buf_slice) + f.pad_integral(is_nonnegative, "", as_str) } })* #[cfg(feature = "optimize_for_size")] fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const SIZE: usize = $u::MAX.ilog(10) as usize + 1; - let mut buf = [MaybeUninit::<u8>::uninit(); SIZE]; - let mut curr = buf.len(); + const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1; + let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N]; + let mut curr = MAX_DEC_N; let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning @@ -726,28 +733,9 @@ fn udiv_1e19(n: u128) -> (u128, u64) { let quot = if n < 1 << 83 { ((n >> 19) as u64 / (DIV >> 19)) as u128 } else { - u128_mulhi(n, FACTOR) >> 62 + n.widening_mul(FACTOR).1 >> 62 }; let rem = (n - quot * DIV as u128) as u64; (quot, rem) } - -/// Multiply unsigned 128 bit integers, return upper 128 bits of the result -#[inline] -fn u128_mulhi(x: u128, y: u128) -> u128 { - let x_lo = x as u64; - let x_hi = (x >> 64) as u64; - let y_lo = y as u64; - let y_hi = (y >> 64) as u64; - - // handle possibility of overflow - let carry = (x_lo as u128 * y_lo as u128) >> 64; - let m = x_lo as u128 * y_hi as u128 + carry; - let high1 = m >> 64; - - let m_lo = m as u64; - let high2 = (x_hi as u128 * y_lo as u128 + m_lo as u128) >> 64; - - x_hi as u128 * y_hi as u128 + high1 + high2 -} diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 80f6e32b6b2..e5c1a64c12e 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -468,9 +468,11 @@ pub fn spin_loop() { /// // No assumptions can be made about either operand, so the multiplication is not optimized out. /// let y = black_box(5) * black_box(10); /// ``` +/// +/// During constant evaluation, `black_box` is treated as a no-op. #[inline] #[stable(feature = "bench_black_box", since = "1.66.0")] -#[rustc_const_unstable(feature = "const_black_box", issue = "none")] +#[rustc_const_stable(feature = "const_black_box", since = "CURRENT_RUSTC_VERSION")] pub const fn black_box<T>(dummy: T) -> T { crate::intrinsics::black_box(dummy) } diff --git a/library/core/src/intrinsics/fallback.rs b/library/core/src/intrinsics/fallback.rs index 70484e4d0f2..eec5c4d646d 100644 --- a/library/core/src/intrinsics/fallback.rs +++ b/library/core/src/intrinsics/fallback.rs @@ -110,3 +110,41 @@ impl const CarryingMulAdd for i128 { (low, high) } } + +#[const_trait] +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] +pub trait DisjointBitOr: Copy + 'static { + /// See [`super::disjoint_bitor`]; we just need the trait indirection to handle + /// different types since calling intrinsics with generics doesn't work. + unsafe fn disjoint_bitor(self, other: Self) -> Self; +} +macro_rules! zero { + (bool) => { + false + }; + ($t:ident) => { + 0 + }; +} +macro_rules! impl_disjoint_bitor { + ($($t:ident,)+) => {$( + #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] + impl const DisjointBitOr for $t { + #[cfg_attr(miri, track_caller)] + #[inline] + unsafe fn disjoint_bitor(self, other: Self) -> Self { + // Note that the assume here is required for UB detection in Miri! + + // SAFETY: our precondition is that there are no bits in common, + // so this is just telling that to the backend. + unsafe { super::assume((self & other) == zero!($t)) }; + self | other + } + } + )+}; +} +impl_disjoint_bitor! { + bool, + u8, u16, u32, u64, u128, usize, + i8, i16, i32, i64, i128, isize, +} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 41b2ffad668..6c9c6d0edc2 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -78,7 +78,11 @@ pub mod simd; use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, Ordering}; #[stable(feature = "drop_in_place", since = "1.8.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead" +)] #[deprecated(note = "no longer an intrinsic - use `ptr::drop_in_place` directly", since = "1.52.0")] #[inline] pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { @@ -3244,6 +3248,26 @@ pub const fn three_way_compare<T: Copy>(_lhs: T, _rhss: T) -> crate::cmp::Orderi unimplemented!() } +/// Combine two values which have no bits in common. +/// +/// This allows the backend to implement it as `a + b` *or* `a | b`, +/// depending which is easier to implement on a specific target. +/// +/// # Safety +/// +/// Requires that `(a & b) == 0`, or equivalently that `(a | b) == (a + b)`. +/// +/// Otherwise it's immediate UB. +#[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")] +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[miri::intrinsic_fallback_is_spec] // the fallbacks all `assume` to tell Miri +pub const unsafe fn disjoint_bitor<T: ~const fallback::DisjointBitOr>(a: T, b: T) -> T { + // SAFETY: same preconditions as this function. + unsafe { fallback::DisjointBitOr::disjoint_bitor(a, b) } +} + /// Performs checked integer addition. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -3725,6 +3749,7 @@ pub const unsafe fn compare_bytes(_left: *const u8, _right: *const u8, _bytes: u #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] +#[rustc_intrinsic_const_stable_indirect] pub const fn black_box<T>(_dummy: T) -> T { unimplemented!() } @@ -4039,6 +4064,52 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) // Runtime NOP } +/// Returns whether we should perform contract-checking at runtime. +/// +/// This is meant to be similar to the ub_checks intrinsic, in terms +/// of not prematurely commiting at compile-time to whether contract +/// checking is turned on, so that we can specify contracts in libstd +/// and let an end user opt into turning them on. +#[cfg(not(bootstrap))] +#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[inline(always)] +#[rustc_intrinsic] +pub const fn contract_checks() -> bool { + // FIXME: should this be `false` or `cfg!(contract_checks)`? + + // cfg!(contract_checks) + false +} + +/// Check if the pre-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. +#[cfg(not(bootstrap))] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[lang = "contract_check_requires"] +#[rustc_intrinsic] +pub fn contract_check_requires<C: Fn() -> bool>(cond: C) { + if contract_checks() && !cond() { + // Emit no unwind panic in case this was a safety requirement. + crate::panicking::panic_nounwind("failed requires check"); + } +} + +/// Check if the post-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. +#[cfg(not(bootstrap))] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[rustc_intrinsic] +pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) { + if contract_checks() && !cond(ret) { + crate::panicking::panic_nounwind("failed ensures check"); + } +} + /// The intrinsic will return the size stored in that vtable. /// /// # Safety @@ -4784,7 +4855,7 @@ pub const unsafe fn copysignf128(_x: f128, _y: f128) -> f128 { #[cfg(miri)] #[rustc_allow_const_fn_unstable(const_eval_select)] pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize) { - extern "Rust" { + unsafe extern "Rust" { /// Miri-provided extern function to promise that a given pointer is properly aligned for /// "symbolic" alignment checks. Will fail if the pointer is not actually aligned or `align` is /// not a power of two. Has no effect when alignment checks are concrete (which is the default). diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index d03d801b936..e59d3aff379 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -2,669 +2,939 @@ //! //! In this module, a "vector" is any `repr(simd)` type. -extern "rust-intrinsic" { - /// Inserts an element into a vector, returning the updated vector. - /// - /// `T` must be a vector with element type `U`. - /// - /// # Safety - /// - /// `idx` must be in-bounds of the vector. - #[rustc_nounwind] - pub fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T; - - /// Extracts an element from a vector. - /// - /// `T` must be a vector with element type `U`. - /// - /// # Safety - /// - /// `idx` must be in-bounds of the vector. - #[rustc_nounwind] - pub fn simd_extract<T, U>(x: T, idx: u32) -> U; - - /// Adds two simd vectors elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - #[rustc_nounwind] - pub fn simd_add<T>(x: T, y: T) -> T; - - /// Subtracts `rhs` from `lhs` elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - #[rustc_nounwind] - pub fn simd_sub<T>(lhs: T, rhs: T) -> T; - - /// Multiplies two simd vectors elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - #[rustc_nounwind] - pub fn simd_mul<T>(x: T, y: T) -> T; - - /// Divides `lhs` by `rhs` elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - /// - /// # Safety - /// For integers, `rhs` must not contain any zero elements. - /// Additionally for signed integers, `<int>::MIN / -1` is undefined behavior. - #[rustc_nounwind] - pub fn simd_div<T>(lhs: T, rhs: T) -> T; - - /// Returns remainder of two vectors elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - /// - /// # Safety - /// For integers, `rhs` must not contain any zero elements. - /// Additionally for signed integers, `<int>::MIN / -1` is undefined behavior. - #[rustc_nounwind] - pub fn simd_rem<T>(lhs: T, rhs: T) -> T; - - /// Shifts vector left elementwise, with UB on overflow. - /// - /// Shifts `lhs` left by `rhs`, shifting in sign bits for signed types. - /// - /// `T` must be a vector of integer primitive types. - /// - /// # Safety - /// - /// Each element of `rhs` must be less than `<int>::BITS`. - #[rustc_nounwind] - pub fn simd_shl<T>(lhs: T, rhs: T) -> T; - - /// Shifts vector right elementwise, with UB on overflow. - /// - /// `T` must be a vector of integer primitive types. - /// - /// Shifts `lhs` right by `rhs`, shifting in sign bits for signed types. - /// - /// # Safety - /// - /// Each element of `rhs` must be less than `<int>::BITS`. - #[rustc_nounwind] - pub fn simd_shr<T>(lhs: T, rhs: T) -> T; - - /// "Ands" vectors elementwise. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_and<T>(x: T, y: T) -> T; - - /// "Ors" vectors elementwise. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_or<T>(x: T, y: T) -> T; - - /// "Exclusive ors" vectors elementwise. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_xor<T>(x: T, y: T) -> T; - - /// Numerically casts a vector, elementwise. - /// - /// `T` and `U` must be vectors of integer or floating point primitive types, and must have the - /// same length. - /// - /// When casting floats to integers, the result is truncated. Out-of-bounds result lead to UB. - /// When casting integers to floats, the result is rounded. - /// Otherwise, truncates or extends the value, maintaining the sign for signed integers. - /// - /// # Safety - /// Casting from integer types is always safe. - /// Casting between two float types is also always safe. - /// - /// Casting floats to integers truncates, following the same rules as `to_int_unchecked`. - /// Specifically, each element must: - /// * Not be `NaN` - /// * Not be infinite - /// * Be representable in the return type, after truncating off its fractional part - #[rustc_nounwind] - pub fn simd_cast<T, U>(x: T) -> U; - - /// Numerically casts a vector, elementwise. - /// - /// `T` and `U` be a vectors of integer or floating point primitive types, and must have the - /// same length. - /// - /// Like `simd_cast`, but saturates float-to-integer conversions (NaN becomes 0). - /// This matches regular `as` and is always safe. - /// - /// When casting floats to integers, the result is truncated. - /// When casting integers to floats, the result is rounded. - /// Otherwise, truncates or extends the value, maintaining the sign for signed integers. - #[rustc_nounwind] - pub fn simd_as<T, U>(x: T) -> U; - - /// Negates a vector elementwise. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// Rust panics for `-<int>::Min` due to overflow, but it is not UB with this intrinsic. - #[rustc_nounwind] - pub fn simd_neg<T>(x: T) -> T; - - /// Returns absolute value of a vector, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - #[rustc_nounwind] - pub fn simd_fabs<T>(x: T) -> T; - - /// Returns the minimum of two vectors, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// Follows IEEE-754 `minNum` semantics. - #[rustc_nounwind] - pub fn simd_fmin<T>(x: T, y: T) -> T; - - /// Returns the maximum of two vectors, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// Follows IEEE-754 `maxNum` semantics. - #[rustc_nounwind] - pub fn simd_fmax<T>(x: T, y: T) -> T; - - /// Tests elementwise equality of two vectors. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_eq<T, U>(x: T, y: T) -> U; - - /// Tests elementwise inequality equality of two vectors. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_ne<T, U>(x: T, y: T) -> U; - - /// Tests if `x` is less than `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_lt<T, U>(x: T, y: T) -> U; - - /// Tests if `x` is less than or equal to `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_le<T, U>(x: T, y: T) -> U; - - /// Tests if `x` is greater than `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_gt<T, U>(x: T, y: T) -> U; - - /// Tests if `x` is greater than or equal to `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_ge<T, U>(x: T, y: T) -> U; - - /// Shuffles two vectors by const indices. - /// - /// `T` must be a vector. - /// - /// `U` must be a **const** vector of `u32`s. This means it must either refer to a named - /// const or be given as an inline const expression (`const { ... }`). - /// - /// `V` must be a vector with the same element type as `T` and the same length as `U`. - /// - /// Returns a new vector such that element `i` is selected from `xy[idx[i]]`, where `xy` - /// is the concatenation of `x` and `y`. It is a compile-time error if `idx[i]` is out-of-bounds - /// of `xy`. - #[rustc_nounwind] - pub fn simd_shuffle<T, U, V>(x: T, y: T, idx: U) -> V; - - /// Reads a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, read the pointer. - /// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from - /// `val`. - /// - /// # Safety - /// Unmasked values in `T` must be readable as if by `<ptr>::read` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_gather<T, U, V>(val: T, ptr: U, mask: V) -> T; - - /// Writes to a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, write the - /// corresponding value in `val` to the pointer. - /// Otherwise if the corresponding value in `mask` is `0`, do nothing. - /// - /// The stores happen in left-to-right order. - /// (This is relevant in case two of the stores overlap.) - /// - /// # Safety - /// Unmasked values in `T` must be writeable as if by `<ptr>::write` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_scatter<T, U, V>(val: T, ptr: U, mask: V); - - /// Reads a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a pointer to the element type of `T` - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each element, if the corresponding value in `mask` is `!0`, read the corresponding - /// pointer offset from `ptr`. - /// The first element is loaded from `ptr`, the second from `ptr.wrapping_offset(1)` and so on. - /// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from - /// `val`. - /// - /// # Safety - /// Unmasked values in `T` must be readable as if by `<ptr>::read` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_masked_load<V, U, T>(mask: V, ptr: U, val: T) -> T; - - /// Writes to a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a pointer to the element type of `T` - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each element, if the corresponding value in `mask` is `!0`, write the corresponding - /// value in `val` to the pointer offset from `ptr`. - /// The first element is written to `ptr`, the second to `ptr.wrapping_offset(1)` and so on. - /// Otherwise if the corresponding value in `mask` is `0`, do nothing. - /// - /// # Safety - /// Unmasked values in `T` must be writeable as if by `<ptr>::write` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_masked_store<V, U, T>(mask: V, ptr: U, val: T); - - /// Adds two simd vectors elementwise, with saturation. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_saturating_add<T>(x: T, y: T) -> T; - - /// Subtracts two simd vectors elementwise, with saturation. - /// - /// `T` must be a vector of integer primitive types. - /// - /// Subtract `rhs` from `lhs`. - #[rustc_nounwind] - pub fn simd_saturating_sub<T>(lhs: T, rhs: T) -> T; - - /// Adds elements within a vector from left to right. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// Starting with the value `y`, add the elements of `x` and accumulate. - #[rustc_nounwind] - pub fn simd_reduce_add_ordered<T, U>(x: T, y: U) -> U; - - /// Adds elements within a vector in arbitrary order. May also be re-associated with - /// unordered additions on the inputs/outputs. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_add_unordered<T, U>(x: T) -> U; - - /// Multiplies elements within a vector from left to right. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// Starting with the value `y`, multiply the elements of `x` and accumulate. - #[rustc_nounwind] - pub fn simd_reduce_mul_ordered<T, U>(x: T, y: U) -> U; - - /// Multiplies elements within a vector in arbitrary order. May also be re-associated with - /// unordered additions on the inputs/outputs. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_mul_unordered<T, U>(x: T) -> U; - - /// Checks if all mask values are true. - /// - /// `T` must be a vector of integer primitive types. - /// - /// # Safety - /// `x` must contain only `0` or `!0`. - #[rustc_nounwind] - pub fn simd_reduce_all<T>(x: T) -> bool; - - /// Checks if any mask value is true. - /// - /// `T` must be a vector of integer primitive types. - /// - /// # Safety - /// `x` must contain only `0` or `!0`. - #[rustc_nounwind] - pub fn simd_reduce_any<T>(x: T) -> bool; - - /// Returns the maximum element of a vector. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// For floating-point values, uses IEEE-754 `maxNum`. - #[rustc_nounwind] - pub fn simd_reduce_max<T, U>(x: T) -> U; - - /// Returns the minimum element of a vector. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// For floating-point values, uses IEEE-754 `minNum`. - #[rustc_nounwind] - pub fn simd_reduce_min<T, U>(x: T) -> U; - - /// Logical "ands" all elements together. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_and<T, U>(x: T) -> U; - - /// Logical "ors" all elements together. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_or<T, U>(x: T) -> U; - - /// Logical "exclusive ors" all elements together. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_xor<T, U>(x: T) -> U; - - /// Truncates an integer vector to a bitmask. - /// - /// `T` must be an integer vector. - /// - /// `U` must be either the smallest unsigned integer with at least as many bits as the length - /// of `T`, or the smallest array of `u8` with at least as many bits as the length of `T`. - /// - /// Each element is truncated to a single bit and packed into the result. - /// - /// No matter whether the output is an array or an unsigned integer, it is treated as a single - /// contiguous list of bits. The bitmask is always packed on the least-significant side of the - /// output, and padded with 0s in the most-significant bits. The order of the bits depends on - /// endianness: - /// - /// * On little endian, the least significant bit corresponds to the first vector element. - /// * On big endian, the least significant bit corresponds to the last vector element. - /// - /// For example, `[!0, 0, !0, !0]` packs to - /// - `0b1101u8` or `[0b1101]` on little endian, and - /// - `0b1011u8` or `[0b1011]` on big endian. - /// - /// To consider a larger example, - /// `[!0, 0, 0, 0, 0, 0, 0, 0, !0, !0, 0, 0, 0, 0, !0, 0]` packs to - /// - `0b0100001100000001u16` or `[0b00000001, 0b01000011]` on little endian, and - /// - `0b1000000011000010u16` or `[0b10000000, 0b11000010]` on big endian. - /// - /// And finally, a non-power-of-2 example with multiple bytes: - /// `[!0, !0, 0, !0, 0, 0, !0, 0, !0, 0]` packs to - /// - `0b0101001011u16` or `[0b01001011, 0b01]` on little endian, and - /// - `0b1101001010u16` or `[0b11, 0b01001010]` on big endian. - /// - /// # Safety - /// `x` must contain only `0` and `!0`. - #[rustc_nounwind] - pub fn simd_bitmask<T, U>(x: T) -> U; - - /// Selects elements from a mask. - /// - /// `M` must be an integer vector. - /// - /// `T` must be a vector with the same number of elements as `M`. - /// - /// For each element, if the corresponding value in `mask` is `!0`, select the element from - /// `if_true`. If the corresponding value in `mask` is `0`, select the element from - /// `if_false`. - /// - /// # Safety - /// `mask` must only contain `0` and `!0`. - #[rustc_nounwind] - pub fn simd_select<M, T>(mask: M, if_true: T, if_false: T) -> T; - - /// Selects elements from a bitmask. - /// - /// `M` must be an unsigned integer or array of `u8`, matching `simd_bitmask`. - /// - /// `T` must be a vector. - /// - /// For each element, if the bit in `mask` is `1`, select the element from - /// `if_true`. If the corresponding bit in `mask` is `0`, select the element from - /// `if_false`. - /// - /// The bitmask bit order matches `simd_bitmask`. - /// - /// # Safety - /// Padding bits must be all zero. - #[rustc_nounwind] - pub fn simd_select_bitmask<M, T>(m: M, yes: T, no: T) -> T; - - /// Calculates the offset from a pointer vector elementwise, potentially - /// wrapping. - /// - /// `T` must be a vector of pointers. - /// - /// `U` must be a vector of `isize` or `usize` with the same number of elements as `T`. - /// - /// Operates as if by `<ptr>::wrapping_offset`. - #[rustc_nounwind] - pub fn simd_arith_offset<T, U>(ptr: T, offset: U) -> T; - - /// Casts a vector of pointers. - /// - /// `T` and `U` must be vectors of pointers with the same number of elements. - #[rustc_nounwind] - pub fn simd_cast_ptr<T, U>(ptr: T) -> U; - - /// Exposes a vector of pointers as a vector of addresses. - /// - /// `T` must be a vector of pointers. - /// - /// `U` must be a vector of `usize` with the same length as `T`. - #[rustc_nounwind] - pub fn simd_expose_provenance<T, U>(ptr: T) -> U; - - /// Creates a vector of pointers from a vector of addresses. - /// - /// `T` must be a vector of `usize`. - /// - /// `U` must be a vector of pointers, with the same length as `T`. - #[rustc_nounwind] - pub fn simd_with_exposed_provenance<T, U>(addr: T) -> U; - - /// Swaps bytes of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_bswap<T>(x: T) -> T; - - /// Reverses bits of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_bitreverse<T>(x: T) -> T; - - /// Counts the leading zeros of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_ctlz<T>(x: T) -> T; - - /// Counts the number of ones in each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_ctpop<T>(x: T) -> T; - - /// Counts the trailing zeros of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_cttz<T>(x: T) -> T; - - /// Rounds up each element to the next highest integer-valued float. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_ceil<T>(x: T) -> T; - - /// Rounds down each element to the next lowest integer-valued float. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_floor<T>(x: T) -> T; - - /// Rounds each element to the closest integer-valued float. - /// Ties are resolved by rounding away from 0. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_round<T>(x: T) -> T; - - /// Returns the integer part of each element as an integer-valued float. - /// In other words, non-integer values are truncated towards zero. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_trunc<T>(x: T) -> T; - - /// Takes the square root of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fsqrt<T>(x: T) -> T; - - /// Computes `(x*y) + z` for each element, but without any intermediate rounding. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fma<T>(x: T, y: T, z: T) -> T; - - /// Computes `(x*y) + z` for each element, non-deterministically executing either - /// a fused multiply-add or two operations with rounding of the intermediate result. - /// - /// The operation is fused if the code generator determines that target instruction - /// set has support for a fused operation, and that the fused operation is more efficient - /// than the equivalent, separate pair of mul and add instructions. It is unspecified - /// whether or not a fused operation is selected, and that may depend on optimization - /// level and context, for example. It may even be the case that some SIMD lanes get fused - /// and others do not. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_relaxed_fma<T>(x: T, y: T, z: T) -> T; - - // Computes the sine of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fsin<T>(a: T) -> T; - - // Computes the cosine of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fcos<T>(a: T) -> T; - - // Computes the exponential function of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fexp<T>(a: T) -> T; - - // Computes 2 raised to the power of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fexp2<T>(a: T) -> T; - - // Computes the base 10 logarithm of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_flog10<T>(a: T) -> T; - - // Computes the base 2 logarithm of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_flog2<T>(a: T) -> T; - - // Computes the natural logarithm of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_flog<T>(a: T) -> T; +/// Inserts an element into a vector, returning the updated vector. +/// +/// `T` must be a vector with element type `U`. +/// +/// # Safety +/// +/// `idx` must be in-bounds of the vector. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_insert<T, U>(_x: T, _idx: u32, _val: U) -> T { + unreachable!() +} + +/// Extracts an element from a vector. +/// +/// `T` must be a vector with element type `U`. +/// +/// # Safety +/// +/// `idx` must be in-bounds of the vector. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_extract<T, U>(_x: T, _idx: u32) -> U { + unreachable!() +} + +/// Adds two simd vectors elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_add<T>(_x: T, _y: T) -> T { + unreachable!() +} + +/// Subtracts `rhs` from `lhs` elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_sub<T>(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Multiplies two simd vectors elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_mul<T>(_x: T, _y: T) -> T { + unreachable!() +} + +/// Divides `lhs` by `rhs` elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +/// +/// # Safety +/// For integers, `rhs` must not contain any zero elements. +/// Additionally for signed integers, `<int>::MIN / -1` is undefined behavior. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_div<T>(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Returns remainder of two vectors elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +/// +/// # Safety +/// For integers, `rhs` must not contain any zero elements. +/// Additionally for signed integers, `<int>::MIN / -1` is undefined behavior. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_rem<T>(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Shifts vector left elementwise, with UB on overflow. +/// +/// Shifts `lhs` left by `rhs`, shifting in sign bits for signed types. +/// +/// `T` must be a vector of integer primitive types. +/// +/// # Safety +/// +/// Each element of `rhs` must be less than `<int>::BITS`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_shl<T>(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Shifts vector right elementwise, with UB on overflow. +/// +/// `T` must be a vector of integer primitive types. +/// +/// Shifts `lhs` right by `rhs`, shifting in sign bits for signed types. +/// +/// # Safety +/// +/// Each element of `rhs` must be less than `<int>::BITS`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_shr<T>(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// "Ands" vectors elementwise. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_and<T>(_x: T, _y: T) -> T { + unreachable!() +} + +/// "Ors" vectors elementwise. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_or<T>(_x: T, _y: T) -> T { + unreachable!() +} + +/// "Exclusive ors" vectors elementwise. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_xor<T>(_x: T, _y: T) -> T { + unreachable!() +} + +/// Numerically casts a vector, elementwise. +/// +/// `T` and `U` must be vectors of integer or floating point primitive types, and must have the +/// same length. +/// +/// When casting floats to integers, the result is truncated. Out-of-bounds result lead to UB. +/// When casting integers to floats, the result is rounded. +/// Otherwise, truncates or extends the value, maintaining the sign for signed integers. +/// +/// # Safety +/// Casting from integer types is always safe. +/// Casting between two float types is also always safe. +/// +/// Casting floats to integers truncates, following the same rules as `to_int_unchecked`. +/// Specifically, each element must: +/// * Not be `NaN` +/// * Not be infinite +/// * Be representable in the return type, after truncating off its fractional part +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_cast<T, U>(_x: T) -> U { + unreachable!() +} + +/// Numerically casts a vector, elementwise. +/// +/// `T` and `U` be a vectors of integer or floating point primitive types, and must have the +/// same length. +/// +/// Like `simd_cast`, but saturates float-to-integer conversions (NaN becomes 0). +/// This matches regular `as` and is always safe. +/// +/// When casting floats to integers, the result is truncated. +/// When casting integers to floats, the result is rounded. +/// Otherwise, truncates or extends the value, maintaining the sign for signed integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_as<T, U>(_x: T) -> U { + unreachable!() +} + +/// Negates a vector elementwise. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// Rust panics for `-<int>::Min` due to overflow, but it is not UB with this intrinsic. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_neg<T>(_x: T) -> T { + unreachable!() +} + +/// Returns absolute value of a vector, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fabs<T>(_x: T) -> T { + unreachable!() +} + +/// Returns the minimum of two vectors, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// Follows IEEE-754 `minNum` semantics. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fmin<T>(_x: T, _y: T) -> T { + unreachable!() +} + +/// Returns the maximum of two vectors, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// Follows IEEE-754 `maxNum` semantics. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fmax<T>(_x: T, _y: T) -> T { + unreachable!() +} + +/// Tests elementwise equality of two vectors. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_eq<T, U>(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests elementwise inequality equality of two vectors. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ne<T, U>(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is less than `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_lt<T, U>(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is less than or equal to `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_le<T, U>(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is greater than `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_gt<T, U>(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is greater than or equal to `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ge<T, U>(_x: T, _y: T) -> U { + unreachable!() +} + +/// Shuffles two vectors by const indices. +/// +/// `T` must be a vector. +/// +/// `U` must be a **const** vector of `u32`s. This means it must either refer to a named +/// const or be given as an inline const expression (`const { ... }`). +/// +/// `V` must be a vector with the same element type as `T` and the same length as `U`. +/// +/// Returns a new vector such that element `i` is selected from `xy[idx[i]]`, where `xy` +/// is the concatenation of `x` and `y`. It is a compile-time error if `idx[i]` is out-of-bounds +/// of `xy`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_shuffle<T, U, V>(_x: T, _y: T, _idx: U) -> V { + unreachable!() +} + +/// Reads a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, read the pointer. +/// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from +/// `val`. +/// +/// # Safety +/// Unmasked values in `T` must be readable as if by `<ptr>::read` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_gather<T, U, V>(_val: T, _ptr: U, _mask: V) -> T { + unreachable!() +} + +/// Writes to a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, write the +/// corresponding value in `val` to the pointer. +/// Otherwise if the corresponding value in `mask` is `0`, do nothing. +/// +/// The stores happen in left-to-right order. +/// (This is relevant in case two of the stores overlap.) +/// +/// # Safety +/// Unmasked values in `T` must be writeable as if by `<ptr>::write` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_scatter<T, U, V>(_val: T, _ptr: U, _mask: V) { + unreachable!() +} + +/// Reads a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a pointer to the element type of `T` +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each element, if the corresponding value in `mask` is `!0`, read the corresponding +/// pointer offset from `ptr`. +/// The first element is loaded from `ptr`, the second from `ptr.wrapping_offset(1)` and so on. +/// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from +/// `val`. +/// +/// # Safety +/// Unmasked values in `T` must be readable as if by `<ptr>::read` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_masked_load<V, U, T>(_mask: V, _ptr: U, _val: T) -> T { + unreachable!() +} + +/// Writes to a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a pointer to the element type of `T` +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each element, if the corresponding value in `mask` is `!0`, write the corresponding +/// value in `val` to the pointer offset from `ptr`. +/// The first element is written to `ptr`, the second to `ptr.wrapping_offset(1)` and so on. +/// Otherwise if the corresponding value in `mask` is `0`, do nothing. +/// +/// # Safety +/// Unmasked values in `T` must be writeable as if by `<ptr>::write` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_masked_store<V, U, T>(_mask: V, _ptr: U, _val: T) { + unreachable!() +} + +/// Adds two simd vectors elementwise, with saturation. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_saturating_add<T>(_x: T, _y: T) -> T { + unreachable!() +} + +/// Subtracts two simd vectors elementwise, with saturation. +/// +/// `T` must be a vector of integer primitive types. +/// +/// Subtract `rhs` from `lhs`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_saturating_sub<T>(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Adds elements within a vector from left to right. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// Starting with the value `y`, add the elements of `x` and accumulate. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_add_ordered<T, U>(_x: T, _y: U) -> U { + unreachable!() +} + +/// Adds elements within a vector in arbitrary order. May also be re-associated with +/// unordered additions on the inputs/outputs. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_add_unordered<T, U>(_x: T) -> U { + unreachable!() +} + +/// Multiplies elements within a vector from left to right. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// Starting with the value `y`, multiply the elements of `x` and accumulate. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_mul_ordered<T, U>(_x: T, _y: U) -> U { + unreachable!() +} + +/// Multiplies elements within a vector in arbitrary order. May also be re-associated with +/// unordered additions on the inputs/outputs. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_mul_unordered<T, U>(_x: T) -> U { + unreachable!() +} + +/// Checks if all mask values are true. +/// +/// `T` must be a vector of integer primitive types. +/// +/// # Safety +/// `x` must contain only `0` or `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_all<T>(_x: T) -> bool { + unreachable!() +} + +/// Checks if any mask value is true. +/// +/// `T` must be a vector of integer primitive types. +/// +/// # Safety +/// `x` must contain only `0` or `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_any<T>(_x: T) -> bool { + unreachable!() +} + +/// Returns the maximum element of a vector. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// For floating-point values, uses IEEE-754 `maxNum`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_max<T, U>(_x: T) -> U { + unreachable!() +} + +/// Returns the minimum element of a vector. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// For floating-point values, uses IEEE-754 `minNum`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_min<T, U>(_x: T) -> U { + unreachable!() +} + +/// Logical "ands" all elements together. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_and<T, U>(_x: T) -> U { + unreachable!() +} + +/// Logical "ors" all elements together. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_or<T, U>(_x: T) -> U { + unreachable!() +} + +/// Logical "exclusive ors" all elements together. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_xor<T, U>(_x: T) -> U { + unreachable!() +} + +/// Truncates an integer vector to a bitmask. +/// +/// `T` must be an integer vector. +/// +/// `U` must be either the smallest unsigned integer with at least as many bits as the length +/// of `T`, or the smallest array of `u8` with at least as many bits as the length of `T`. +/// +/// Each element is truncated to a single bit and packed into the result. +/// +/// No matter whether the output is an array or an unsigned integer, it is treated as a single +/// contiguous list of bits. The bitmask is always packed on the least-significant side of the +/// output, and padded with 0s in the most-significant bits. The order of the bits depends on +/// endianness: +/// +/// * On little endian, the least significant bit corresponds to the first vector element. +/// * On big endian, the least significant bit corresponds to the last vector element. +/// +/// For example, `[!0, 0, !0, !0]` packs to +/// - `0b1101u8` or `[0b1101]` on little endian, and +/// - `0b1011u8` or `[0b1011]` on big endian. +/// +/// To consider a larger example, +/// `[!0, 0, 0, 0, 0, 0, 0, 0, !0, !0, 0, 0, 0, 0, !0, 0]` packs to +/// - `0b0100001100000001u16` or `[0b00000001, 0b01000011]` on little endian, and +/// - `0b1000000011000010u16` or `[0b10000000, 0b11000010]` on big endian. +/// +/// And finally, a non-power-of-2 example with multiple bytes: +/// `[!0, !0, 0, !0, 0, 0, !0, 0, !0, 0]` packs to +/// - `0b0101001011u16` or `[0b01001011, 0b01]` on little endian, and +/// - `0b1101001010u16` or `[0b11, 0b01001010]` on big endian. +/// +/// # Safety +/// `x` must contain only `0` and `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_bitmask<T, U>(_x: T) -> U { + unreachable!() +} + +/// Selects elements from a mask. +/// +/// `M` must be an integer vector. +/// +/// `T` must be a vector with the same number of elements as `M`. +/// +/// For each element, if the corresponding value in `mask` is `!0`, select the element from +/// `if_true`. If the corresponding value in `mask` is `0`, select the element from +/// `if_false`. +/// +/// # Safety +/// `mask` must only contain `0` and `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_select<M, T>(_mask: M, _if_true: T, _if_false: T) -> T { + unreachable!() +} + +/// Selects elements from a bitmask. +/// +/// `M` must be an unsigned integer or array of `u8`, matching `simd_bitmask`. +/// +/// `T` must be a vector. +/// +/// For each element, if the bit in `mask` is `1`, select the element from +/// `if_true`. If the corresponding bit in `mask` is `0`, select the element from +/// `if_false`. +/// +/// The bitmask bit order matches `simd_bitmask`. +/// +/// # Safety +/// Padding bits must be all zero. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_select_bitmask<M, T>(_m: M, _yes: T, _no: T) -> T { + unreachable!() +} + +/// Calculates the offset from a pointer vector elementwise, potentially +/// wrapping. +/// +/// `T` must be a vector of pointers. +/// +/// `U` must be a vector of `isize` or `usize` with the same number of elements as `T`. +/// +/// Operates as if by `<ptr>::wrapping_offset`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_arith_offset<T, U>(_ptr: T, _offset: U) -> T { + unreachable!() +} + +/// Casts a vector of pointers. +/// +/// `T` and `U` must be vectors of pointers with the same number of elements. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_cast_ptr<T, U>(_ptr: T) -> U { + unreachable!() +} + +/// Exposes a vector of pointers as a vector of addresses. +/// +/// `T` must be a vector of pointers. +/// +/// `U` must be a vector of `usize` with the same length as `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_expose_provenance<T, U>(_ptr: T) -> U { + unreachable!() +} + +/// Creates a vector of pointers from a vector of addresses. +/// +/// `T` must be a vector of `usize`. +/// +/// `U` must be a vector of pointers, with the same length as `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_with_exposed_provenance<T, U>(_addr: T) -> U { + unreachable!() +} + +/// Swaps bytes of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_bswap<T>(_x: T) -> T { + unreachable!() +} + +/// Reverses bits of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_bitreverse<T>(_x: T) -> T { + unreachable!() +} + +/// Counts the leading zeros of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ctlz<T>(_x: T) -> T { + unreachable!() +} + +/// Counts the number of ones in each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ctpop<T>(_x: T) -> T { + unreachable!() +} + +/// Counts the trailing zeros of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_cttz<T>(_x: T) -> T { + unreachable!() +} + +/// Rounds up each element to the next highest integer-valued float. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ceil<T>(_x: T) -> T { + unreachable!() +} + +/// Rounds down each element to the next lowest integer-valued float. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_floor<T>(_x: T) -> T { + unreachable!() +} + +/// Rounds each element to the closest integer-valued float. +/// Ties are resolved by rounding away from 0. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_round<T>(_x: T) -> T { + unreachable!() +} + +/// Returns the integer part of each element as an integer-valued float. +/// In other words, non-integer values are truncated towards zero. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_trunc<T>(_x: T) -> T { + unreachable!() +} + +/// Takes the square root of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fsqrt<T>(_x: T) -> T { + unreachable!() +} + +/// Computes `(x*y) + z` for each element, but without any intermediate rounding. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fma<T>(_x: T, _y: T, _z: T) -> T { + unreachable!() +} + +/// Computes `(x*y) + z` for each element, non-deterministically executing either +/// a fused multiply-add or two operations with rounding of the intermediate result. +/// +/// The operation is fused if the code generator determines that target instruction +/// set has support for a fused operation, and that the fused operation is more efficient +/// than the equivalent, separate pair of mul and add instructions. It is unspecified +/// whether or not a fused operation is selected, and that may depend on optimization +/// level and context, for example. It may even be the case that some SIMD lanes get fused +/// and others do not. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_relaxed_fma<T>(_x: T, _y: T, _z: T) -> T { + unreachable!() +} + +// Computes the sine of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fsin<T>(_a: T) -> T { + unreachable!() +} + +// Computes the cosine of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fcos<T>(_a: T) -> T { + unreachable!() +} + +// Computes the exponential function of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fexp<T>(_a: T) -> T { + unreachable!() +} + +// Computes 2 raised to the power of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fexp2<T>(_a: T) -> T { + unreachable!() +} + +// Computes the base 10 logarithm of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_flog10<T>(_a: T) -> T { + unreachable!() +} + +// Computes the base 2 logarithm of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_flog2<T>(_a: T) -> T { + unreachable!() +} + +// Computes the natural logarithm of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_flog<T>(_a: T) -> T { + unreachable!() } diff --git a/library/core/src/iter/sources/from_fn.rs b/library/core/src/iter/sources/from_fn.rs index 5f3d404d7dc..1c7e1b30a2f 100644 --- a/library/core/src/iter/sources/from_fn.rs +++ b/library/core/src/iter/sources/from_fn.rs @@ -1,7 +1,7 @@ use crate::fmt; -/// Creates a new iterator where each iteration calls the provided closure -/// `F: FnMut() -> Option<T>`. +/// Creates an iterator with the provided closure +/// `F: FnMut() -> Option<T>` as its [`next`](Iterator::next) method. /// /// The iterator will yield the `T`s returned from the closure. /// diff --git a/library/core/src/iter/sources/once_with.rs b/library/core/src/iter/sources/once_with.rs index 8b31ab2ff90..c9698b4fd43 100644 --- a/library/core/src/iter/sources/once_with.rs +++ b/library/core/src/iter/sources/once_with.rs @@ -58,8 +58,8 @@ use crate::iter::{FusedIterator, TrustedLen}; /// ``` #[inline] #[stable(feature = "iter_once_with", since = "1.43.0")] -pub fn once_with<A, F: FnOnce() -> A>(gen: F) -> OnceWith<F> { - OnceWith { gen: Some(gen) } +pub fn once_with<A, F: FnOnce() -> A>(make: F) -> OnceWith<F> { + OnceWith { make: Some(make) } } /// An iterator that yields a single element of type `A` by @@ -70,13 +70,13 @@ pub fn once_with<A, F: FnOnce() -> A>(gen: F) -> OnceWith<F> { #[derive(Clone)] #[stable(feature = "iter_once_with", since = "1.43.0")] pub struct OnceWith<F> { - gen: Option<F>, + make: Option<F>, } #[stable(feature = "iter_once_with_debug", since = "1.68.0")] impl<F> fmt::Debug for OnceWith<F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.gen.is_some() { + if self.make.is_some() { f.write_str("OnceWith(Some(_))") } else { f.write_str("OnceWith(None)") @@ -90,13 +90,13 @@ impl<A, F: FnOnce() -> A> Iterator for OnceWith<F> { #[inline] fn next(&mut self) -> Option<A> { - let f = self.gen.take()?; + let f = self.make.take()?; Some(f()) } #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - self.gen.iter().size_hint() + self.make.iter().size_hint() } } @@ -110,7 +110,7 @@ impl<A, F: FnOnce() -> A> DoubleEndedIterator for OnceWith<F> { #[stable(feature = "iter_once_with", since = "1.43.0")] impl<A, F: FnOnce() -> A> ExactSizeIterator for OnceWith<F> { fn len(&self) -> usize { - self.gen.iter().len() + self.make.iter().len() } } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 91c3a4b29b5..42886e90f99 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -3493,7 +3493,8 @@ pub trait Iterator { /// /// Takes each element, adds them together, and returns the result. /// - /// An empty iterator returns the zero value of the type. + /// An empty iterator returns the *additive identity* ("zero") of the type, + /// which is `0` for integers and `-0.0` for floats. /// /// `sum()` can be used to sum any type implementing [`Sum`][`core::iter::Sum`], /// including [`Option`][`Option::sum`] and [`Result`][`Result::sum`]. @@ -3511,6 +3512,10 @@ pub trait Iterator { /// let sum: i32 = a.iter().sum(); /// /// assert_eq!(sum, 6); + /// + /// let b: Vec<f32> = vec![]; + /// let sum: f32 = b.iter().sum(); + /// assert_eq!(sum, -0.0_f32); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] fn sum<S>(self) -> S diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index f58eab3b1b1..49ce1bbcf39 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -44,7 +44,7 @@ //! called. The `lang` attribute is called `eh_personality`. // Since core defines many fundamental lang items, all tests live in a -// separate crate, libcoretest (library/core/tests), to avoid bizarre issues. +// separate crate, coretests (library/coretests), to avoid bizarre issues. // // Here we explicitly #[cfg]-out this whole crate when testing. If we don't do // this, both the generated test artifact and the linked libtest (which @@ -111,10 +111,14 @@ #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] #![feature(bigint_helper_methods)] +#![feature(bstr)] +#![feature(bstr_internals)] +#![feature(closure_track_caller)] #![feature(const_carrying_mul_add)] #![feature(const_eval_select)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] +#![feature(disjoint_bitor)] #![feature(internal_impls_macro)] #![feature(ip)] #![feature(is_ascii_octdigit)] @@ -244,6 +248,10 @@ pub mod autodiff { pub use crate::macros::builtin::autodiff; } +#[cfg(not(bootstrap))] +#[unstable(feature = "contracts", issue = "128044")] +pub mod contracts; + #[unstable(feature = "cfg_match", issue = "115585")] pub use crate::macros::cfg_match; @@ -336,6 +344,8 @@ pub mod ascii; pub mod asserting; #[unstable(feature = "async_iterator", issue = "79024")] pub mod async_iter; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod cell; pub mod char; pub mod ffi; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 5c04e5a40df..4c6fd196bd3 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -313,6 +313,17 @@ pub macro cfg_match { /// } /// } /// ``` +/// +/// If desired, it is possible to return expressions through the use of surrounding braces: +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// let _some_string = cfg_match! {{ +/// unix => { "With great power comes great electricity bills" } +/// _ => { "Behind every successful diet is an unwatched pizza" } +/// }}; +/// ``` #[cfg(not(bootstrap))] #[unstable(feature = "cfg_match", issue = "115585")] #[rustc_diagnostic_item = "cfg_match"] @@ -1766,6 +1777,32 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Attribute macro applied to a function to give it a post-condition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as a unary closure expression that is + /// invoked on a reference to the return value. + #[cfg(not(bootstrap))] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] + #[rustc_builtin_macro] + pub macro contracts_ensures($item:item) { + /* compiler built-in */ + } + + /// Attribute macro applied to a function to give it a precondition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as an boolean expression with access to the + /// function's formal parameters + #[cfg(not(bootstrap))] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] + #[rustc_builtin_macro] + pub macro contracts_requires($item:item) { + /* compiler built-in */ + } + /// Attribute macro applied to a function to register it as a handler for allocation failure. /// /// See also [`std::alloc::handle_alloc_error`](../../../std/alloc/fn.handle_alloc_error.html). @@ -1820,32 +1857,4 @@ pub(crate) mod builtin { pub macro deref($pat:pat) { builtin # deref($pat) } - - /// Derive macro for `rustc-serialize`. Should not be used in new code. - #[rustc_builtin_macro] - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] - #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. - pub macro RustcDecodable($item:item) { - /* compiler built-in */ - } - - /// Derive macro for `rustc-serialize`. Should not be used in new code. - #[rustc_builtin_macro] - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] - #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. - pub macro RustcEncodable($item:item) { - /* compiler built-in */ - } } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 01af964a83e..029c8b356d0 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -6,6 +6,13 @@ #![stable(feature = "rust1", since = "1.0.0")] +mod variance; + +#[unstable(feature = "phantom_variance_markers", issue = "135806")] +pub use self::variance::{ + PhantomContravariant, PhantomContravariantLifetime, PhantomCovariant, PhantomCovariantLifetime, + PhantomInvariant, PhantomInvariantLifetime, Variance, variance, +}; use crate::cell::UnsafeCell; use crate::cmp; use crate::fmt::Debug; @@ -1070,6 +1077,9 @@ marker_impls! { } /// A common trait implemented by all function pointers. +// +// Note that while the trait is internal and unstable it is nevertheless +// exposed as a public bound of the stable `core::ptr::fn_addr_eq` function. #[unstable( feature = "fn_ptr_trait", issue = "none", @@ -1084,10 +1094,212 @@ pub trait FnPtr: Copy + Clone { fn addr(self) -> *const (); } -/// Derive macro generating impls of traits related to smart pointers. +/// Derive macro that makes a smart pointer usable with trait objects. +/// +/// # What this macro does +/// +/// This macro is intended to be used with user-defined pointer types, and makes it possible to +/// perform coercions on the pointee of the user-defined pointer. There are two aspects to this: +/// +/// ## Unsizing coercions of the pointee +/// +/// By using the macro, the following example will compile: +/// ``` +/// #![feature(derive_coerce_pointee)] +/// use std::marker::CoercePointee; +/// use std::ops::Deref; +/// +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// struct MySmartPointer<T: ?Sized>(Box<T>); +/// +/// impl<T: ?Sized> Deref for MySmartPointer<T> { +/// type Target = T; +/// fn deref(&self) -> &T { +/// &self.0 +/// } +/// } +/// +/// trait MyTrait {} +/// +/// impl MyTrait for i32 {} +/// +/// fn main() { +/// let ptr: MySmartPointer<i32> = MySmartPointer(Box::new(4)); +/// +/// // This coercion would be an error without the derive. +/// let ptr: MySmartPointer<dyn MyTrait> = ptr; +/// } +/// ``` +/// Without the `#[derive(CoercePointee)]` macro, this example would fail with the following error: +/// ```text +/// error[E0308]: mismatched types +/// --> src/main.rs:11:44 +/// | +/// 11 | let ptr: MySmartPointer<dyn MyTrait> = ptr; +/// | --------------------------- ^^^ expected `MySmartPointer<dyn MyTrait>`, found `MySmartPointer<i32>` +/// | | +/// | expected due to this +/// | +/// = note: expected struct `MySmartPointer<dyn MyTrait>` +/// found struct `MySmartPointer<i32>` +/// = help: `i32` implements `MyTrait` so you could box the found value and coerce it to the trait object `Box<dyn MyTrait>`, you will have to change the expected type as well +/// ``` +/// +/// ## Dyn compatibility +/// +/// This macro allows you to dispatch on the user-defined pointer type. That is, traits using the +/// type as a receiver are dyn-compatible. For example, this compiles: +/// +/// ``` +/// #![feature(arbitrary_self_types, derive_coerce_pointee)] +/// use std::marker::CoercePointee; +/// use std::ops::Deref; +/// +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// struct MySmartPointer<T: ?Sized>(Box<T>); +/// +/// impl<T: ?Sized> Deref for MySmartPointer<T> { +/// type Target = T; +/// fn deref(&self) -> &T { +/// &self.0 +/// } +/// } +/// +/// // You can always define this trait. (as long as you have #![feature(arbitrary_self_types)]) +/// trait MyTrait { +/// fn func(self: MySmartPointer<Self>); +/// } +/// +/// // But using `dyn MyTrait` requires #[derive(CoercePointee)]. +/// fn call_func(value: MySmartPointer<dyn MyTrait>) { +/// value.func(); +/// } +/// ``` +/// If you remove the `#[derive(CoercePointee)]` annotation from the struct, then the above example +/// will fail with this error message: +/// ```text +/// error[E0038]: the trait `MyTrait` is not dyn compatible +/// --> src/lib.rs:21:36 +/// | +/// 17 | fn func(self: MySmartPointer<Self>); +/// | -------------------- help: consider changing method `func`'s `self` parameter to be `&self`: `&Self` +/// ... +/// 21 | fn call_func(value: MySmartPointer<dyn MyTrait>) { +/// | ^^^^^^^^^^^ `MyTrait` is not dyn compatible +/// | +/// note: for a trait to be dyn compatible it needs to allow building a vtable +/// for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> +/// --> src/lib.rs:17:19 +/// | +/// 16 | trait MyTrait { +/// | ------- this trait is not dyn compatible... +/// 17 | fn func(self: MySmartPointer<Self>); +/// | ^^^^^^^^^^^^^^^^^^^^ ...because method `func`'s `self` parameter cannot be dispatched on +/// ``` +/// +/// # Requirements for using the macro +/// +/// This macro can only be used if: +/// * The type is a `#[repr(transparent)]` struct. +/// * The type of its non-zero-sized field must either be a standard library pointer type +/// (reference, raw pointer, `NonNull`, `Box`, `Rc`, `Arc`, etc.) or another user-defined type +/// also using the `#[derive(CoercePointee)]` macro. +/// * Zero-sized fields must not mention any generic parameters unless the zero-sized field has +/// type [`PhantomData`]. +/// +/// ## Multiple type parameters +/// +/// If the type has multiple type parameters, then you must explicitly specify which one should be +/// used for dynamic dispatch. For example: +/// ``` +/// # #![feature(derive_coerce_pointee)] +/// # use std::marker::{CoercePointee, PhantomData}; +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// struct MySmartPointer<#[pointee] T: ?Sized, U> { +/// ptr: Box<T>, +/// _phantom: PhantomData<U>, +/// } +/// ``` +/// Specifying `#[pointee]` when the struct has only one type parameter is allowed, but not required. +/// +/// # Examples +/// +/// A custom implementation of the `Rc` type: +/// ``` +/// #![feature(derive_coerce_pointee)] +/// use std::marker::CoercePointee; +/// use std::ops::Deref; +/// use std::ptr::NonNull; +/// +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// pub struct Rc<T: ?Sized> { +/// inner: NonNull<RcInner<T>>, +/// } +/// +/// struct RcInner<T: ?Sized> { +/// refcount: usize, +/// value: T, +/// } +/// +/// impl<T: ?Sized> Deref for Rc<T> { +/// type Target = T; +/// fn deref(&self) -> &T { +/// let ptr = self.inner.as_ptr(); +/// unsafe { &(*ptr).value } +/// } +/// } +/// +/// impl<T> Rc<T> { +/// pub fn new(value: T) -> Self { +/// let inner = Box::new(RcInner { +/// refcount: 1, +/// value, +/// }); +/// Self { +/// inner: NonNull::from(Box::leak(inner)), +/// } +/// } +/// } +/// +/// impl<T: ?Sized> Clone for Rc<T> { +/// fn clone(&self) -> Self { +/// // A real implementation would handle overflow here. +/// unsafe { (*self.inner.as_ptr()).refcount += 1 }; +/// Self { inner: self.inner } +/// } +/// } +/// +/// impl<T: ?Sized> Drop for Rc<T> { +/// fn drop(&mut self) { +/// let ptr = self.inner.as_ptr(); +/// unsafe { (*ptr).refcount -= 1 }; +/// if unsafe { (*ptr).refcount } == 0 { +/// drop(unsafe { Box::from_raw(ptr) }); +/// } +/// } +/// } +/// ``` #[rustc_builtin_macro(CoercePointee, attributes(pointee))] -#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] +#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize, coerce_pointee_validated)] #[unstable(feature = "derive_coerce_pointee", issue = "123430")] pub macro CoercePointee($item:item) { /* compiler built-in */ } + +/// A trait that is implemented for ADTs with `derive(CoercePointee)` so that +/// the compiler can enforce the derive impls are valid post-expansion, since +/// the derive has stricter requirements than if the impls were written by hand. +/// +/// This trait is not intended to be implemented by users or used other than +/// validation, so it should never be stabilized. +#[cfg(not(bootstrap))] +#[lang = "coerce_pointee_validated"] +#[unstable(feature = "coerce_pointee_validated", issue = "none")] +#[doc(hidden)] +pub trait CoercePointeeValidated { + /* compiler built-in */ +} diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs new file mode 100644 index 00000000000..23334e6575d --- /dev/null +++ b/library/core/src/marker/variance.rs @@ -0,0 +1,260 @@ +#![unstable(feature = "phantom_variance_markers", issue = "135806")] + +use super::PhantomData; +use crate::any::type_name; +use crate::cmp::Ordering; +use crate::fmt; +use crate::hash::{Hash, Hasher}; + +macro_rules! first_token { + ($first:tt $($rest:tt)*) => { + $first + }; +} + +macro_rules! phantom_type { + ($( + $(#[$attr:meta])* + pub struct $name:ident <$t:ident> ($($inner:tt)*); + )*) => {$( + $(#[$attr])* + pub struct $name<$t>($($inner)*) where T: ?Sized; + + impl<T> $name<T> + where T: ?Sized + { + /// Constructs a new instance of the variance marker. + pub const fn new() -> Self { + Self(PhantomData) + } + } + + impl<T> self::sealed::Sealed for $name<T> where T: ?Sized { + const VALUE: Self = Self::new(); + } + impl<T> Variance for $name<T> where T: ?Sized {} + + impl<T> Default for $name<T> + where T: ?Sized + { + fn default() -> Self { + Self(PhantomData) + } + } + + impl<T> fmt::Debug for $name<T> + where T: ?Sized + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}<{}>", stringify!($name), type_name::<T>()) + } + } + + impl<T> Clone for $name<T> + where T: ?Sized + { + fn clone(&self) -> Self { + *self + } + } + + impl<T> Copy for $name<T> where T: ?Sized {} + + impl<T> PartialEq for $name<T> + where T: ?Sized + { + fn eq(&self, _: &Self) -> bool { + true + } + } + + impl<T> Eq for $name<T> where T: ?Sized {} + + impl<T> PartialOrd for $name<T> + where T: ?Sized + { + fn partial_cmp(&self, _: &Self) -> Option<Ordering> { + Some(Ordering::Equal) + } + } + + impl<T> Ord for $name<T> + where T: ?Sized + { + fn cmp(&self, _: &Self) -> Ordering { + Ordering::Equal + } + } + + impl<T> Hash for $name<T> + where T: ?Sized + { + fn hash<H: Hasher>(&self, _: &mut H) {} + } + )*}; +} + +macro_rules! phantom_lifetime { + ($( + $(#[$attr:meta])* + pub struct $name:ident <$lt:lifetime> ($($inner:tt)*); + )*) => {$( + $(#[$attr])* + #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $name<$lt>($($inner)*); + + impl $name<'_> { + /// Constructs a new instance of the variance marker. + pub const fn new() -> Self { + Self(first_token!($($inner)*)(PhantomData)) + } + } + + impl self::sealed::Sealed for $name<'_> { + const VALUE: Self = Self::new(); + } + impl Variance for $name<'_> {} + + impl fmt::Debug for $name<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", stringify!($name)) + } + } + )*}; +} + +phantom_lifetime! { + /// Zero-sized type used to mark a lifetime as covariant. + /// + /// Covariant lifetimes must live at least as long as declared. See [the reference][1] for more + /// information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::<PhantomCovariantLifetime<'a>>() == 0` + /// * `align_of::<PhantomCovariantLifetime<'a>>() == 1` + pub struct PhantomCovariantLifetime<'a>(PhantomCovariant<&'a ()>); + /// Zero-sized type used to mark a lifetime as contravariant. + /// + /// Contravariant lifetimes must live at most as long as declared. See [the reference][1] for + /// more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::<PhantomContravariantLifetime<'a>>() == 0` + /// * `align_of::<PhantomContravariantLifetime<'a>>() == 1` + pub struct PhantomContravariantLifetime<'a>(PhantomContravariant<&'a ()>); + /// Zero-sized type used to mark a lifetime as invariant. + /// + /// Invariant lifetimes must be live for the exact length declared, neither shorter nor longer. + /// See [the reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::<PhantomInvariantLifetime<'a>>() == 0` + /// * `align_of::<PhantomInvariantLifetime<'a>>() == 1` + pub struct PhantomInvariantLifetime<'a>(PhantomInvariant<&'a ()>); +} + +phantom_type! { + /// Zero-sized type used to mark a type parameter as covariant. + /// + /// Types used as part of the return value from a function are covariant. If the type is _also_ + /// passed as a parameter then it is [invariant][PhantomInvariant]. See [the reference][1] for + /// more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::<PhantomCovariant<T>>() == 0` + /// * `align_of::<PhantomCovariant<T>>() == 1` + pub struct PhantomCovariant<T>(PhantomData<fn() -> T>); + /// Zero-sized type used to mark a type parameter as contravariant. + /// + /// Types passed as arguments to a function are contravariant. If the type is _also_ part of the + /// return value from a function then it is [invariant][PhantomInvariant]. See [the + /// reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::<PhantomContravariant<T>>() == 0` + /// * `align_of::<PhantomContravariant<T>>() == 1` + pub struct PhantomContravariant<T>(PhantomData<fn(T)>); + /// Zero-sized type used to mark a type parameter as invariant. + /// + /// Types that are both passed as an argument _and_ used as part of the return value from a + /// function are invariant. See [the reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::<PhantomInvariant<T>>() == 0` + /// * `align_of::<PhantomInvariant<T>>() == 1` + pub struct PhantomInvariant<T>(PhantomData<fn(T) -> T>); +} + +mod sealed { + pub trait Sealed { + const VALUE: Self; + } +} + +/// A marker trait for phantom variance types. +pub trait Variance: sealed::Sealed + Default {} + +/// Construct a variance marker; equivalent to [`Default::default`]. +/// +/// This type can be any of the following. You generally should not need to explicitly name the +/// type, however. +/// +/// - [`PhantomCovariant`] +/// - [`PhantomContravariant`] +/// - [`PhantomInvariant`] +/// - [`PhantomCovariantLifetime`] +/// - [`PhantomContravariantLifetime`] +/// - [`PhantomInvariantLifetime`] +/// +/// # Example +/// +/// ```rust +/// #![feature(phantom_variance_markers)] +/// +/// use core::marker::{PhantomCovariant, variance}; +/// +/// struct BoundFn<F, P, R> +/// where +/// F: Fn(P) -> R, +/// { +/// function: F, +/// parameter: P, +/// return_value: PhantomCovariant<R>, +/// } +/// +/// let bound_fn = BoundFn { +/// function: core::convert::identity, +/// parameter: 5u8, +/// return_value: variance(), +/// }; +/// ``` +pub const fn variance<T>() -> T +where + T: Variance, +{ + T::VALUE +} diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index ac5307a671d..0d8c3ef906b 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -276,10 +276,9 @@ impl<T: Copy> Clone for MaybeUninit<T> { impl<T> fmt::Debug for MaybeUninit<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // NB: there is no `.pad_fmt` so we can't use a simpler `format_args!("MaybeUninit<{..}>"). - // This needs to be adjusted if `MaybeUninit` moves modules. let full_name = type_name::<Self>(); - let short_name = full_name.split_once("mem::maybe_uninit::").unwrap().1; - f.pad(short_name) + let prefix_len = full_name.find("MaybeUninit").unwrap(); + f.pad(&full_name[prefix_len..]) } } diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 8fb1588e60b..5e45974b3d4 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -670,7 +670,8 @@ impl f128 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f128)] @@ -696,7 +697,8 @@ impl f128 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f128)] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 8c2af74b8f8..e3176cd1688 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -662,7 +662,8 @@ impl f16 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f16)] @@ -687,7 +688,8 @@ impl f16 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f16)] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 817bedbd44f..4d42997369f 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -874,7 +874,8 @@ impl f32 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0f32; @@ -895,7 +896,8 @@ impl f32 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 1b0651a0def..907971d303f 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -892,7 +892,8 @@ impl f64 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0_f64; @@ -913,7 +914,8 @@ impl f64 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0_f64; diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 6c1b568e231..55f4ccd958e 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1322,20 +1322,6 @@ pub enum FpCategory { Normal, } -macro_rules! from_str_radix_int_impl { - ($($t:ty)*) => {$( - #[stable(feature = "rust1", since = "1.0.0")] - impl FromStr for $t { - type Err = ParseIntError; - #[inline] - fn from_str(src: &str) -> Result<Self, ParseIntError> { - <$t>::from_str_radix(src, 10) - } - } - )*} -} -from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } - /// Determines if a string of text of that length of that radix could be guaranteed to be /// stored in the given type T. /// Note that if the radix is known to the compiler, it is just the check of digits.len that @@ -1351,18 +1337,58 @@ pub const fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8]) #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -const fn from_str_radix_panic(radix: u32) -> ! { +const fn from_ascii_radix_panic(radix: u32) -> ! { const_panic!( - "from_str_radix_int: must lie in the range `[2, 36]`", - "from_str_radix_int: must lie in the range `[2, 36]` - found {radix}", + "from_ascii_radix: radix must lie in the range `[2, 36]`", + "from_ascii_radix: radix must lie in the range `[2, 36]` - found {radix}", radix: u32 = radix, ) } -macro_rules! from_str_radix { +macro_rules! from_str_int_impl { ($signedness:ident $($int_ty:ty)+) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl FromStr for $int_ty { + type Err = ParseIntError; + + /// Parses an integer from a string slice with decimal digits. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// use std::str::FromStr; + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str(\"+10\"), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # use std::str::FromStr; + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_str(\"1 \").is_err());")] + /// ``` + #[inline] + fn from_str(src: &str) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_str_radix(src, 10) + } + } + impl $int_ty { - /// Converts a string slice in a given base to an integer. + /// Parses an integer from a string slice with digits in a given base. /// /// The string is expected to be an optional #[doc = sign_dependent_expr!{ @@ -1375,7 +1401,7 @@ macro_rules! from_str_radix { } }] /// sign followed by only digits. Leading and trailing non-digit characters (including - /// whitespace) represent an error. Underscores (which are accepted in rust literals) + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) /// also represent an error. /// /// Digits are a subset of these characters, depending on `radix`: @@ -1401,11 +1427,92 @@ macro_rules! from_str_radix { #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] #[inline] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_ascii_radix(src.as_bytes(), radix) + } + + /// Parses an integer from an ASCII-byte slice with decimal digits. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(int_from_ascii)] + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii(b\"+10\"), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # #![feature(int_from_ascii)] + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii(b\"1 \").is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii(src: &[u8]) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_ascii_radix(src, 10) + } + + /// Parses an integer from an ASCII-byte slice with digits in a given base. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// Digits are a subset of these characters, depending on `radix`: + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(int_from_ascii)] + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_radix(b\"A\", 16), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # #![feature(int_from_ascii)] + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_radix(b\"1 \", 10).is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; if 2 > radix || radix > 36 { - from_str_radix_panic(radix); + from_ascii_radix_panic(radix); } if src.is_empty() { @@ -1415,12 +1522,6 @@ macro_rules! from_str_radix { #[allow(unused_comparisons)] let is_signed_ty = 0 > <$int_ty>::MIN; - // all valid digits are ascii, so we will just iterate over the utf8 bytes - // and cast them to chars. .to_digit() will safely return None for anything - // other than a valid ascii digit for the given radix, including the first-byte - // of multi-byte sequences - let src = src.as_bytes(); - let (is_positive, mut digits) = match src { [b'+' | b'-'] => { return Err(PIE { kind: InvalidDigit }); @@ -1496,67 +1597,8 @@ macro_rules! from_str_radix { Ok(result) } } - )+} -} - -from_str_radix! { unsigned u8 u16 u32 u64 u128 } -from_str_radix! { signed i8 i16 i32 i64 i128 } - -// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two -// identical functions. -macro_rules! from_str_radix_size_impl { - ($($signedness:ident $t:ident $size:ty),*) => {$( - impl $size { - /// Converts a string slice in a given base to an integer. - /// - /// The string is expected to be an optional - #[doc = sign_dependent_expr!{ - $signedness ? - if signed { - " `+` or `-` " - } - if unsigned { - " `+` " - } - }] - /// sign followed by only digits. Leading and trailing non-digit characters (including - /// whitespace) represent an error. Underscores (which are accepted in rust literals) - /// also represent an error. - /// - /// Digits are a subset of these characters, depending on `radix`: - /// * `0-9` - /// * `a-z` - /// * `A-Z` - /// - /// # Panics - /// - /// This function panics if `radix` is not in the range from 2 to 36. - /// - /// # Examples - /// - /// Basic usage: - /// ``` - #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] - /// ``` - /// Trailing space returns error: - /// ``` - #[doc = concat!("assert!(", stringify!($size), "::from_str_radix(\"1 \", 10).is_err());")] - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] - #[inline] - pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { - match <$t>::from_str_radix(src, radix) { - Ok(x) => Ok(x as $size), - Err(e) => Err(e), - } - } - })*} + )*} } -#[cfg(target_pointer_width = "16")] -from_str_radix_size_impl! { signed i16 isize, unsigned u16 usize } -#[cfg(target_pointer_width = "32")] -from_str_radix_size_impl! { signed i32 isize, unsigned u32 usize } -#[cfg(target_pointer_width = "64")] -from_str_radix_size_impl! { signed i64 isize, unsigned u64 usize } +from_str_int_impl! { signed isize i8 i16 i32 i64 i128 } +from_str_int_impl! { unsigned usize u8 u16 u32 u64 u128 } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index dbce64420ac..21bad6705ab 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -90,6 +90,26 @@ impl_zeroable_primitive!( /// /// assert_eq!(size_of::<Option<NonZero<u32>>>(), size_of::<u32>()); /// ``` +/// +/// # Layout +/// +/// `NonZero<T>` is guaranteed to have the same layout and bit validity as `T` +/// with the exception that the all-zero bit pattern is invalid. +/// `Option<NonZero<T>>` is guaranteed to be compatible with `T`, including in +/// FFI. +/// +/// Thanks to the [null pointer optimization], `NonZero<T>` and +/// `Option<NonZero<T>>` are guaranteed to have the same size and alignment: +/// +/// ``` +/// # use std::mem::{size_of, align_of}; +/// use std::num::NonZero; +/// +/// assert_eq!(size_of::<NonZero<u32>>(), size_of::<Option<NonZero<u32>>>()); +/// assert_eq!(align_of::<NonZero<u32>>(), align_of::<Option<NonZero<u32>>>()); +/// ``` +/// +/// [null pointer optimization]: crate::option#representation #[stable(feature = "generic_nonzero", since = "1.79.0")] #[repr(transparent)] #[rustc_nonnull_optimization_guaranteed] @@ -454,6 +474,7 @@ macro_rules! nonzero_integer { #[$stability:meta] Self = $Ty:ident, Primitive = $signedness:ident $Int:ident, + SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, // Used in doc comments. @@ -591,8 +612,6 @@ macro_rules! nonzero_integer { /// Basic usage: /// /// ``` - /// #![feature(non_zero_count_ones)] - /// /// # use std::num::NonZero; /// # /// # fn main() { test().unwrap(); } @@ -606,7 +625,8 @@ macro_rules! nonzero_integer { /// # } /// ``` /// - #[unstable(feature = "non_zero_count_ones", issue = "120287")] + #[stable(feature = "non_zero_count_ones", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "non_zero_count_ones", since = "CURRENT_RUSTC_VERSION")] #[doc(alias = "popcount")] #[doc(alias = "popcnt")] #[must_use = "this returns the result of the operation, \ @@ -885,6 +905,7 @@ macro_rules! nonzero_integer { nonzero_integer_signedness_dependent_methods! { Primitive = $signedness $Int, + SignedPrimitive = $Sint, UnsignedPrimitive = $Uint, } @@ -1108,6 +1129,7 @@ macro_rules! nonzero_integer { ( Self = $Ty:ident, Primitive = unsigned $Int:ident, + SignedPrimitive = $Sint:ident, rot = $rot:literal, rot_op = $rot_op:literal, rot_result = $rot_result:literal, @@ -1120,6 +1142,7 @@ macro_rules! nonzero_integer { #[stable(feature = "nonzero", since = "1.28.0")] Self = $Ty, Primitive = unsigned $Int, + SignedPrimitive = $Sint, UnsignedPrimitive = $Int, rot = $rot, rot_op = $rot_op, @@ -1134,7 +1157,7 @@ macro_rules! nonzero_integer { ( Self = $Ty:ident, Primitive = signed $Int:ident, - UnsignedPrimitive = $UInt:ident, + UnsignedPrimitive = $Uint:ident, rot = $rot:literal, rot_op = $rot_op:literal, rot_result = $rot_result:literal, @@ -1146,7 +1169,8 @@ macro_rules! nonzero_integer { #[stable(feature = "signed_nonzero", since = "1.34.0")] Self = $Ty, Primitive = signed $Int, - UnsignedPrimitive = $UInt, + SignedPrimitive = $Int, + UnsignedPrimitive = $Uint, rot = $rot, rot_op = $rot_op, rot_result = $rot_result, @@ -1165,8 +1189,12 @@ macro_rules! nonzero_integer_signedness_dependent_impls { impl Div<NonZero<$Int>> for $Int { type Output = $Int; + /// Same as `self / other.get()`, but because `other` is a `NonZero<_>`, + /// there's never a runtime check for division-by-zero. + /// /// This operation rounds towards zero, truncating any fractional /// part of the exact result, and cannot panic. + #[doc(alias = "unchecked_div")] #[inline] fn div(self, other: NonZero<$Int>) -> $Int { // SAFETY: Division by zero is checked because `other` is non-zero, @@ -1177,6 +1205,9 @@ macro_rules! nonzero_integer_signedness_dependent_impls { #[stable(feature = "nonzero_div_assign", since = "1.79.0")] impl DivAssign<NonZero<$Int>> for $Int { + /// Same as `self /= other.get()`, but because `other` is a `NonZero<_>`, + /// there's never a runtime check for division-by-zero. + /// /// This operation rounds towards zero, truncating any fractional /// part of the exact result, and cannot panic. #[inline] @@ -1259,6 +1290,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // Associated items for unsigned nonzero types only. ( Primitive = unsigned $Int:ident, + SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, ) => { /// The smallest value that can be represented by this non-zero @@ -1593,11 +1625,35 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // results will be sqrt(1), which is 1, so a result can't be zero. unsafe { Self::new_unchecked(result) } } + + /// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// # use std::num::NonZero; + /// + #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::MAX;")] + /// + #[doc = concat!("assert_eq!(n.cast_signed(), NonZero::new(-1", stringify!($Sint), ").unwrap());")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_signed(self) -> NonZero<$Sint> { + // SAFETY: `self.get()` can't be zero + unsafe { NonZero::new_unchecked(self.get().cast_signed()) } + } }; // Associated items for signed nonzero types only. ( Primitive = signed $Int:ident, + SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, ) => { /// The smallest value that can be represented by this non-zero @@ -2008,12 +2064,37 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // SAFETY: negation of nonzero cannot yield zero values. unsafe { Self::new_unchecked(result) } } + + /// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// # use std::num::NonZero; + /// + #[doc = concat!("let n = NonZero::new(-1", stringify!($Int), ").unwrap();")] + /// + #[doc = concat!("assert_eq!(n.cast_unsigned(), NonZero::<", stringify!($Uint), ">::MAX);")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_unsigned(self) -> NonZero<$Uint> { + // SAFETY: `self.get()` can't be zero + unsafe { NonZero::new_unchecked(self.get().cast_unsigned()) } + } + }; } nonzero_integer! { Self = NonZeroU8, Primitive = unsigned u8, + SignedPrimitive = i8, rot = 2, rot_op = "0x82", rot_result = "0xa", @@ -2025,6 +2106,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU16, Primitive = unsigned u16, + SignedPrimitive = i16, rot = 4, rot_op = "0xa003", rot_result = "0x3a", @@ -2036,6 +2118,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU32, Primitive = unsigned u32, + SignedPrimitive = i32, rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", @@ -2047,6 +2130,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU64, Primitive = unsigned u64, + SignedPrimitive = i64, rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", @@ -2058,6 +2142,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU128, Primitive = unsigned u128, + SignedPrimitive = i128, rot = 16, rot_op = "0x13f40000000000000000000000004f76", rot_result = "0x4f7613f4", @@ -2070,6 +2155,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + SignedPrimitive = isize, rot = 4, rot_op = "0xa003", rot_result = "0x3a", @@ -2082,6 +2168,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + SignedPrimitive = isize, rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", @@ -2094,6 +2181,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + SignedPrimitive = isize, rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index c8433b3bb16..29f6791ee6a 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1187,6 +1187,50 @@ macro_rules! uint_impl { self % rhs } + /// Same value as `self | other`, but UB if any bit position is set in both inputs. + /// + /// This is a situational micro-optimization for places where you'd rather + /// use addition on some platforms and bitwise or on other platforms, based + /// on exactly which instructions combine better with whatever else you're + /// doing. Note that there's no reason to bother using this for places + /// where it's clear from the operations involved that they can't overlap. + /// For example, if you're combining `u16`s into a `u32` with + /// `((a as u32) << 16) | (b as u32)`, that's fine, as the backend will + /// know those sides of the `|` are disjoint without needing help. + /// + /// # Examples + /// + /// ``` + /// #![feature(disjoint_bitor)] + /// + /// // SAFETY: `1` and `4` have no bits in common. + /// unsafe { + #[doc = concat!(" assert_eq!(1_", stringify!($SelfT), ".unchecked_disjoint_bitor(4), 5);")] + /// } + /// ``` + /// + /// # Safety + /// + /// Requires that `(self & other) == 0`, otherwise it's immediate UB. + /// + /// Equivalently, requires that `(self | other) == (self + other)`. + #[unstable(feature = "disjoint_bitor", issue = "135758")] + #[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")] + #[inline] + pub const unsafe fn unchecked_disjoint_bitor(self, other: Self) -> Self { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_disjoint_bitor cannot have overlapping bits"), + ( + lhs: $SelfT = self, + rhs: $SelfT = other, + ) => (lhs & rhs) == 0, + ); + + // SAFETY: Same precondition + unsafe { intrinsics::disjoint_bitor(self, other) } + } + /// Returns the logarithm of the number with respect to an arbitrary base, /// rounded down. /// @@ -2346,15 +2390,22 @@ macro_rules! uint_impl { /// assert_eq!((sum1, sum0), (9, 6)); /// ``` #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) { // note: longer-term this should be done via an intrinsic, but this has been shown // to generate optimal code for now, and LLVM doesn't have an equivalent intrinsic - let (a, b) = self.overflowing_add(rhs); - let (c, d) = a.overflowing_add(carry as $SelfT); - (c, b | d) + let (a, c1) = self.overflowing_add(rhs); + let (b, c2) = a.overflowing_add(carry as $SelfT); + // Ideally LLVM would know this is disjoint without us telling them, + // but it doesn't <https://github.com/llvm/llvm-project/issues/118162> + // SAFETY: Only one of `c1` and `c2` can be set. + // For c1 to be set we need to have overflowed, but if we did then + // `a` is at most `MAX-1`, which means that `c2` cannot possibly + // overflow because it's adding at most `1` (since it came from `bool`) + (b, unsafe { intrinsics::disjoint_bitor(c1, c2) }) } /// Calculates `self` + `rhs` with a signed `rhs`. diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs index c90ae7babbd..6be42ca7d32 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -6,7 +6,6 @@ use crate::marker::Tuple; /// All `async fn` and functions returning futures implement this trait. #[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] -#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn"] pub trait AsyncFn<Args: Tuple>: AsyncFnMut<Args> { @@ -20,7 +19,6 @@ pub trait AsyncFn<Args: Tuple>: AsyncFnMut<Args> { /// All `async fn` and functions returning futures implement this trait. #[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] -#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn_mut"] pub trait AsyncFnMut<Args: Tuple>: AsyncFnOnce<Args> { @@ -41,7 +39,6 @@ pub trait AsyncFnMut<Args: Tuple>: AsyncFnOnce<Args> { /// All `async fn` and functions returning futures implement this trait. #[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] -#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn_once"] pub trait AsyncFnOnce<Args: Tuple> { diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 40526f9583e..7b2ced2cc4b 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -182,10 +182,10 @@ pub use self::function::{Fn, FnMut, FnOnce}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::index::{Index, IndexMut}; pub(crate) use self::index_range::IndexRange; -#[unstable(feature = "one_sided_range", issue = "69780")] -pub use self::range::OneSidedRange; #[stable(feature = "inclusive_range", since = "1.26.0")] pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; +#[unstable(feature = "one_sided_range", issue = "69780")] +pub use self::range::{OneSidedRange, OneSidedRangeBound}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 727a22e454d..42e07a0e51d 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -979,6 +979,19 @@ impl<T> RangeBounds<T> for RangeToInclusive<&T> { } } +/// An internal helper for `split_off` functions indicating +/// which end a `OneSidedRange` is bounded on. +#[unstable(feature = "one_sided_range", issue = "69780")] +#[allow(missing_debug_implementations)] +pub enum OneSidedRangeBound { + /// The range is bounded inclusively from below and is unbounded above. + StartInclusive, + /// The range is bounded exclusively from above and is unbounded below. + End, + /// The range is bounded inclusively from above and is unbounded below. + EndInclusive, +} + /// `OneSidedRange` is implemented for built-in range types that are unbounded /// on one side. For example, `a..`, `..b` and `..=c` implement `OneSidedRange`, /// but `..`, `d..e`, and `f..=g` do not. @@ -986,13 +999,38 @@ impl<T> RangeBounds<T> for RangeToInclusive<&T> { /// Types that implement `OneSidedRange<T>` must return `Bound::Unbounded` /// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`. #[unstable(feature = "one_sided_range", issue = "69780")] -pub trait OneSidedRange<T: ?Sized>: RangeBounds<T> {} +pub trait OneSidedRange<T: ?Sized>: RangeBounds<T> { + /// An internal-only helper function for `split_off` and + /// `split_off_mut` that returns the bound of the one-sided range. + fn bound(self) -> (OneSidedRangeBound, T); +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl<T> OneSidedRange<T> for RangeTo<T> where Self: RangeBounds<T> {} +impl<T> OneSidedRange<T> for RangeTo<T> +where + Self: RangeBounds<T>, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::End, self.end) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl<T> OneSidedRange<T> for RangeFrom<T> where Self: RangeBounds<T> {} +impl<T> OneSidedRange<T> for RangeFrom<T> +where + Self: RangeBounds<T>, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::StartInclusive, self.start) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl<T> OneSidedRange<T> for RangeToInclusive<T> where Self: RangeBounds<T> {} +impl<T> OneSidedRange<T> for RangeToInclusive<T> +where + Self: RangeBounds<T>, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::EndInclusive, self.end) + } +} diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 53e2b238bae..d36e677d21a 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -59,7 +59,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call // that gets resolved to the `#[panic_handler]` function. - extern "Rust" { + unsafe extern "Rust" { #[lang = "panic_impl"] fn panic_impl(pi: &PanicInfo<'_>) -> !; } @@ -100,7 +100,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call // that gets resolved to the `#[panic_handler]` function. - extern "Rust" { + unsafe extern "Rust" { #[lang = "panic_impl"] fn panic_impl(pi: &PanicInfo<'_>) -> !; } @@ -291,6 +291,22 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { ) } +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[track_caller] +#[cfg_attr(not(bootstrap), lang = "panic_null_pointer_dereference")] // needed by codegen for panic on null pointer deref +#[rustc_nounwind] // `CheckNull` MIR pass requires this function to never unwind +fn panic_null_pointer_dereference() -> ! { + if cfg!(feature = "panic_immediate_abort") { + super::intrinsics::abort() + } + + panic_nounwind_fmt( + format_args!("null pointer dereference occurred"), + /* force_no_backtrace */ false, + ) +} + /// Panics because we cannot unwind out of a function. /// /// This is a separate function to avoid the codesize impact of each crate containing the string to diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 71044190f0c..2a0bf89fcf7 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -156,8 +156,8 @@ //! //! In order to implement the second option, we must in some way enforce its key invariant, //! *i.e.* prevent the value from being *moved* or otherwise invalidated (you may notice this -//! sounds an awful lot like the definition of *pinning* a value). There a few ways one might be -//! able to enforce this invariant in Rust: +//! sounds an awful lot like the definition of *pinning* a value). There are a few ways one might +//! be able to enforce this invariant in Rust: //! //! 1. Offer a wholly `unsafe` API to interact with the object, thus requiring every caller to //! uphold the invariant themselves diff --git a/library/core/src/prelude/common.rs b/library/core/src/prelude/common.rs index e38ef1e147c..8b116cecb52 100644 --- a/library/core/src/prelude/common.rs +++ b/library/core/src/prelude/common.rs @@ -12,6 +12,9 @@ pub use crate::marker::{Copy, Send, Sized, Sync, Unpin}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; +#[stable(feature = "async_closure", since = "1.85.0")] +#[doc(no_inline)] +pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; // Re-exported functions #[stable(feature = "core_prelude", since = "1.4.0")] diff --git a/library/core/src/prelude/mod.rs b/library/core/src/prelude/mod.rs index d3fda1cd273..0ab97f5bbd5 100644 --- a/library/core/src/prelude/mod.rs +++ b/library/core/src/prelude/mod.rs @@ -18,16 +18,6 @@ mod common; pub mod v1 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - - // Do not `doc(inline)` these `doc(hidden)` items. - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[allow(deprecated)] - pub use crate::macros::builtin::{RustcDecodable, RustcEncodable}; } /// The 2015 version of the core prelude. diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index c5f029363e5..bbf5939fe1b 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1160,9 +1160,9 @@ impl<T> (T,) {} /// /// Note that most common platforms will not support `f16` in hardware without enabling extra target /// features, with the notable exception of Apple Silicon (also known as M1, M2, etc.) processors. -/// Hardware support on x86-64 requires the avx512fp16 feature, while RISC-V requires Zhf. -/// Usually the fallback implementation will be to use `f32` hardware if it exists, and convert -/// between `f16` and `f32` when performing math. +/// Hardware support on x86/x86-64 requires the avx512fp16 or avx10.1 features, while RISC-V requires +/// Zfh, and Arm/AArch64 requires FEAT_FP16. Usually the fallback implementation will be to use `f32` +/// hardware if it exists, and convert between `f16` and `f32` when performing math. /// /// *[See also the `std::f16::consts` module](crate::f16::consts).* /// @@ -1344,10 +1344,10 @@ mod prim_f64 {} /// quad-precision values][wikipedia] for more information. /// /// Note that no platforms have hardware support for `f128` without enabling target specific features, -/// as for all instruction set architectures `f128` is considered an optional feature. -/// Only Power ISA ("PowerPC") and RISC-V specify it, and only certain microarchitectures -/// actually implement it. For x86-64 and AArch64, ISA support is not even specified, -/// so it will always be a software implementation significantly slower than `f64`. +/// as for all instruction set architectures `f128` is considered an optional feature. Only Power ISA +/// ("PowerPC") and RISC-V (via the Q extension) specify it, and only certain microarchitectures +/// actually implement it. For x86-64 and AArch64, ISA support is not even specified, so it will always +/// be a software implementation significantly slower than `f64`. /// /// _Note: `f128` support is incomplete. Many platforms will not be able to link math functions. On /// x86 in particular, these functions do link but their results are always incorrect._ diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index ec569291853..0c6eaf60d04 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1681,7 +1681,7 @@ impl<T, const N: usize> *const [T; N] { } } -// Equality for pointers +/// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> PartialEq for *const T { #[inline] @@ -1691,10 +1691,11 @@ impl<T: ?Sized> PartialEq for *const T { } } +/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> Eq for *const T {} -// Comparison for pointers +/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> Ord for *const T { #[inline] @@ -1710,6 +1711,7 @@ impl<T: ?Sized> Ord for *const T { } } +/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> PartialOrd for *const T { #[inline] diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index e93b5658e24..9eee29d485f 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -155,7 +155,7 @@ pub struct DynMetadata<Dyn: ?Sized> { _phantom: crate::marker::PhantomData<Dyn>, } -extern "C" { +unsafe extern "C" { /// Opaque type for accessing vtables. /// /// Private implementation detail of `DynMetadata::size_of` etc. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5d9d337f101..d1b0104c0fa 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -2097,7 +2097,7 @@ impl<T, const N: usize> *mut [T; N] { } } -// Equality for pointers +/// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> PartialEq for *mut T { #[inline(always)] @@ -2107,9 +2107,11 @@ impl<T: ?Sized> PartialEq for *mut T { } } +/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> Eq for *mut T {} +/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> Ord for *mut T { #[inline] @@ -2125,6 +2127,7 @@ impl<T: ?Sized> Ord for *mut T { } } +/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> PartialOrd for *mut T { #[inline(always)] diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 427526fd14b..6a62928873f 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -48,6 +48,7 @@ pub use crate::ops::{Bound, OneSidedRange, RangeBounds, RangeFull, RangeTo, Rang /// assert_eq!(Range::from(3..5), Range { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, Range::from(3..6).into_iter().sum()); /// ``` +#[cfg_attr(not(bootstrap), lang = "RangeCopy")] #[derive(Clone, Copy, Default, PartialEq, Eq, Hash)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct Range<Idx> { @@ -205,6 +206,7 @@ impl<T> From<legacy::Range<T>> for Range<T> { /// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, RangeInclusive::from(3..=5).into_iter().sum()); /// ``` +#[cfg_attr(not(bootstrap), lang = "RangeInclusiveCopy")] #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct RangeInclusive<Idx> { @@ -388,6 +390,7 @@ impl<T> From<legacy::RangeInclusive<T>> for RangeInclusive<T> { /// assert_eq!(RangeFrom::from(2..), core::range::RangeFrom { start: 2 }); /// assert_eq!(2 + 3 + 4, RangeFrom::from(2..).into_iter().take(3).sum()); /// ``` +#[cfg_attr(not(bootstrap), lang = "RangeFromCopy")] #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct RangeFrom<Idx> { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index ba5746d0ade..fe9d7c10db2 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -10,7 +10,7 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics::{exact_div, unchecked_sub}; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; -use crate::ops::{Bound, OneSidedRange, Range, RangeBounds, RangeInclusive}; +use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive}; use crate::panic::const_panic; use crate::simd::{self, Simd}; use crate::ub_checks::assert_unsafe_precondition; @@ -83,14 +83,12 @@ pub use raw::{from_raw_parts, from_raw_parts_mut}; /// which to split. Returns `None` if the split index would overflow. #[inline] fn split_point_of(range: impl OneSidedRange<usize>) -> Option<(Direction, usize)> { - use Bound::*; - - Some(match (range.start_bound(), range.end_bound()) { - (Unbounded, Excluded(i)) => (Direction::Front, *i), - (Unbounded, Included(i)) => (Direction::Front, i.checked_add(1)?), - (Excluded(i), Unbounded) => (Direction::Back, i.checked_add(1)?), - (Included(i), Unbounded) => (Direction::Back, *i), - _ => unreachable!(), + use OneSidedRangeBound::{End, EndInclusive, StartInclusive}; + + Some(match range.bound() { + (StartInclusive, i) => (Direction::Back, i), + (End, i) => (Direction::Front, i), + (EndInclusive, i) => (Direction::Front, i.checked_add(1)?), }) } @@ -1099,10 +1097,15 @@ impl<T> [T] { /// assert!(iter.next().is_none()); /// ``` /// - /// There's no `windows_mut`, as that existing would let safe code violate the - /// "only one `&mut` at a time to the same thing" rule. However, you can sometimes - /// use [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in - /// conjunction with `windows` to accomplish something similar: + /// Because the [Iterator] trait cannot represent the required lifetimes, + /// there is no `windows_mut` analog to `windows`; + /// `[0,1,2].windows_mut(2).collect()` would violate [the rules of references] + /// (though a [LendingIterator] analog is possible). You can sometimes use + /// [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in + /// conjunction with `windows` instead: + /// + /// [the rules of references]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references + /// [LendingIterator]: https://blog.rust-lang.org/2022/10/28/gats-stabilization.html /// ``` /// use std::cell::Cell; /// @@ -4289,25 +4292,25 @@ impl<T> [T] { /// /// # Examples /// - /// Taking the first three elements of a slice: + /// Splitting off the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut first_three = slice.take(..3).unwrap(); + /// let mut first_three = slice.split_off(..3).unwrap(); /// /// assert_eq!(slice, &['d']); /// assert_eq!(first_three, &['a', 'b', 'c']); /// ``` /// - /// Taking the last two elements of a slice: + /// Splitting off the last two elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut tail = slice.take(2..).unwrap(); + /// let mut tail = slice.split_off(2..).unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(tail, &['c', 'd']); @@ -4320,16 +4323,19 @@ impl<T> [T] { /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.take(5..)); - /// assert_eq!(None, slice.take(..5)); - /// assert_eq!(None, slice.take(..=4)); + /// assert_eq!(None, slice.split_off(5..)); + /// assert_eq!(None, slice.split_off(..5)); + /// assert_eq!(None, slice.split_off(..=4)); /// let expected: &[char] = &['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.take(..4)); + /// assert_eq!(Some(expected), slice.split_off(..4)); /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take<'a, R: OneSidedRange<usize>>(self: &mut &'a Self, range: R) -> Option<&'a Self> { + pub fn split_off<'a, R: OneSidedRange<usize>>( + self: &mut &'a Self, + range: R, + ) -> Option<&'a Self> { let (direction, split_index) = split_point_of(range)?; if split_index > self.len() { return None; @@ -4358,13 +4364,13 @@ impl<T> [T] { /// /// # Examples /// - /// Taking the first three elements of a slice: + /// Splitting off the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut first_three = slice.take_mut(..3).unwrap(); + /// let mut first_three = slice.split_off_mut(..3).unwrap(); /// /// assert_eq!(slice, &mut ['d']); /// assert_eq!(first_three, &mut ['a', 'b', 'c']); @@ -4376,7 +4382,7 @@ impl<T> [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut tail = slice.take_mut(2..).unwrap(); + /// let mut tail = slice.split_off_mut(2..).unwrap(); /// /// assert_eq!(slice, &mut ['a', 'b']); /// assert_eq!(tail, &mut ['c', 'd']); @@ -4389,16 +4395,16 @@ impl<T> [T] { /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.take_mut(5..)); - /// assert_eq!(None, slice.take_mut(..5)); - /// assert_eq!(None, slice.take_mut(..=4)); + /// assert_eq!(None, slice.split_off_mut(5..)); + /// assert_eq!(None, slice.split_off_mut(..5)); + /// assert_eq!(None, slice.split_off_mut(..=4)); /// let expected: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.take_mut(..4)); + /// assert_eq!(Some(expected), slice.split_off_mut(..4)); /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_mut<'a, R: OneSidedRange<usize>>( + pub fn split_off_mut<'a, R: OneSidedRange<usize>>( self: &mut &'a mut Self, range: R, ) -> Option<&'a mut Self> { @@ -4430,14 +4436,14 @@ impl<T> [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let first = slice.take_first().unwrap(); + /// let first = slice.split_off_first().unwrap(); /// /// assert_eq!(slice, &['b', 'c']); /// assert_eq!(first, &'a'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_first<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn split_off_first<'a>(self: &mut &'a Self) -> Option<&'a T> { let (first, rem) = self.split_first()?; *self = rem; Some(first) @@ -4454,7 +4460,7 @@ impl<T> [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let first = slice.take_first_mut().unwrap(); + /// let first = slice.split_off_first_mut().unwrap(); /// *first = 'd'; /// /// assert_eq!(slice, &['b', 'c']); @@ -4462,7 +4468,7 @@ impl<T> [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn split_off_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { let (first, rem) = mem::take(self).split_first_mut()?; *self = rem; Some(first) @@ -4479,14 +4485,14 @@ impl<T> [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let last = slice.take_last().unwrap(); + /// let last = slice.split_off_last().unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(last, &'c'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_last<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn split_off_last<'a>(self: &mut &'a Self) -> Option<&'a T> { let (last, rem) = self.split_last()?; *self = rem; Some(last) @@ -4503,7 +4509,7 @@ impl<T> [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let last = slice.take_last_mut().unwrap(); + /// let last = slice.split_off_last_mut().unwrap(); /// *last = 'd'; /// /// assert_eq!(slice, &['a', 'b']); @@ -4511,7 +4517,7 @@ impl<T> [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn split_off_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { let (last, rem) = mem::take(self).split_last_mut()?; *self = rem; Some(last) diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index d8e0acb565c..5d5ee4c7b62 100644 --- a/library/core/src/slice/rotate.rs +++ b/library/core/src/slice/rotate.rs @@ -1,6 +1,8 @@ use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::{cmp, ptr}; +type BufType = [usize; 32]; + /// Rotates the range `[mid-left, mid+right)` such that the element at `mid` becomes the first /// element. Equivalently, rotates the range `left` elements to the left or `right` elements to the /// right. @@ -8,14 +10,82 @@ use crate::{cmp, ptr}; /// # Safety /// /// The specified range must be valid for reading and writing. +#[inline] +pub(super) unsafe fn ptr_rotate<T>(left: usize, mid: *mut T, right: usize) { + if T::IS_ZST { + return; + } + // abort early if the rotate is a no-op + if (left == 0) || (right == 0) { + return; + } + // `T` is not a zero-sized type, so it's okay to divide by its size. + if !cfg!(feature = "optimize_for_size") + && cmp::min(left, right) <= mem::size_of::<BufType>() / mem::size_of::<T>() + { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_memmove(left, mid, right) }; + } else if !cfg!(feature = "optimize_for_size") + && ((left + right < 24) || (mem::size_of::<T>() > mem::size_of::<[usize; 4]>())) + { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_gcd(left, mid, right) } + } else { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_swap(left, mid, right) } + } +} + +/// Algorithm 1 is used if `min(left, right)` is small enough to fit onto a stack buffer. The +/// `min(left, right)` elements are copied onto the buffer, `memmove` is applied to the others, and +/// the ones on the buffer are moved back into the hole on the opposite side of where they +/// originated. /// -/// # Algorithm +/// # Safety /// -/// Algorithm 1 is used for small values of `left + right` or for large `T`. The elements are moved -/// into their final positions one at a time starting at `mid - left` and advancing by `right` steps -/// modulo `left + right`, such that only one temporary is needed. Eventually, we arrive back at -/// `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps skipped over -/// elements. For example: +/// The specified range must be valid for reading and writing. +#[inline] +unsafe fn ptr_rotate_memmove<T>(left: usize, mid: *mut T, right: usize) { + // The `[T; 0]` here is to ensure this is appropriately aligned for T + let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); + let buf = rawarray.as_mut_ptr() as *mut T; + // SAFETY: `mid-left <= mid-left+right < mid+right` + let dim = unsafe { mid.sub(left).add(right) }; + if left <= right { + // SAFETY: + // + // 1) The `if` condition about the sizes ensures `[mid-left; left]` will fit in + // `buf` without overflow and `buf` was created just above and so cannot be + // overlapped with any value of `[mid-left; left]` + // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care + // about overlaps here. + // 3) The `if` condition about `left <= right` ensures writing `left` elements to + // `dim = mid-left+right` is valid because: + // - `buf` is valid and `left` elements were written in it in 1) + // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` + unsafe { + // 1) + ptr::copy_nonoverlapping(mid.sub(left), buf, left); + // 2) + ptr::copy(mid, mid.sub(left), right); + // 3) + ptr::copy_nonoverlapping(buf, dim, left); + } + } else { + // SAFETY: same reasoning as above but with `left` and `right` reversed + unsafe { + ptr::copy_nonoverlapping(mid, buf, right); + ptr::copy(mid.sub(left), dim, left); + ptr::copy_nonoverlapping(buf, mid.sub(left), right); + } + } +} + +/// Algorithm 2 is used for small values of `left + right` or for large `T`. The elements +/// are moved into their final positions one at a time starting at `mid - left` and advancing by +/// `right` steps modulo `left + right`, such that only one temporary is needed. Eventually, we +/// arrive back at `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps +/// skipped over elements. For example: /// ```text /// left = 10, right = 6 /// the `^` indicates an element in its final place @@ -39,17 +109,104 @@ use crate::{cmp, ptr}; /// `gcd(left + right, right)` value). The end result is that all elements are finalized once and /// only once. /// -/// Algorithm 2 is used if `left + right` is large but `min(left, right)` is small enough to -/// fit onto a stack buffer. The `min(left, right)` elements are copied onto the buffer, `memmove` -/// is applied to the others, and the ones on the buffer are moved back into the hole on the -/// opposite side of where they originated. -/// -/// Algorithms that can be vectorized outperform the above once `left + right` becomes large enough. -/// Algorithm 1 can be vectorized by chunking and performing many rounds at once, but there are too +/// Algorithm 2 can be vectorized by chunking and performing many rounds at once, but there are too /// few rounds on average until `left + right` is enormous, and the worst case of a single -/// round is always there. Instead, algorithm 3 utilizes repeated swapping of -/// `min(left, right)` elements until a smaller rotate problem is left. +/// round is always there. +/// +/// # Safety +/// +/// The specified range must be valid for reading and writing. +#[inline] +unsafe fn ptr_rotate_gcd<T>(left: usize, mid: *mut T, right: usize) { + // Algorithm 2 + // Microbenchmarks indicate that the average performance for random shifts is better all + // the way until about `left + right == 32`, but the worst case performance breaks even + // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 + // `usize`s, this algorithm also outperforms other algorithms. + // SAFETY: callers must ensure `mid - left` is valid for reading and writing. + let x = unsafe { mid.sub(left) }; + // beginning of first round + // SAFETY: see previous comment. + let mut tmp: T = unsafe { x.read() }; + let mut i = right; + // `gcd` can be found before hand by calculating `gcd(left + right, right)`, + // but it is faster to do one loop which calculates the gcd as a side effect, then + // doing the rest of the chunk + let mut gcd = right; + // benchmarks reveal that it is faster to swap temporaries all the way through instead + // of reading one temporary once, copying backwards, and then writing that temporary at + // the very end. This is possibly due to the fact that swapping or replacing temporaries + // uses only one memory address in the loop instead of needing to manage two. + loop { + // [long-safety-expl] + // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and + // writing. + // + // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` + // - `i <= left+right-1` is always true + // - if `i < left`, `right` is added so `i < left+right` and on the next + // iteration `left` is removed from `i` so it doesn't go further + // - if `i >= left`, `left` is removed immediately and so it doesn't go further. + // - overflows cannot happen for `i` since the function's safety contract ask for + // `mid+right-1 = x+left+right` to be valid for writing + // - underflows cannot happen because `i` must be bigger or equal to `left` for + // a subtraction of `left` to happen. + // + // So `x+i` is valid for reading and writing if the caller respected the contract + tmp = unsafe { x.add(i).replace(tmp) }; + // instead of incrementing `i` and then checking if it is outside the bounds, we + // check if `i` will go outside the bounds on the next increment. This prevents + // any wrapping of pointers or `usize`. + if i >= left { + i -= left; + if i == 0 { + // end of first round + // SAFETY: tmp has been read from a valid source and x is valid for writing + // according to the caller. + unsafe { x.write(tmp) }; + break; + } + // this conditional must be here if `left + right >= 15` + if i < gcd { + gcd = i; + } + } else { + i += right; + } + } + // finish the chunk with more rounds + for start in 1..gcd { + // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for + // reading and writing as per the function's safety contract, see [long-safety-expl] + // above + tmp = unsafe { x.add(start).read() }; + // [safety-expl-addition] + // + // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the + // greatest common divisor of `(left+right, right)` means that `left = right` so + // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing + // according to the function's safety contract. + i = start + right; + loop { + // SAFETY: see [long-safety-expl] and [safety-expl-addition] + tmp = unsafe { x.add(i).replace(tmp) }; + if i >= left { + i -= left; + if i == start { + // SAFETY: see [long-safety-expl] and [safety-expl-addition] + unsafe { x.add(start).write(tmp) }; + break; + } + } else { + i += right; + } + } + } +} + +/// Algorithm 3 utilizes repeated swapping of `min(left, right)` elements. /// +/// /// /// ```text /// left = 11, right = 4 /// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3] @@ -60,144 +217,14 @@ use crate::{cmp, ptr}; /// we cannot swap any more, but a smaller rotation problem is left to solve /// ``` /// when `left < right` the swapping happens from the left instead. -pub(super) unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize) { - type BufType = [usize; 32]; - if T::IS_ZST { - return; - } +/// +/// # Safety +/// +/// The specified range must be valid for reading and writing. +#[inline] +unsafe fn ptr_rotate_swap<T>(mut left: usize, mut mid: *mut T, mut right: usize) { loop { - // N.B. the below algorithms can fail if these cases are not checked - if (right == 0) || (left == 0) { - return; - } - if !cfg!(feature = "optimize_for_size") - && ((left + right < 24) || (mem::size_of::<T>() > mem::size_of::<[usize; 4]>())) - { - // Algorithm 1 - // Microbenchmarks indicate that the average performance for random shifts is better all - // the way until about `left + right == 32`, but the worst case performance breaks even - // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 - // `usize`s, this algorithm also outperforms other algorithms. - // SAFETY: callers must ensure `mid - left` is valid for reading and writing. - let x = unsafe { mid.sub(left) }; - // beginning of first round - // SAFETY: see previous comment. - let mut tmp: T = unsafe { x.read() }; - let mut i = right; - // `gcd` can be found before hand by calculating `gcd(left + right, right)`, - // but it is faster to do one loop which calculates the gcd as a side effect, then - // doing the rest of the chunk - let mut gcd = right; - // benchmarks reveal that it is faster to swap temporaries all the way through instead - // of reading one temporary once, copying backwards, and then writing that temporary at - // the very end. This is possibly due to the fact that swapping or replacing temporaries - // uses only one memory address in the loop instead of needing to manage two. - loop { - // [long-safety-expl] - // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and - // writing. - // - // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` - // - `i <= left+right-1` is always true - // - if `i < left`, `right` is added so `i < left+right` and on the next - // iteration `left` is removed from `i` so it doesn't go further - // - if `i >= left`, `left` is removed immediately and so it doesn't go further. - // - overflows cannot happen for `i` since the function's safety contract ask for - // `mid+right-1 = x+left+right` to be valid for writing - // - underflows cannot happen because `i` must be bigger or equal to `left` for - // a subtraction of `left` to happen. - // - // So `x+i` is valid for reading and writing if the caller respected the contract - tmp = unsafe { x.add(i).replace(tmp) }; - // instead of incrementing `i` and then checking if it is outside the bounds, we - // check if `i` will go outside the bounds on the next increment. This prevents - // any wrapping of pointers or `usize`. - if i >= left { - i -= left; - if i == 0 { - // end of first round - // SAFETY: tmp has been read from a valid source and x is valid for writing - // according to the caller. - unsafe { x.write(tmp) }; - break; - } - // this conditional must be here if `left + right >= 15` - if i < gcd { - gcd = i; - } - } else { - i += right; - } - } - // finish the chunk with more rounds - for start in 1..gcd { - // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for - // reading and writing as per the function's safety contract, see [long-safety-expl] - // above - tmp = unsafe { x.add(start).read() }; - // [safety-expl-addition] - // - // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the - // greatest common divisor of `(left+right, right)` means that `left = right` so - // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing - // according to the function's safety contract. - i = start + right; - loop { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - tmp = unsafe { x.add(i).replace(tmp) }; - if i >= left { - i -= left; - if i == start { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - unsafe { x.add(start).write(tmp) }; - break; - } - } else { - i += right; - } - } - } - return; - // `T` is not a zero-sized type, so it's okay to divide by its size. - } else if !cfg!(feature = "optimize_for_size") - && cmp::min(left, right) <= mem::size_of::<BufType>() / mem::size_of::<T>() - { - // Algorithm 2 - // The `[T; 0]` here is to ensure this is appropriately aligned for T - let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); - let buf = rawarray.as_mut_ptr() as *mut T; - // SAFETY: `mid-left <= mid-left+right < mid+right` - let dim = unsafe { mid.sub(left).add(right) }; - if left <= right { - // SAFETY: - // - // 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in - // `buf` without overflow and `buf` was created just above and so cannot be - // overlapped with any value of `[mid-left; left]` - // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care - // about overlaps here. - // 3) The `if` condition about `left <= right` ensures writing `left` elements to - // `dim = mid-left+right` is valid because: - // - `buf` is valid and `left` elements were written in it in 1) - // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` - unsafe { - // 1) - ptr::copy_nonoverlapping(mid.sub(left), buf, left); - // 2) - ptr::copy(mid, mid.sub(left), right); - // 3) - ptr::copy_nonoverlapping(buf, dim, left); - } - } else { - // SAFETY: same reasoning as above but with `left` and `right` reversed - unsafe { - ptr::copy_nonoverlapping(mid, buf, right); - ptr::copy(mid.sub(left), dim, left); - ptr::copy_nonoverlapping(buf, mid.sub(left), right); - } - } - return; - } else if left >= right { + if left >= right { // Algorithm 3 // There is an alternate way of swapping that involves finding where the last swap // of this algorithm would be, and swapping using that last chunk instead of swapping @@ -233,5 +260,8 @@ pub(super) unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: } } } + if (right == 0) || (left == 0) { + return; + } } } diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs index 644e75a4581..cf1df1e91a5 100644 --- a/library/core/src/slice/sort/stable/drift.rs +++ b/library/core/src/slice/sort/stable/drift.rs @@ -10,8 +10,8 @@ use crate::{cmp, intrinsics}; /// Sorts `v` based on comparison function `is_less`. If `eager_sort` is true, /// it will only do small-sorts and physical merges, ensuring O(N * log(N)) -/// worst-case complexity. `scratch.len()` must be at least `max(v.len() / 2, -/// MIN_SMALL_SORT_SCRATCH_LEN)` otherwise the implementation may abort. +/// worst-case complexity. `scratch.len()` must be at least +/// `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` otherwise the implementation may abort. /// Fully ascending and descending inputs will be sorted with exactly N - 1 /// comparisons. /// diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index 7adcc83b818..3ff2e71fd05 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -41,6 +41,8 @@ pub fn sort<T, F: FnMut(&T, &T) -> bool, BufT: BufGuard<T>>(v: &mut [T], is_less cfg_if! { if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { + // Unlike driftsort, mergesort only requires len / 2, + // not len - len / 2. let alloc_len = len / 2; cfg_if! { @@ -91,16 +93,26 @@ fn driftsort_main<T, F: FnMut(&T, &T) -> bool, BufT: BufGuard<T>>(v: &mut [T], i // By allocating n elements of memory we can ensure the entire input can // be sorted using stable quicksort, which allows better performance on // random and low-cardinality distributions. However, we still want to - // reduce our memory usage to n / 2 for large inputs. We do this by scaling - // our allocation as max(n / 2, min(n, 8MB)), ensuring we scale like n for - // small inputs and n / 2 for large inputs, without a sudden drop off. We - // also need to ensure our alloc >= MIN_SMALL_SORT_SCRATCH_LEN, as the + // reduce our memory usage to n - n / 2 for large inputs. We do this by scaling + // our allocation as max(n - n / 2, min(n, 8MB)), ensuring we scale like n for + // small inputs and n - n / 2 for large inputs, without a sudden drop off. We + // also need to ensure our alloc >= SMALL_SORT_GENERAL_SCRATCH_LEN, as the // small-sort always needs this much memory. + // + // driftsort will produce unsorted runs of up to min_good_run_len, which + // is at most len - len / 2. + // Unsorted runs need to be processed by quicksort, which requires as much + // scratch space as the run length, therefore the scratch space must be at + // least len - len / 2. + // If min_good_run_len is ever modified, this code must be updated to allocate + // the correct scratch size for it. const MAX_FULL_ALLOC_BYTES: usize = 8_000_000; // 8MB let max_full_alloc = MAX_FULL_ALLOC_BYTES / mem::size_of::<T>(); let len = v.len(); - let alloc_len = - cmp::max(cmp::max(len / 2, cmp::min(len, max_full_alloc)), SMALL_SORT_GENERAL_SCRATCH_LEN); + let alloc_len = cmp::max( + cmp::max(len - len / 2, cmp::min(len, max_full_alloc)), + SMALL_SORT_GENERAL_SCRATCH_LEN, + ); // For small inputs 4KiB of stack storage suffices, which allows us to avoid // calling the (de-)allocator. Benchmarks showed this was quite beneficial. diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index 0c8308bfce0..630c6ff9077 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -7,6 +7,8 @@ use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; use crate::{intrinsics, ptr}; /// Sorts `v` recursively using quicksort. +/// `scratch.len()` must be at least `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` +/// otherwise the implementation may abort. /// /// `limit` when initialized with `c*log(v.len())` for some c ensures we do not /// overflow the stack or go quadratic. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 8a473b398bb..5b258a7c844 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -160,6 +160,182 @@ impl str { self.len() == 0 } + /// Converts a slice of bytes to a string slice. + /// + /// A string slice ([`&str`]) is made of bytes ([`u8`]), and a byte slice + /// ([`&[u8]`][byteslice]) is made of bytes, so this function converts between + /// the two. Not all byte slices are valid string slices, however: [`&str`] requires + /// that it is valid UTF-8. `from_utf8()` checks to ensure that the bytes are valid + /// UTF-8, and then does the conversion. + /// + /// [`&str`]: str + /// [byteslice]: prim@slice + /// + /// If you are sure that the byte slice is valid UTF-8, and you don't want to + /// incur the overhead of the validity check, there is an unsafe version of + /// this function, [`from_utf8_unchecked`], which has the same + /// behavior but skips the check. + /// + /// If you need a `String` instead of a `&str`, consider + /// [`String::from_utf8`][string]. + /// + /// [string]: ../std/string/struct.String.html#method.from_utf8 + /// + /// Because you can stack-allocate a `[u8; N]`, and you can take a + /// [`&[u8]`][byteslice] of it, this function is one way to have a + /// stack-allocated string. There is an example of this in the + /// examples section below. + /// + /// [byteslice]: slice + /// + /// # Errors + /// + /// Returns `Err` if the slice is not UTF-8 with a description as to why the + /// provided slice is not UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// // We can use the ? (try) operator to check if the bytes are valid + /// let sparkle_heart = str::from_utf8(&sparkle_heart)?; + /// + /// assert_eq!("💖", sparkle_heart); + /// # Ok::<_, str::Utf8Error>(()) + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// use std::str; + /// + /// // some invalid bytes, in a vector + /// let sparkle_heart = vec![0, 159, 146, 150]; + /// + /// assert!(str::from_utf8(&sparkle_heart).is_err()); + /// ``` + /// + /// See the docs for [`Utf8Error`] for more details on the kinds of + /// errors that can be returned. + /// + /// A "stack allocated string": + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a stack-allocated array + /// let sparkle_heart = [240, 159, 146, 150]; + /// + /// // We know these bytes are valid, so just use `unwrap()`. + /// let sparkle_heart: &str = str::from_utf8(&sparkle_heart).unwrap(); + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { + converts::from_utf8(v) + } + + /// Converts a mutable slice of bytes to a mutable string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // "Hello, Rust!" as a mutable vector + /// let mut hellorust = vec![72, 101, 108, 108, 111, 44, 32, 82, 117, 115, 116, 33]; + /// + /// // As we know these bytes are valid, we can use `unwrap()` + /// let outstr = str::from_utf8_mut(&mut hellorust).unwrap(); + /// + /// assert_eq!("Hello, Rust!", outstr); + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// use std::str; + /// + /// // Some invalid bytes in a mutable vector + /// let mut invalid = vec![128, 223]; + /// + /// assert!(str::from_utf8_mut(&mut invalid).is_err()); + /// ``` + /// See the docs for [`Utf8Error`] for more details on the kinds of + /// errors that can be returned. + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + #[rustc_const_unstable(feature = "const_str_from_utf8", issue = "91006")] + pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { + converts::from_utf8_mut(v) + } + + /// Converts a slice of bytes to a string slice without checking + /// that the string contains valid UTF-8. + /// + /// See the safe version, [`from_utf8`], for more information. + /// + /// # Safety + /// + /// The bytes passed in must be valid UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// let sparkle_heart = unsafe { + /// str::from_utf8_unchecked(&sparkle_heart) + /// }; + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { + // SAFETY: converts::from_utf8_unchecked has the same safety requirements as this function. + unsafe { converts::from_utf8_unchecked(v) } + } + + /// Converts a slice of bytes to a string slice without checking + /// that the string contains valid UTF-8; mutable version. + /// + /// See the immutable version, [`from_utf8_unchecked()`] for more information. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// let mut heart = vec![240, 159, 146, 150]; + /// let heart = unsafe { str::from_utf8_unchecked_mut(&mut heart) }; + /// + /// assert_eq!("💖", heart); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { + // SAFETY: converts::from_utf8_unchecked_mut has the same safety requirements as this function. + unsafe { converts::from_utf8_unchecked_mut(v) } + } + /// Checks that `index`-th byte is the first byte in a UTF-8 code point /// sequence or the end of the string. /// @@ -1108,7 +1284,8 @@ impl str { LinesAny(self.lines()) } - /// Returns an iterator of `u16` over the string encoded as UTF-16. + /// Returns an iterator of `u16` over the string encoded + /// as native endian UTF-16 (without byte-order mark). /// /// # Examples /// diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index fda26a67299..73180bde54a 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -716,6 +716,12 @@ impl AtomicBool { /// AcqRel | AcqRel | Acquire /// SeqCst | SeqCst | SeqCst /// + /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use + /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`, + /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err` + /// rather than to infer success vs failure based on the value that was read. + /// + /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead. /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, /// which allows the compiler to generate better assembly code when the compare and swap /// is used in a loop. @@ -1164,7 +1170,7 @@ impl AtomicBool { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1203,6 +1209,125 @@ impl AtomicBool { } Err(prev) } + + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicBool::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(true)); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(bool) -> Option<bool>, + ) -> Result<bool, bool> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicBool::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), true); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(bool) -> bool, + ) -> bool { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } } #[cfg(target_has_atomic_load_store = "ptr")] @@ -1532,6 +1657,12 @@ impl<T> AtomicPtr<T> { /// AcqRel | AcqRel | Acquire /// SeqCst | SeqCst | SeqCst /// + /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use + /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`, + /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err` + /// rather than to infer success vs failure based on the value that was read. + /// + /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead. /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, /// which allows the compiler to generate better assembly code when the compare and swap /// is used in a loop. @@ -1684,7 +1815,7 @@ impl<T> AtomicPtr<T> { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1732,6 +1863,137 @@ impl<T> AtomicPtr<T> { } Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicPtr::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// assert_eq!(some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr)); + /// let result = some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + /// if x == ptr { + /// Some(new) + /// } else { + /// None + /// } + /// }); + /// assert_eq!(result, Ok(ptr)); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "ptr")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(*mut T) -> Option<*mut T>, + ) -> Result<*mut T, *mut T> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicPtr::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// let result = some_ptr.update(Ordering::SeqCst, Ordering::SeqCst, |_| new); + /// assert_eq!(result, ptr); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(*mut T) -> *mut T, + ) -> *mut T { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } /// Offsets the pointer's address by adding `val` (in units of `T`), /// returning the previous pointer. @@ -2297,7 +2559,7 @@ macro_rules! atomic_int { $int_type, no = [ "**Note:** This function is only available on targets where `", - stringify!($int_type), "` has an alignment of ", $align, " bytes." + stringify!($atomic_type), "` has the same alignment as `", stringify!($int_type), "`." ], }] /// @@ -2521,6 +2783,12 @@ macro_rules! atomic_int { /// AcqRel | AcqRel | Acquire /// SeqCst | SeqCst | SeqCst /// + /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use + /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`, + /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err` + /// rather than to infer success vs failure based on the value that was read. + /// + /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead. /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, /// which allows the compiler to generate better assembly code when the compare and swap /// is used in a loop. @@ -2875,7 +3143,7 @@ macro_rules! atomic_int { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] /// and suffers from the same drawbacks. @@ -2913,6 +3181,127 @@ macro_rules! atomic_int { Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else + /// `Err(previous_value)`. + /// + #[doc = concat!("See also: [`update`](`", stringify!($atomic_type), "::update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied + /// only once to the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut($int_type) -> Option<$int_type>, + ) -> Result<$int_type, $int_type> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + #[doc = concat!("See also: [`try_update`](`", stringify!($atomic_type), "::try_update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 7); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 8); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut($int_type) -> $int_type, + ) -> $int_type { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } + /// Maximum with the current value. /// /// Finds the maximum of the current value and the argument `val`, and @@ -3727,33 +4116,33 @@ pub fn fence(order: Ordering) { /// /// # Examples /// -/// Without `compiler_fence`, the `assert_eq!` in following code -/// is *not* guaranteed to succeed, despite everything happening in a single thread. -/// To see why, remember that the compiler is free to swap the stores to -/// `IMPORTANT_VARIABLE` and `IS_READY` since they are both -/// `Ordering::Relaxed`. If it does, and the signal handler is invoked right -/// after `IS_READY` is updated, then the signal handler will see -/// `IS_READY=1`, but `IMPORTANT_VARIABLE=0`. -/// Using a `compiler_fence` remedies this situation. +/// Without the two `compiler_fence` calls, the read of `IMPORTANT_VARIABLE` in `signal_handler` +/// is *undefined behavior* due to a data race, despite everything happening in a single thread. +/// This is because the signal handler is considered to run concurrently with its associated +/// thread, and explicit synchronization is required to pass data between a thread and its +/// signal handler. The code below uses two `compiler_fence` calls to establish the usual +/// release-acquire synchronization pattern (see [`fence`] for an image). /// /// ``` -/// use std::sync::atomic::{AtomicBool, AtomicUsize}; +/// use std::sync::atomic::AtomicBool; /// use std::sync::atomic::Ordering; /// use std::sync::atomic::compiler_fence; /// -/// static IMPORTANT_VARIABLE: AtomicUsize = AtomicUsize::new(0); +/// static mut IMPORTANT_VARIABLE: usize = 0; /// static IS_READY: AtomicBool = AtomicBool::new(false); /// /// fn main() { -/// IMPORTANT_VARIABLE.store(42, Ordering::Relaxed); -/// // prevent earlier writes from being moved beyond this point +/// unsafe { IMPORTANT_VARIABLE = 42 }; +/// // Marks earlier writes as being released with future relaxed stores. /// compiler_fence(Ordering::Release); /// IS_READY.store(true, Ordering::Relaxed); /// } /// /// fn signal_handler() { /// if IS_READY.load(Ordering::Relaxed) { -/// assert_eq!(IMPORTANT_VARIABLE.load(Ordering::Relaxed), 42); +/// // Acquires writes that were released with relaxed stores that we read from. +/// compiler_fence(Ordering::Acquire); +/// assert_eq!(unsafe { IMPORTANT_VARIABLE }, 42); /// } /// } /// ``` diff --git a/library/coretests/Cargo.toml b/library/coretests/Cargo.toml new file mode 100644 index 00000000000..ec940abea11 --- /dev/null +++ b/library/coretests/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "coretests" +version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "Tests for the Rust Core Library" +autotests = false +autobenches = false +edition = "2021" + +[lib] +path = "lib.rs" +test = false +bench = false + +[[test]] +name = "coretests" +path = "tests/lib.rs" + +[[bench]] +name = "corebenches" +path = "benches/lib.rs" +test = true + +[dev-dependencies] +rand = { version = "0.8.5", default-features = false } +rand_xorshift = { version = "0.3.0", default-features = false } diff --git a/library/core/benches/any.rs b/library/coretests/benches/any.rs index 6b150432f87..6b150432f87 100644 --- a/library/core/benches/any.rs +++ b/library/coretests/benches/any.rs diff --git a/library/core/benches/array.rs b/library/coretests/benches/array.rs index 751f3235a5f..751f3235a5f 100644 --- a/library/core/benches/array.rs +++ b/library/coretests/benches/array.rs diff --git a/library/core/benches/ascii.rs b/library/coretests/benches/ascii.rs index 3fe45aa360b..3fe45aa360b 100644 --- a/library/core/benches/ascii.rs +++ b/library/coretests/benches/ascii.rs diff --git a/library/core/benches/ascii/is_ascii.rs b/library/coretests/benches/ascii/is_ascii.rs index ced7084fb0e..ced7084fb0e 100644 --- a/library/core/benches/ascii/is_ascii.rs +++ b/library/coretests/benches/ascii/is_ascii.rs diff --git a/library/core/benches/char/methods.rs b/library/coretests/benches/char/methods.rs index ed71920a4fc..ed71920a4fc 100644 --- a/library/core/benches/char/methods.rs +++ b/library/coretests/benches/char/methods.rs diff --git a/library/core/benches/char/mod.rs b/library/coretests/benches/char/mod.rs index 9ca51a76847..9ca51a76847 100644 --- a/library/core/benches/char/mod.rs +++ b/library/coretests/benches/char/mod.rs diff --git a/library/core/benches/fmt.rs b/library/coretests/benches/fmt.rs index ed478b0f1e0..ee8e981b46b 100644 --- a/library/core/benches/fmt.rs +++ b/library/coretests/benches/fmt.rs @@ -124,42 +124,41 @@ fn write_str_macro_debug_ascii(bh: &mut Bencher) { #[bench] fn write_u128_max(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", u128::MAX)); + black_box(format!("{}", black_box(u128::MAX))); }); } #[bench] fn write_u128_min(bh: &mut Bencher) { bh.iter(|| { - let s = format!("{}", 0u128); - test::black_box(s); + black_box(format!("{}", black_box(u128::MIN))); }); } #[bench] fn write_u64_max(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", u64::MAX)); + black_box(format!("{}", black_box(u64::MAX))); }); } #[bench] fn write_u64_min(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", 0u64)); + black_box(format!("{}", black_box(u64::MIN))); }); } #[bench] fn write_u8_max(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", u8::MAX)); + black_box(format!("{}", black_box(u8::MAX))); }); } #[bench] fn write_u8_min(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", 0u8)); + black_box(format!("{}", black_box(u8::MIN))); }); } diff --git a/library/core/benches/hash/mod.rs b/library/coretests/benches/hash/mod.rs index 4f2e152b695..4f2e152b695 100644 --- a/library/core/benches/hash/mod.rs +++ b/library/coretests/benches/hash/mod.rs diff --git a/library/core/benches/hash/sip.rs b/library/coretests/benches/hash/sip.rs index c6562d3c011..c6562d3c011 100644 --- a/library/core/benches/hash/sip.rs +++ b/library/coretests/benches/hash/sip.rs diff --git a/library/core/benches/iter.rs b/library/coretests/benches/iter.rs index e14f26b7290..e14f26b7290 100644 --- a/library/core/benches/iter.rs +++ b/library/coretests/benches/iter.rs diff --git a/library/core/benches/lib.rs b/library/coretests/benches/lib.rs index 32d15c386cb..32d15c386cb 100644 --- a/library/core/benches/lib.rs +++ b/library/coretests/benches/lib.rs diff --git a/library/core/benches/net/addr_parser.rs b/library/coretests/benches/net/addr_parser.rs index bbf2ea3eb97..bbf2ea3eb97 100644 --- a/library/core/benches/net/addr_parser.rs +++ b/library/coretests/benches/net/addr_parser.rs diff --git a/library/core/benches/net/mod.rs b/library/coretests/benches/net/mod.rs index c29aed46ccd..c29aed46ccd 100644 --- a/library/core/benches/net/mod.rs +++ b/library/coretests/benches/net/mod.rs diff --git a/library/core/benches/num/dec2flt/mod.rs b/library/coretests/benches/num/dec2flt/mod.rs index bad211f240c..bad211f240c 100644 --- a/library/core/benches/num/dec2flt/mod.rs +++ b/library/coretests/benches/num/dec2flt/mod.rs diff --git a/library/core/benches/num/flt2dec/mod.rs b/library/coretests/benches/num/flt2dec/mod.rs index 428d0bbbbfb..428d0bbbbfb 100644 --- a/library/core/benches/num/flt2dec/mod.rs +++ b/library/coretests/benches/num/flt2dec/mod.rs diff --git a/library/core/benches/num/flt2dec/strategy/dragon.rs b/library/coretests/benches/num/flt2dec/strategy/dragon.rs index 45266971403..45266971403 100644 --- a/library/core/benches/num/flt2dec/strategy/dragon.rs +++ b/library/coretests/benches/num/flt2dec/strategy/dragon.rs diff --git a/library/core/benches/num/flt2dec/strategy/grisu.rs b/library/coretests/benches/num/flt2dec/strategy/grisu.rs index d20f9b02f7e..d20f9b02f7e 100644 --- a/library/core/benches/num/flt2dec/strategy/grisu.rs +++ b/library/coretests/benches/num/flt2dec/strategy/grisu.rs diff --git a/library/core/benches/num/int_log/mod.rs b/library/coretests/benches/num/int_log/mod.rs index e5874ddf03b..e5874ddf03b 100644 --- a/library/core/benches/num/int_log/mod.rs +++ b/library/coretests/benches/num/int_log/mod.rs diff --git a/library/core/benches/num/int_pow/mod.rs b/library/coretests/benches/num/int_pow/mod.rs index 46f47028d56..46f47028d56 100644 --- a/library/core/benches/num/int_pow/mod.rs +++ b/library/coretests/benches/num/int_pow/mod.rs diff --git a/library/core/benches/num/int_sqrt/mod.rs b/library/coretests/benches/num/int_sqrt/mod.rs index e47b92e866e..e47b92e866e 100644 --- a/library/core/benches/num/int_sqrt/mod.rs +++ b/library/coretests/benches/num/int_sqrt/mod.rs diff --git a/library/core/benches/num/mod.rs b/library/coretests/benches/num/mod.rs index b36100e59a9..b36100e59a9 100644 --- a/library/core/benches/num/mod.rs +++ b/library/coretests/benches/num/mod.rs diff --git a/library/core/benches/ops.rs b/library/coretests/benches/ops.rs index 3d0b3302957..3d0b3302957 100644 --- a/library/core/benches/ops.rs +++ b/library/coretests/benches/ops.rs diff --git a/library/core/benches/pattern.rs b/library/coretests/benches/pattern.rs index b0f8b39c22e..b0f8b39c22e 100644 --- a/library/core/benches/pattern.rs +++ b/library/coretests/benches/pattern.rs diff --git a/library/core/benches/slice.rs b/library/coretests/benches/slice.rs index 29a66b62199..29a66b62199 100644 --- a/library/core/benches/slice.rs +++ b/library/coretests/benches/slice.rs diff --git a/library/core/benches/str.rs b/library/coretests/benches/str.rs index 2f7d9d56a70..2f7d9d56a70 100644 --- a/library/core/benches/str.rs +++ b/library/coretests/benches/str.rs diff --git a/library/core/benches/str/char_count.rs b/library/coretests/benches/str/char_count.rs index 343e23dcf41..343e23dcf41 100644 --- a/library/core/benches/str/char_count.rs +++ b/library/coretests/benches/str/char_count.rs diff --git a/library/core/benches/str/corpora.rs b/library/coretests/benches/str/corpora.rs index b4ac625061d..b4ac625061d 100644 --- a/library/core/benches/str/corpora.rs +++ b/library/coretests/benches/str/corpora.rs diff --git a/library/core/benches/str/debug.rs b/library/coretests/benches/str/debug.rs index e41d4fa110a..e41d4fa110a 100644 --- a/library/core/benches/str/debug.rs +++ b/library/coretests/benches/str/debug.rs diff --git a/library/core/benches/str/iter.rs b/library/coretests/benches/str/iter.rs index d2586cef258..d2586cef258 100644 --- a/library/core/benches/str/iter.rs +++ b/library/coretests/benches/str/iter.rs diff --git a/library/core/benches/tuple.rs b/library/coretests/benches/tuple.rs index dcda7c641aa..dcda7c641aa 100644 --- a/library/core/benches/tuple.rs +++ b/library/coretests/benches/tuple.rs diff --git a/library/coretests/lib.rs b/library/coretests/lib.rs new file mode 100644 index 00000000000..b49208cd4eb --- /dev/null +++ b/library/coretests/lib.rs @@ -0,0 +1 @@ +// Intentionally left empty. diff --git a/library/core/tests/alloc.rs b/library/coretests/tests/alloc.rs index b88f1821cd7..b88f1821cd7 100644 --- a/library/core/tests/alloc.rs +++ b/library/coretests/tests/alloc.rs diff --git a/library/core/tests/any.rs b/library/coretests/tests/any.rs index 25002617d0b..25002617d0b 100644 --- a/library/core/tests/any.rs +++ b/library/coretests/tests/any.rs diff --git a/library/core/tests/array.rs b/library/coretests/tests/array.rs index b6d18f1ec38..b6d18f1ec38 100644 --- a/library/core/tests/array.rs +++ b/library/coretests/tests/array.rs diff --git a/library/core/tests/ascii.rs b/library/coretests/tests/ascii.rs index ce09ee507f1..ce09ee507f1 100644 --- a/library/core/tests/ascii.rs +++ b/library/coretests/tests/ascii.rs diff --git a/library/core/tests/ascii_char.rs b/library/coretests/tests/ascii_char.rs index 75b5fd4b9e6..75b5fd4b9e6 100644 --- a/library/core/tests/ascii_char.rs +++ b/library/coretests/tests/ascii_char.rs diff --git a/library/core/tests/asserting.rs b/library/coretests/tests/asserting.rs index 1d9670886eb..1d9670886eb 100644 --- a/library/core/tests/asserting.rs +++ b/library/coretests/tests/asserting.rs diff --git a/library/core/tests/async_iter/mod.rs b/library/coretests/tests/async_iter/mod.rs index 4f425d7286d..4f425d7286d 100644 --- a/library/core/tests/async_iter/mod.rs +++ b/library/coretests/tests/async_iter/mod.rs diff --git a/library/core/tests/atomic.rs b/library/coretests/tests/atomic.rs index 0ffba538b20..0ffba538b20 100644 --- a/library/core/tests/atomic.rs +++ b/library/coretests/tests/atomic.rs diff --git a/library/core/tests/bool.rs b/library/coretests/tests/bool.rs index 47f6459915b..bcd6dc2abac 100644 --- a/library/core/tests/bool.rs +++ b/library/coretests/tests/bool.rs @@ -71,14 +71,14 @@ fn test_bool() { #[test] pub fn test_bool_not() { if !false { - assert!((true)); + assert!(true); } else { - assert!((false)); + assert!(false); } if !true { - assert!((false)); + assert!(false); } else { - assert!((true)); + assert!(true); } } diff --git a/library/coretests/tests/bstr.rs b/library/coretests/tests/bstr.rs new file mode 100644 index 00000000000..cd4d69d6b33 --- /dev/null +++ b/library/coretests/tests/bstr.rs @@ -0,0 +1,52 @@ +use core::bstr::ByteStr; + +#[test] +fn test_debug() { + assert_eq!( + r#""\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff""#, + format!("{:?}", ByteStr::new(b"\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff")), + ); +} + +#[test] +fn test_display() { + let b1 = ByteStr::new("abc"); + let b2 = ByteStr::new(b"\xf0\x28\x8c\xbc"); + + assert_eq!(&format!("{b1}"), "abc"); + assert_eq!(&format!("{b2}"), "�(��"); + + assert_eq!(&format!("{b1:<7}!"), "abc !"); + assert_eq!(&format!("{b1:>7}!"), " abc!"); + assert_eq!(&format!("{b1:^7}!"), " abc !"); + assert_eq!(&format!("{b1:^6}!"), " abc !"); + assert_eq!(&format!("{b1:-<7}!"), "abc----!"); + assert_eq!(&format!("{b1:->7}!"), "----abc!"); + assert_eq!(&format!("{b1:-^7}!"), "--abc--!"); + assert_eq!(&format!("{b1:-^6}!"), "-abc--!"); + + assert_eq!(&format!("{b2:<7}!"), "�(�� !"); + assert_eq!(&format!("{b2:>7}!"), " �(��!"); + assert_eq!(&format!("{b2:^7}!"), " �(�� !"); + assert_eq!(&format!("{b2:^6}!"), " �(�� !"); + assert_eq!(&format!("{b2:-<7}!"), "�(��---!"); + assert_eq!(&format!("{b2:->7}!"), "---�(��!"); + assert_eq!(&format!("{b2:-^7}!"), "-�(��--!"); + assert_eq!(&format!("{b2:-^6}!"), "-�(��-!"); + + assert_eq!(&format!("{b1:<2}!"), "abc!"); + assert_eq!(&format!("{b1:>2}!"), "abc!"); + assert_eq!(&format!("{b1:^2}!"), "abc!"); + assert_eq!(&format!("{b1:-<2}!"), "abc!"); + assert_eq!(&format!("{b1:->2}!"), "abc!"); + assert_eq!(&format!("{b1:-^2}!"), "abc!"); + + assert_eq!(&format!("{b2:<3}!"), "�(��!"); + assert_eq!(&format!("{b2:>3}!"), "�(��!"); + assert_eq!(&format!("{b2:^3}!"), "�(��!"); + assert_eq!(&format!("{b2:^2}!"), "�(��!"); + assert_eq!(&format!("{b2:-<3}!"), "�(��!"); + assert_eq!(&format!("{b2:->3}!"), "�(��!"); + assert_eq!(&format!("{b2:-^3}!"), "�(��!"); + assert_eq!(&format!("{b2:-^2}!"), "�(��!"); +} diff --git a/library/core/tests/cell.rs b/library/coretests/tests/cell.rs index d6a401c2b4d..d6a401c2b4d 100644 --- a/library/core/tests/cell.rs +++ b/library/coretests/tests/cell.rs diff --git a/library/core/tests/char.rs b/library/coretests/tests/char.rs index 6422387e956..6422387e956 100644 --- a/library/core/tests/char.rs +++ b/library/coretests/tests/char.rs diff --git a/library/core/tests/clone.rs b/library/coretests/tests/clone.rs index 054b1d3d498..054b1d3d498 100644 --- a/library/core/tests/clone.rs +++ b/library/coretests/tests/clone.rs diff --git a/library/core/tests/cmp.rs b/library/coretests/tests/cmp.rs index 6c4e2146f91..6c4e2146f91 100644 --- a/library/core/tests/cmp.rs +++ b/library/coretests/tests/cmp.rs diff --git a/library/core/tests/const_ptr.rs b/library/coretests/tests/const_ptr.rs index d874f08317f..d874f08317f 100644 --- a/library/core/tests/const_ptr.rs +++ b/library/coretests/tests/const_ptr.rs diff --git a/library/core/tests/convert.rs b/library/coretests/tests/convert.rs index f76dd277884..f76dd277884 100644 --- a/library/core/tests/convert.rs +++ b/library/coretests/tests/convert.rs diff --git a/library/core/tests/error.rs b/library/coretests/tests/error.rs index 996566d3848..996566d3848 100644 --- a/library/core/tests/error.rs +++ b/library/coretests/tests/error.rs diff --git a/library/core/tests/ffi.rs b/library/coretests/tests/ffi.rs index 2b33fbd95f0..2b33fbd95f0 100644 --- a/library/core/tests/ffi.rs +++ b/library/coretests/tests/ffi.rs diff --git a/library/core/tests/ffi/cstr.rs b/library/coretests/tests/ffi/cstr.rs index 9bf4c21a9ab..9bf4c21a9ab 100644 --- a/library/core/tests/ffi/cstr.rs +++ b/library/coretests/tests/ffi/cstr.rs diff --git a/library/core/tests/fmt/builders.rs b/library/coretests/tests/fmt/builders.rs index ba4801f5912..ba4801f5912 100644 --- a/library/core/tests/fmt/builders.rs +++ b/library/coretests/tests/fmt/builders.rs diff --git a/library/core/tests/fmt/float.rs b/library/coretests/tests/fmt/float.rs index 003782f34dc..003782f34dc 100644 --- a/library/core/tests/fmt/float.rs +++ b/library/coretests/tests/fmt/float.rs diff --git a/library/core/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs index 2c93a9bc80d..025c69c4f62 100644 --- a/library/core/tests/fmt/mod.rs +++ b/library/coretests/tests/fmt/mod.rs @@ -52,6 +52,12 @@ fn test_maybe_uninit_short() { } #[test] +fn formatting_options_ctor() { + use core::fmt::FormattingOptions; + assert_eq!(FormattingOptions::new(), FormattingOptions::default()); +} + +#[test] fn formatting_options_flags() { use core::fmt::*; for sign in [None, Some(Sign::Plus), Some(Sign::Minus)] { diff --git a/library/core/tests/fmt/num.rs b/library/coretests/tests/fmt/num.rs index bc387a46ea7..bc387a46ea7 100644 --- a/library/core/tests/fmt/num.rs +++ b/library/coretests/tests/fmt/num.rs diff --git a/library/core/tests/future.rs b/library/coretests/tests/future.rs index ebfe5a0a66d..ebfe5a0a66d 100644 --- a/library/core/tests/future.rs +++ b/library/coretests/tests/future.rs diff --git a/library/core/tests/hash/mod.rs b/library/coretests/tests/hash/mod.rs index 9f14995f73f..1f10a4733b0 100644 --- a/library/core/tests/hash/mod.rs +++ b/library/coretests/tests/hash/mod.rs @@ -141,9 +141,6 @@ fn test_custom_state() { // const { assert!(hash(&Custom { hash: 6 }) == 6) }; } -// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten. -// See https://github.com/kripken/emscripten-fastcomp/issues/169 -#[cfg(not(target_os = "emscripten"))] #[test] fn test_indirect_hasher() { let mut hasher = MyHasher { hash: 0 }; diff --git a/library/core/tests/hash/sip.rs b/library/coretests/tests/hash/sip.rs index f79954f916b..f79954f916b 100644 --- a/library/core/tests/hash/sip.rs +++ b/library/coretests/tests/hash/sip.rs diff --git a/library/core/tests/intrinsics.rs b/library/coretests/tests/intrinsics.rs index 744a6a0d2dd..744a6a0d2dd 100644 --- a/library/core/tests/intrinsics.rs +++ b/library/coretests/tests/intrinsics.rs diff --git a/library/core/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs index a5dd4e52577..a5dd4e52577 100644 --- a/library/core/tests/io/borrowed_buf.rs +++ b/library/coretests/tests/io/borrowed_buf.rs diff --git a/library/core/tests/io/mod.rs b/library/coretests/tests/io/mod.rs index a24893a525a..a24893a525a 100644 --- a/library/core/tests/io/mod.rs +++ b/library/coretests/tests/io/mod.rs diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/coretests/tests/iter/adapters/array_chunks.rs index fb19a519f63..fb19a519f63 100644 --- a/library/core/tests/iter/adapters/array_chunks.rs +++ b/library/coretests/tests/iter/adapters/array_chunks.rs diff --git a/library/core/tests/iter/adapters/by_ref_sized.rs b/library/coretests/tests/iter/adapters/by_ref_sized.rs index a9c066f0e8c..a9c066f0e8c 100644 --- a/library/core/tests/iter/adapters/by_ref_sized.rs +++ b/library/coretests/tests/iter/adapters/by_ref_sized.rs diff --git a/library/core/tests/iter/adapters/chain.rs b/library/coretests/tests/iter/adapters/chain.rs index 1b2c026ee1e..1b2c026ee1e 100644 --- a/library/core/tests/iter/adapters/chain.rs +++ b/library/coretests/tests/iter/adapters/chain.rs diff --git a/library/core/tests/iter/adapters/cloned.rs b/library/coretests/tests/iter/adapters/cloned.rs index 78babb7feab..78babb7feab 100644 --- a/library/core/tests/iter/adapters/cloned.rs +++ b/library/coretests/tests/iter/adapters/cloned.rs diff --git a/library/core/tests/iter/adapters/copied.rs b/library/coretests/tests/iter/adapters/copied.rs index b12f2035dc1..b12f2035dc1 100644 --- a/library/core/tests/iter/adapters/copied.rs +++ b/library/coretests/tests/iter/adapters/copied.rs diff --git a/library/core/tests/iter/adapters/cycle.rs b/library/coretests/tests/iter/adapters/cycle.rs index 8831c09b48b..8831c09b48b 100644 --- a/library/core/tests/iter/adapters/cycle.rs +++ b/library/coretests/tests/iter/adapters/cycle.rs diff --git a/library/core/tests/iter/adapters/enumerate.rs b/library/coretests/tests/iter/adapters/enumerate.rs index b57d51c077e..b57d51c077e 100644 --- a/library/core/tests/iter/adapters/enumerate.rs +++ b/library/coretests/tests/iter/adapters/enumerate.rs diff --git a/library/core/tests/iter/adapters/filter.rs b/library/coretests/tests/iter/adapters/filter.rs index 167851e3333..167851e3333 100644 --- a/library/core/tests/iter/adapters/filter.rs +++ b/library/coretests/tests/iter/adapters/filter.rs diff --git a/library/core/tests/iter/adapters/filter_map.rs b/library/coretests/tests/iter/adapters/filter_map.rs index 46738eda63f..46738eda63f 100644 --- a/library/core/tests/iter/adapters/filter_map.rs +++ b/library/coretests/tests/iter/adapters/filter_map.rs diff --git a/library/core/tests/iter/adapters/flat_map.rs b/library/coretests/tests/iter/adapters/flat_map.rs index ee945e69801..ee945e69801 100644 --- a/library/core/tests/iter/adapters/flat_map.rs +++ b/library/coretests/tests/iter/adapters/flat_map.rs diff --git a/library/core/tests/iter/adapters/flatten.rs b/library/coretests/tests/iter/adapters/flatten.rs index 66b7b6cb563..66b7b6cb563 100644 --- a/library/core/tests/iter/adapters/flatten.rs +++ b/library/coretests/tests/iter/adapters/flatten.rs diff --git a/library/core/tests/iter/adapters/fuse.rs b/library/coretests/tests/iter/adapters/fuse.rs index f41b379b3ac..f41b379b3ac 100644 --- a/library/core/tests/iter/adapters/fuse.rs +++ b/library/coretests/tests/iter/adapters/fuse.rs diff --git a/library/core/tests/iter/adapters/inspect.rs b/library/coretests/tests/iter/adapters/inspect.rs index 939e3a28a72..939e3a28a72 100644 --- a/library/core/tests/iter/adapters/inspect.rs +++ b/library/coretests/tests/iter/adapters/inspect.rs diff --git a/library/core/tests/iter/adapters/intersperse.rs b/library/coretests/tests/iter/adapters/intersperse.rs index 72ae59b6b2f..72ae59b6b2f 100644 --- a/library/core/tests/iter/adapters/intersperse.rs +++ b/library/coretests/tests/iter/adapters/intersperse.rs diff --git a/library/core/tests/iter/adapters/map.rs b/library/coretests/tests/iter/adapters/map.rs index 77ce3819b32..77ce3819b32 100644 --- a/library/core/tests/iter/adapters/map.rs +++ b/library/coretests/tests/iter/adapters/map.rs diff --git a/library/core/tests/iter/adapters/map_windows.rs b/library/coretests/tests/iter/adapters/map_windows.rs index b677f1cfd55..01cebc9b27f 100644 --- a/library/core/tests/iter/adapters/map_windows.rs +++ b/library/coretests/tests/iter/adapters/map_windows.rs @@ -159,11 +159,10 @@ fn output_n2() { <Vec<[char; 2]>>::new(), ); assert_eq!("ab".chars().map_windows(|a: &[_; 2]| *a).collect::<Vec<_>>(), vec![['a', 'b']]); - assert_eq!("abcd".chars().map_windows(|a: &[_; 2]| *a).collect::<Vec<_>>(), vec![ - ['a', 'b'], - ['b', 'c'], - ['c', 'd'] - ],); + assert_eq!( + "abcd".chars().map_windows(|a: &[_; 2]| *a).collect::<Vec<_>>(), + vec![['a', 'b'], ['b', 'c'], ['c', 'd']], + ); } #[test] diff --git a/library/core/tests/iter/adapters/mod.rs b/library/coretests/tests/iter/adapters/mod.rs index dedb4c0a9dd..dedb4c0a9dd 100644 --- a/library/core/tests/iter/adapters/mod.rs +++ b/library/coretests/tests/iter/adapters/mod.rs diff --git a/library/core/tests/iter/adapters/peekable.rs b/library/coretests/tests/iter/adapters/peekable.rs index 7f4341b8902..7f4341b8902 100644 --- a/library/core/tests/iter/adapters/peekable.rs +++ b/library/coretests/tests/iter/adapters/peekable.rs diff --git a/library/core/tests/iter/adapters/scan.rs b/library/coretests/tests/iter/adapters/scan.rs index 1d28ca6b7fd..1d28ca6b7fd 100644 --- a/library/core/tests/iter/adapters/scan.rs +++ b/library/coretests/tests/iter/adapters/scan.rs diff --git a/library/core/tests/iter/adapters/skip.rs b/library/coretests/tests/iter/adapters/skip.rs index 8d5d06ad9fb..8d5d06ad9fb 100644 --- a/library/core/tests/iter/adapters/skip.rs +++ b/library/coretests/tests/iter/adapters/skip.rs diff --git a/library/core/tests/iter/adapters/skip_while.rs b/library/coretests/tests/iter/adapters/skip_while.rs index 929d4f6e64f..929d4f6e64f 100644 --- a/library/core/tests/iter/adapters/skip_while.rs +++ b/library/coretests/tests/iter/adapters/skip_while.rs diff --git a/library/core/tests/iter/adapters/step_by.rs b/library/coretests/tests/iter/adapters/step_by.rs index 6f3300e7a88..6f3300e7a88 100644 --- a/library/core/tests/iter/adapters/step_by.rs +++ b/library/coretests/tests/iter/adapters/step_by.rs diff --git a/library/core/tests/iter/adapters/take.rs b/library/coretests/tests/iter/adapters/take.rs index b932059afec..b932059afec 100644 --- a/library/core/tests/iter/adapters/take.rs +++ b/library/coretests/tests/iter/adapters/take.rs diff --git a/library/core/tests/iter/adapters/take_while.rs b/library/coretests/tests/iter/adapters/take_while.rs index 6f1ebab29b3..6f1ebab29b3 100644 --- a/library/core/tests/iter/adapters/take_while.rs +++ b/library/coretests/tests/iter/adapters/take_while.rs diff --git a/library/core/tests/iter/adapters/zip.rs b/library/coretests/tests/iter/adapters/zip.rs index 70392dca0fa..70392dca0fa 100644 --- a/library/core/tests/iter/adapters/zip.rs +++ b/library/coretests/tests/iter/adapters/zip.rs diff --git a/library/core/tests/iter/mod.rs b/library/coretests/tests/iter/mod.rs index 5b2769d0469..5b2769d0469 100644 --- a/library/core/tests/iter/mod.rs +++ b/library/coretests/tests/iter/mod.rs diff --git a/library/core/tests/iter/range.rs b/library/coretests/tests/iter/range.rs index d5d2b8bf2b0..d5d2b8bf2b0 100644 --- a/library/core/tests/iter/range.rs +++ b/library/coretests/tests/iter/range.rs diff --git a/library/core/tests/iter/sources.rs b/library/coretests/tests/iter/sources.rs index 506febaa056..506febaa056 100644 --- a/library/core/tests/iter/sources.rs +++ b/library/coretests/tests/iter/sources.rs diff --git a/library/core/tests/iter/traits/accum.rs b/library/coretests/tests/iter/traits/accum.rs index f3eeb31fe58..f3eeb31fe58 100644 --- a/library/core/tests/iter/traits/accum.rs +++ b/library/coretests/tests/iter/traits/accum.rs diff --git a/library/core/tests/iter/traits/double_ended.rs b/library/coretests/tests/iter/traits/double_ended.rs index 00ef4a6e6a9..00ef4a6e6a9 100644 --- a/library/core/tests/iter/traits/double_ended.rs +++ b/library/coretests/tests/iter/traits/double_ended.rs diff --git a/library/core/tests/iter/traits/iterator.rs b/library/coretests/tests/iter/traits/iterator.rs index e31d2e15b6d..e31d2e15b6d 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/coretests/tests/iter/traits/iterator.rs diff --git a/library/core/tests/iter/traits/mod.rs b/library/coretests/tests/iter/traits/mod.rs index 80619f53f25..80619f53f25 100644 --- a/library/core/tests/iter/traits/mod.rs +++ b/library/coretests/tests/iter/traits/mod.rs diff --git a/library/core/tests/iter/traits/step.rs b/library/coretests/tests/iter/traits/step.rs index bf935af397c..bf935af397c 100644 --- a/library/core/tests/iter/traits/step.rs +++ b/library/coretests/tests/iter/traits/step.rs diff --git a/library/core/tests/lazy.rs b/library/coretests/tests/lazy.rs index 32d0ac51f03..32d0ac51f03 100644 --- a/library/core/tests/lazy.rs +++ b/library/coretests/tests/lazy.rs diff --git a/library/core/tests/lib.rs b/library/coretests/tests/lib.rs index a8980c5f30a..f1bbed3de30 100644 --- a/library/core/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -11,9 +11,9 @@ #![feature(async_iter_from_iter)] #![feature(async_iterator)] #![feature(bigint_helper_methods)] +#![feature(bstr)] #![feature(cell_update)] #![feature(clone_to_uninit)] -#![feature(const_black_box)] #![feature(const_eval_select)] #![feature(const_swap_nonoverlapping)] #![feature(const_trait_impl)] @@ -81,7 +81,6 @@ #![feature(strict_provenance_atomic_ptr)] #![feature(strict_provenance_lints)] #![feature(test)] -#![feature(trait_upcasting)] #![feature(trusted_len)] #![feature(trusted_random_access)] #![feature(try_blocks)] @@ -139,6 +138,7 @@ mod asserting; mod async_iter; mod atomic; mod bool; +mod bstr; mod cell; mod char; mod clone; diff --git a/library/core/tests/macros.rs b/library/coretests/tests/macros.rs index b30a40b7df2..b30a40b7df2 100644 --- a/library/core/tests/macros.rs +++ b/library/coretests/tests/macros.rs diff --git a/library/core/tests/macros_bootstrap.rs b/library/coretests/tests/macros_bootstrap.rs index f10ef862c5d..f10ef862c5d 100644 --- a/library/core/tests/macros_bootstrap.rs +++ b/library/coretests/tests/macros_bootstrap.rs diff --git a/library/core/tests/manually_drop.rs b/library/coretests/tests/manually_drop.rs index bbf444471ad..bbf444471ad 100644 --- a/library/core/tests/manually_drop.rs +++ b/library/coretests/tests/manually_drop.rs diff --git a/library/core/tests/mem.rs b/library/coretests/tests/mem.rs index 1b5c5fc82a6..9cb94ca3b0f 100644 --- a/library/core/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -648,7 +648,7 @@ fn offset_of_dst() { z: dyn Trait, } - extern "C" { + unsafe extern "C" { type Extern; } diff --git a/library/core/tests/net/ip_addr.rs b/library/coretests/tests/net/ip_addr.rs index f01b43282ec..f01b43282ec 100644 --- a/library/core/tests/net/ip_addr.rs +++ b/library/coretests/tests/net/ip_addr.rs diff --git a/library/core/tests/net/mod.rs b/library/coretests/tests/net/mod.rs index 8f17bbe5548..8f17bbe5548 100644 --- a/library/core/tests/net/mod.rs +++ b/library/coretests/tests/net/mod.rs diff --git a/library/core/tests/net/parser.rs b/library/coretests/tests/net/parser.rs index e03959ac77c..e03959ac77c 100644 --- a/library/core/tests/net/parser.rs +++ b/library/coretests/tests/net/parser.rs diff --git a/library/core/tests/net/socket_addr.rs b/library/coretests/tests/net/socket_addr.rs index 3d013d37e04..3d013d37e04 100644 --- a/library/core/tests/net/socket_addr.rs +++ b/library/coretests/tests/net/socket_addr.rs diff --git a/library/core/tests/nonzero.rs b/library/coretests/tests/nonzero.rs index 43c279053d8..43c279053d8 100644 --- a/library/core/tests/nonzero.rs +++ b/library/coretests/tests/nonzero.rs diff --git a/library/core/tests/num/bignum.rs b/library/coretests/tests/num/bignum.rs index f213fd5366c..f213fd5366c 100644 --- a/library/core/tests/num/bignum.rs +++ b/library/coretests/tests/num/bignum.rs diff --git a/library/core/tests/num/const_from.rs b/library/coretests/tests/num/const_from.rs index fa58e771879..fa58e771879 100644 --- a/library/core/tests/num/const_from.rs +++ b/library/coretests/tests/num/const_from.rs diff --git a/library/core/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs index 7a9587a18d0..7a9587a18d0 100644 --- a/library/core/tests/num/dec2flt/float.rs +++ b/library/coretests/tests/num/dec2flt/float.rs diff --git a/library/core/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs index f71bbb7c7a3..f71bbb7c7a3 100644 --- a/library/core/tests/num/dec2flt/lemire.rs +++ b/library/coretests/tests/num/dec2flt/lemire.rs diff --git a/library/core/tests/num/dec2flt/mod.rs b/library/coretests/tests/num/dec2flt/mod.rs index 874e0ec7093..874e0ec7093 100644 --- a/library/core/tests/num/dec2flt/mod.rs +++ b/library/coretests/tests/num/dec2flt/mod.rs diff --git a/library/core/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs index 4a5d24ba7d5..4a5d24ba7d5 100644 --- a/library/core/tests/num/dec2flt/parse.rs +++ b/library/coretests/tests/num/dec2flt/parse.rs diff --git a/library/core/tests/num/float_iter_sum_identity.rs b/library/coretests/tests/num/float_iter_sum_identity.rs index 6d3224522a8..6d3224522a8 100644 --- a/library/core/tests/num/float_iter_sum_identity.rs +++ b/library/coretests/tests/num/float_iter_sum_identity.rs diff --git a/library/core/tests/num/flt2dec/estimator.rs b/library/coretests/tests/num/flt2dec/estimator.rs index da203b5f362..da203b5f362 100644 --- a/library/core/tests/num/flt2dec/estimator.rs +++ b/library/coretests/tests/num/flt2dec/estimator.rs diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs index 3d825224813..6041923117c 100644 --- a/library/core/tests/num/flt2dec/mod.rs +++ b/library/coretests/tests/num/flt2dec/mod.rs @@ -80,7 +80,7 @@ fn ldexp_f32(a: f32, b: i32) -> f32 { } fn ldexp_f64(a: f64, b: i32) -> f64 { - extern "C" { + unsafe extern "C" { fn ldexp(x: f64, n: i32) -> f64; } // SAFETY: assuming a correct `ldexp` has been supplied, the given arguments cannot possibly diff --git a/library/core/tests/num/flt2dec/random.rs b/library/coretests/tests/num/flt2dec/random.rs index 99fc23af7ea..90042ae03bf 100644 --- a/library/core/tests/num/flt2dec/random.rs +++ b/library/coretests/tests/num/flt2dec/random.rs @@ -84,9 +84,6 @@ where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>, G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16), { - if cfg!(target_os = "emscripten") { - return; // using rng pulls in i128 support, which doesn't work - } let mut rng = crate::test_rng(); let f32_range = Uniform::new(0x0000_0001u32, 0x7f80_0000); iterate("f32_random_equivalence_test", k, n, f, g, |_| { @@ -100,9 +97,6 @@ where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>, G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16), { - if cfg!(target_os = "emscripten") { - return; // using rng pulls in i128 support, which doesn't work - } let mut rng = crate::test_rng(); let f64_range = Uniform::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000); iterate("f64_random_equivalence_test", k, n, f, g, |_| { diff --git a/library/core/tests/num/flt2dec/strategy/dragon.rs b/library/coretests/tests/num/flt2dec/strategy/dragon.rs index be25fee3f6c..be25fee3f6c 100644 --- a/library/core/tests/num/flt2dec/strategy/dragon.rs +++ b/library/coretests/tests/num/flt2dec/strategy/dragon.rs diff --git a/library/core/tests/num/flt2dec/strategy/grisu.rs b/library/coretests/tests/num/flt2dec/strategy/grisu.rs index 9b2f0453de7..9b2f0453de7 100644 --- a/library/core/tests/num/flt2dec/strategy/grisu.rs +++ b/library/coretests/tests/num/flt2dec/strategy/grisu.rs diff --git a/library/core/tests/num/i128.rs b/library/coretests/tests/num/i128.rs index 745fee05164..745fee05164 100644 --- a/library/core/tests/num/i128.rs +++ b/library/coretests/tests/num/i128.rs diff --git a/library/core/tests/num/i16.rs b/library/coretests/tests/num/i16.rs index 6acb8371b87..6acb8371b87 100644 --- a/library/core/tests/num/i16.rs +++ b/library/coretests/tests/num/i16.rs diff --git a/library/core/tests/num/i32.rs b/library/coretests/tests/num/i32.rs index 38d5071f71d..38d5071f71d 100644 --- a/library/core/tests/num/i32.rs +++ b/library/coretests/tests/num/i32.rs diff --git a/library/core/tests/num/i64.rs b/library/coretests/tests/num/i64.rs index f8dd5f9be7f..f8dd5f9be7f 100644 --- a/library/core/tests/num/i64.rs +++ b/library/coretests/tests/num/i64.rs diff --git a/library/core/tests/num/i8.rs b/library/coretests/tests/num/i8.rs index a10906618c9..a10906618c9 100644 --- a/library/core/tests/num/i8.rs +++ b/library/coretests/tests/num/i8.rs diff --git a/library/core/tests/num/ieee754.rs b/library/coretests/tests/num/ieee754.rs index b0f6a7545aa..b0f6a7545aa 100644 --- a/library/core/tests/num/ieee754.rs +++ b/library/coretests/tests/num/ieee754.rs diff --git a/library/core/tests/num/int_log.rs b/library/coretests/tests/num/int_log.rs index 60902752dab..60902752dab 100644 --- a/library/core/tests/num/int_log.rs +++ b/library/coretests/tests/num/int_log.rs diff --git a/library/core/tests/num/int_macros.rs b/library/coretests/tests/num/int_macros.rs index f13b836378b..f13b836378b 100644 --- a/library/core/tests/num/int_macros.rs +++ b/library/coretests/tests/num/int_macros.rs diff --git a/library/core/tests/num/int_sqrt.rs b/library/coretests/tests/num/int_sqrt.rs index d68db0787d2..d68db0787d2 100644 --- a/library/core/tests/num/int_sqrt.rs +++ b/library/coretests/tests/num/int_sqrt.rs diff --git a/library/core/tests/num/midpoint.rs b/library/coretests/tests/num/midpoint.rs index 71e98006784..71e98006784 100644 --- a/library/core/tests/num/midpoint.rs +++ b/library/coretests/tests/num/midpoint.rs diff --git a/library/core/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 0add9a01e68..0add9a01e68 100644 --- a/library/core/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs diff --git a/library/core/tests/num/nan.rs b/library/coretests/tests/num/nan.rs index ef81988c961..ef81988c961 100644 --- a/library/core/tests/num/nan.rs +++ b/library/coretests/tests/num/nan.rs diff --git a/library/core/tests/num/ops.rs b/library/coretests/tests/num/ops.rs index ae8b938250e..7b2aad48978 100644 --- a/library/core/tests/num/ops.rs +++ b/library/coretests/tests/num/ops.rs @@ -51,9 +51,7 @@ macro_rules! test_op { }; } -test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, f32, f64); -#[cfg(not(target_os = "emscripten"))] -test_op!(test_neg_defined_128, Neg::neg(0), 0, i128); +test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, i128, f32, f64); test_op!(test_not_defined_bool, Not::not(true), false, bool); @@ -69,17 +67,17 @@ macro_rules! test_arith_op { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize, f32, f64 ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method($lhs, $rhs), 0, i128, u128); } }; ($fn_name:ident, $op:ident::$method:ident(&mut $lhs:literal, $rhs:literal)) => { @@ -93,17 +91,17 @@ macro_rules! test_arith_op { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize, f32, f64 ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method(&mut $lhs, $rhs), 0, i128, u128); } }; } @@ -131,15 +129,15 @@ macro_rules! test_bitop { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method(0, 0), 0, i128, u128); impls_defined!($op, $method(false, false), false, bool); } }; @@ -156,15 +154,15 @@ macro_rules! test_bitop_assign { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method(&mut 0, 0), 0, i128, u128); impls_defined!($op, $method(&mut false, false), false, bool); } }; @@ -182,9 +180,11 @@ macro_rules! test_shift_inner { $(impl_defined!($op, $method(0,0), 0, $lt, $rt);)+ }; ($op:ident::$method:ident, $lt:ty) => { - test_shift_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift_inner!($op::$method, $lt, i128, u128); + test_shift_inner!( + $op::$method, $lt, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); }; } @@ -195,9 +195,11 @@ macro_rules! test_shift { ($test_name:ident, $op:ident::$method:ident) => { #[test] fn $test_name() { - test_shift!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift!($op::$method, i128, u128); + test_shift!( + $op::$method, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); } }; } @@ -207,9 +209,11 @@ macro_rules! test_shift_assign_inner { $(impl_defined!($op, $method(&mut 0,0), 0, $lt, $rt);)+ }; ($op:ident::$method:ident, $lt:ty) => { - test_shift_assign_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift_assign_inner!($op::$method, $lt, i128, u128); + test_shift_assign_inner!( + $op::$method, $lt, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); }; } @@ -220,9 +224,11 @@ macro_rules! test_shift_assign { ($test_name:ident, $op:ident::$method:ident) => { #[test] fn $test_name() { - test_shift_assign!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift_assign!($op::$method, i128, u128); + test_shift_assign!( + $op::$method, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); } }; } diff --git a/library/core/tests/num/u128.rs b/library/coretests/tests/num/u128.rs index a7b0f9effef..a7b0f9effef 100644 --- a/library/core/tests/num/u128.rs +++ b/library/coretests/tests/num/u128.rs diff --git a/library/core/tests/num/u16.rs b/library/coretests/tests/num/u16.rs index 010596a34a5..010596a34a5 100644 --- a/library/core/tests/num/u16.rs +++ b/library/coretests/tests/num/u16.rs diff --git a/library/core/tests/num/u32.rs b/library/coretests/tests/num/u32.rs index 687d3bbaa90..687d3bbaa90 100644 --- a/library/core/tests/num/u32.rs +++ b/library/coretests/tests/num/u32.rs diff --git a/library/core/tests/num/u64.rs b/library/coretests/tests/num/u64.rs index ee55071e949..ee55071e949 100644 --- a/library/core/tests/num/u64.rs +++ b/library/coretests/tests/num/u64.rs diff --git a/library/core/tests/num/u8.rs b/library/coretests/tests/num/u8.rs index 12b038ce0f7..12b038ce0f7 100644 --- a/library/core/tests/num/u8.rs +++ b/library/coretests/tests/num/u8.rs diff --git a/library/core/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index 99a2d4cd462..99a2d4cd462 100644 --- a/library/core/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs diff --git a/library/core/tests/num/wrapping.rs b/library/coretests/tests/num/wrapping.rs index c5a71988395..0b9fca8455b 100644 --- a/library/core/tests/num/wrapping.rs +++ b/library/coretests/tests/num/wrapping.rs @@ -64,14 +64,12 @@ wrapping_test!(test_wrapping_i8, i8, i8::MIN, i8::MAX); wrapping_test!(test_wrapping_i16, i16, i16::MIN, i16::MAX); wrapping_test!(test_wrapping_i32, i32, i32::MIN, i32::MAX); wrapping_test!(test_wrapping_i64, i64, i64::MIN, i64::MAX); -#[cfg(not(target_os = "emscripten"))] wrapping_test!(test_wrapping_i128, i128, i128::MIN, i128::MAX); wrapping_test!(test_wrapping_isize, isize, isize::MIN, isize::MAX); wrapping_test!(test_wrapping_u8, u8, u8::MIN, u8::MAX); wrapping_test!(test_wrapping_u16, u16, u16::MIN, u16::MAX); wrapping_test!(test_wrapping_u32, u32, u32::MIN, u32::MAX); wrapping_test!(test_wrapping_u64, u64, u64::MIN, u64::MAX); -#[cfg(not(target_os = "emscripten"))] wrapping_test!(test_wrapping_u128, u128, u128::MIN, u128::MAX); wrapping_test!(test_wrapping_usize, usize, usize::MIN, usize::MAX); diff --git a/library/core/tests/ops.rs b/library/coretests/tests/ops.rs index 501e0f33fe4..501e0f33fe4 100644 --- a/library/core/tests/ops.rs +++ b/library/coretests/tests/ops.rs diff --git a/library/core/tests/ops/control_flow.rs b/library/coretests/tests/ops/control_flow.rs index eacfd63a6c4..eacfd63a6c4 100644 --- a/library/core/tests/ops/control_flow.rs +++ b/library/coretests/tests/ops/control_flow.rs diff --git a/library/core/tests/ops/from_residual.rs b/library/coretests/tests/ops/from_residual.rs index d5c86ccbcd3..d5c86ccbcd3 100644 --- a/library/core/tests/ops/from_residual.rs +++ b/library/coretests/tests/ops/from_residual.rs diff --git a/library/core/tests/option.rs b/library/coretests/tests/option.rs index 336a79a02ce..336a79a02ce 100644 --- a/library/core/tests/option.rs +++ b/library/coretests/tests/option.rs diff --git a/library/core/tests/panic.rs b/library/coretests/tests/panic.rs index 24b6c56b356..24b6c56b356 100644 --- a/library/core/tests/panic.rs +++ b/library/coretests/tests/panic.rs diff --git a/library/core/tests/panic/location.rs b/library/coretests/tests/panic/location.rs index d20241d8380..d20241d8380 100644 --- a/library/core/tests/panic/location.rs +++ b/library/coretests/tests/panic/location.rs diff --git a/library/core/tests/pattern.rs b/library/coretests/tests/pattern.rs index d4bec996d89..d4bec996d89 100644 --- a/library/core/tests/pattern.rs +++ b/library/coretests/tests/pattern.rs diff --git a/library/core/tests/pin.rs b/library/coretests/tests/pin.rs index 026d2ca8de2..026d2ca8de2 100644 --- a/library/core/tests/pin.rs +++ b/library/coretests/tests/pin.rs diff --git a/library/core/tests/pin_macro.rs b/library/coretests/tests/pin_macro.rs index 43542397a61..43542397a61 100644 --- a/library/core/tests/pin_macro.rs +++ b/library/coretests/tests/pin_macro.rs diff --git a/library/core/tests/ptr.rs b/library/coretests/tests/ptr.rs index 7cefb615d03..0c9f9b338b0 100644 --- a/library/core/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -42,11 +42,11 @@ fn test() { let mut v1 = vec![0u16, 0u16, 0u16]; copy(v0.as_ptr().offset(1), v1.as_mut_ptr().offset(1), 1); - assert!((v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16)); + assert!(v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16); copy(v0.as_ptr().offset(2), v1.as_mut_ptr(), 1); - assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 0u16)); + assert!(v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 0u16); copy(v0.as_ptr(), v1.as_mut_ptr().offset(2), 1); - assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 32000u16)); + assert!(v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 32000u16); } } @@ -97,7 +97,7 @@ fn test_is_null() { let nmi: *mut dyn ToString = null_mut::<isize>(); assert!(nmi.is_null()); - extern "C" { + unsafe extern "C" { type Extern; } let ec: *const Extern = null::<Extern>(); @@ -308,7 +308,7 @@ fn test_const_nonnull_new() { pub fn test_variadic_fnptr() { use core::ffi; use core::hash::{Hash, SipHasher}; - extern "C" { + unsafe extern "C" { // This needs to use the correct function signature even though it isn't called as some // codegen backends make it UB to declare a function with multiple conflicting signatures // (like LLVM) while others straight up return an error (like Cranelift). @@ -506,7 +506,7 @@ fn offset_from() { fn ptr_metadata() { struct Unit; struct Pair<A, B: ?Sized>(A, B); - extern "C" { + unsafe extern "C" { type Extern; } let () = metadata(&()); diff --git a/library/core/tests/result.rs b/library/coretests/tests/result.rs index 90ec844bc57..90ec844bc57 100644 --- a/library/core/tests/result.rs +++ b/library/coretests/tests/result.rs diff --git a/library/core/tests/simd.rs b/library/coretests/tests/simd.rs index b8b5f26ca3f..b8b5f26ca3f 100644 --- a/library/core/tests/simd.rs +++ b/library/coretests/tests/simd.rs diff --git a/library/core/tests/slice.rs b/library/coretests/tests/slice.rs index 510dd4967c9..ea5322da381 100644 --- a/library/core/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -2399,18 +2399,18 @@ fn slice_rsplit_once() { assert_eq!(v.rsplit_once(|&x| x == 0), None); } -macro_rules! take_tests { +macro_rules! split_off_tests { (slice: &[], $($tts:tt)*) => { - take_tests!(ty: &[()], slice: &[], $($tts)*); + split_off_tests!(ty: &[()], slice: &[], $($tts)*); }; (slice: &mut [], $($tts:tt)*) => { - take_tests!(ty: &mut [()], slice: &mut [], $($tts)*); + split_off_tests!(ty: &mut [()], slice: &mut [], $($tts)*); }; (slice: &$slice:expr, $($tts:tt)*) => { - take_tests!(ty: &[_], slice: &$slice, $($tts)*); + split_off_tests!(ty: &[_], slice: &$slice, $($tts)*); }; (slice: &mut $slice:expr, $($tts:tt)*) => { - take_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); + split_off_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); }; (ty: $ty:ty, slice: $slice:expr, method: $method:ident, $(($test_name:ident, ($($args:expr),*), $output:expr, $remaining:expr),)*) => { $( @@ -2425,64 +2425,64 @@ macro_rules! take_tests { }; } -take_tests! { - slice: &[0, 1, 2, 3], method: take, - (take_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]), - (take_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]), - (take_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]), - (take_oob_range_to, (..5), None, &[0, 1, 2, 3]), - (take_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]), - (take_oob_range_from, (5..), None, &[0, 1, 2, 3]), +split_off_tests! { + slice: &[0, 1, 2, 3], method: split_off, + (split_off_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]), + (split_off_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]), + (split_off_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]), + (split_off_oob_range_to, (..5), None, &[0, 1, 2, 3]), + (split_off_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]), + (split_off_oob_range_from, (5..), None, &[0, 1, 2, 3]), } -take_tests! { - slice: &mut [0, 1, 2, 3], method: take_mut, - (take_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]), - (take_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]), - (take_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]), - (take_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]), - (take_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]), - (take_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]), +split_off_tests! { + slice: &mut [0, 1, 2, 3], method: split_off_mut, + (split_off_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]), + (split_off_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]), + (split_off_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]), + (split_off_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]), + (split_off_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]), + (split_off_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]), } -take_tests! { - slice: &[1, 2], method: take_first, - (take_first_nonempty, (), Some(&1), &[2]), +split_off_tests! { + slice: &[1, 2], method: split_off_first, + (split_off_first_nonempty, (), Some(&1), &[2]), } -take_tests! { - slice: &mut [1, 2], method: take_first_mut, - (take_first_mut_nonempty, (), Some(&mut 1), &mut [2]), +split_off_tests! { + slice: &mut [1, 2], method: split_off_first_mut, + (split_off_first_mut_nonempty, (), Some(&mut 1), &mut [2]), } -take_tests! { - slice: &[1, 2], method: take_last, - (take_last_nonempty, (), Some(&2), &[1]), +split_off_tests! { + slice: &[1, 2], method: split_off_last, + (split_off_last_nonempty, (), Some(&2), &[1]), } -take_tests! { - slice: &mut [1, 2], method: take_last_mut, - (take_last_mut_nonempty, (), Some(&mut 2), &mut [1]), +split_off_tests! { + slice: &mut [1, 2], method: split_off_last_mut, + (split_off_last_mut_nonempty, (), Some(&mut 2), &mut [1]), } -take_tests! { - slice: &[], method: take_first, - (take_first_empty, (), None, &[]), +split_off_tests! { + slice: &[], method: split_off_first, + (split_off_first_empty, (), None, &[]), } -take_tests! { - slice: &mut [], method: take_first_mut, - (take_first_mut_empty, (), None, &mut []), +split_off_tests! { + slice: &mut [], method: split_off_first_mut, + (split_off_first_mut_empty, (), None, &mut []), } -take_tests! { - slice: &[], method: take_last, - (take_last_empty, (), None, &[]), +split_off_tests! { + slice: &[], method: split_off_last, + (split_off_last_empty, (), None, &[]), } -take_tests! { - slice: &mut [], method: take_last_mut, - (take_last_mut_empty, (), None, &mut []), +split_off_tests! { + slice: &mut [], method: split_off_last_mut, + (split_off_last_mut_empty, (), None, &mut []), } #[cfg(not(miri))] // unused in Miri @@ -2497,19 +2497,19 @@ macro_rules! empty_max_mut { } #[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -take_tests! { - slice: &[(); usize::MAX], method: take, - (take_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]), - (take_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX), - (take_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX), +split_off_tests! { + slice: &[(); usize::MAX], method: split_off, + (split_off_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]), + (split_off_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX), + (split_off_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX), } #[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -take_tests! { - slice: &mut [(); usize::MAX], method: take_mut, - (take_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), - (take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), - (take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), +split_off_tests! { + slice: &mut [(); usize::MAX], method: split_off_mut, + (split_off_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), + (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), + (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), } #[test] diff --git a/library/core/tests/str.rs b/library/coretests/tests/str.rs index f5066343af2..f5066343af2 100644 --- a/library/core/tests/str.rs +++ b/library/coretests/tests/str.rs diff --git a/library/core/tests/str_lossy.rs b/library/coretests/tests/str_lossy.rs index 6e70ea3e285..6e70ea3e285 100644 --- a/library/core/tests/str_lossy.rs +++ b/library/coretests/tests/str_lossy.rs diff --git a/library/core/tests/task.rs b/library/coretests/tests/task.rs index 163b34c9648..163b34c9648 100644 --- a/library/core/tests/task.rs +++ b/library/coretests/tests/task.rs diff --git a/library/core/tests/time.rs b/library/coretests/tests/time.rs index fe7bb11c675..fe7bb11c675 100644 --- a/library/core/tests/time.rs +++ b/library/coretests/tests/time.rs diff --git a/library/core/tests/tuple.rs b/library/coretests/tests/tuple.rs index ea1e281425c..ea1e281425c 100644 --- a/library/core/tests/tuple.rs +++ b/library/coretests/tests/tuple.rs diff --git a/library/core/tests/unicode.rs b/library/coretests/tests/unicode.rs index bbace0ef66c..bbace0ef66c 100644 --- a/library/core/tests/unicode.rs +++ b/library/coretests/tests/unicode.rs diff --git a/library/core/tests/waker.rs b/library/coretests/tests/waker.rs index 4889b8959ec..4889b8959ec 100644 --- a/library/core/tests/waker.rs +++ b/library/coretests/tests/waker.rs diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index dc2b42bb90a..7718d68aef8 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -54,7 +54,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { ))] { unsafe fn abort() -> ! { // call std::sys::abort_internal - extern "C" { + unsafe extern "C" { pub fn __rust_abort() -> !; } __rust_abort(); @@ -87,7 +87,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { } } else if #[cfg(target_os = "teeos")] { mod teeos { - extern "C" { + unsafe extern "C" { pub fn TEE_Panic(code: u32) -> !; } } diff --git a/library/panic_abort/src/zkvm.rs b/library/panic_abort/src/zkvm.rs index a6a02abf109..11150eafd0b 100644 --- a/library/panic_abort/src/zkvm.rs +++ b/library/panic_abort/src/zkvm.rs @@ -16,7 +16,7 @@ pub(crate) unsafe fn zkvm_set_abort_message(payload: &mut dyn PanicPayload) { return; } - extern "C" { + unsafe extern "C" { fn sys_panic(msg_ptr: *const u8, len: usize) -> !; } diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index 9127449edb1..4140b004ad1 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -21,7 +21,7 @@ struct TypeInfo { } unsafe impl Sync for TypeInfo {} -extern "C" { +unsafe extern "C" { // The leading `\x01` byte here is actually a magical signal to LLVM to // *not* apply any other mangling like prefixing with a `_` character. // @@ -98,11 +98,14 @@ pub(crate) unsafe fn panic(data: Box<dyn Any + Send>) -> u32 { if exception.is_null() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } - ptr::write(exception, Exception { - canary: &EXCEPTION_TYPE_INFO, - caught: AtomicBool::new(false), - data: Some(data), - }); + ptr::write( + exception, + Exception { + canary: &EXCEPTION_TYPE_INFO, + caught: AtomicBool::new(false), + data: Some(data), + }, + ); __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup); } @@ -116,7 +119,7 @@ extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void { } } -extern "C" { +unsafe extern "C" { fn __cxa_allocate_exception(thrown_size: libc::size_t) -> *mut libc::c_void; fn __cxa_begin_catch(thrown_exception: *mut libc::c_void) -> *mut libc::c_void; fn __cxa_end_catch(); diff --git a/library/panic_unwind/src/hermit.rs b/library/panic_unwind/src/hermit.rs index 8ac827dd9cc..9719c133415 100644 --- a/library/panic_unwind/src/hermit.rs +++ b/library/panic_unwind/src/hermit.rs @@ -6,15 +6,15 @@ use alloc::boxed::Box; use core::any::Any; pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box<dyn Any + Send> { - extern "C" { - pub fn __rust_abort() -> !; + unsafe extern "C" { + fn __rust_abort() -> !; } __rust_abort(); } pub(crate) unsafe fn panic(_data: Box<dyn Any + Send>) -> u32 { - extern "C" { - pub fn __rust_abort() -> !; + unsafe extern "C" { + fn __rust_abort() -> !; } __rust_abort(); } diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index d6828164195..45e2a466b4d 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -75,7 +75,7 @@ cfg_if::cfg_if! { } } -extern "C" { +unsafe extern "C" { /// Handler in std called when a panic object is dropped outside of /// `catch_unwind`. fn __rust_drop_panic() -> !; diff --git a/library/panic_unwind/src/miri.rs b/library/panic_unwind/src/miri.rs index a86f0e91eef..ec48b1105ab 100644 --- a/library/panic_unwind/src/miri.rs +++ b/library/panic_unwind/src/miri.rs @@ -7,7 +7,7 @@ use core::any::Any; // Must be pointer-sized. type Payload = Box<Box<dyn Any + Send>>; -extern "Rust" { +unsafe extern "Rust" { /// Miri-provided extern function to begin unwinding. fn miri_start_unwind(payload: *mut u8) -> !; } diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 21bfe74e1a2..c8dfddf821e 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -135,7 +135,7 @@ mod imp { #[derive(Copy, Clone)] pub(super) struct ptr_t(u32); - extern "C" { + unsafe extern "C" { static __ImageBase: u8; } @@ -229,7 +229,7 @@ static mut CATCHABLE_TYPE: _CatchableType = _CatchableType { copyFunction: ptr_t::null(), }; -extern "C" { +unsafe extern "C" { // The leading `\x01` byte here is actually a magical signal to LLVM to // *not* apply any other mangling like prefixing with a `_` character. // @@ -343,7 +343,7 @@ pub(crate) unsafe fn panic(data: Box<dyn Any + Send>) -> u32 { ptr_t::new(exception_copy as *mut u8).raw(), ); - extern "system-unwind" { + unsafe extern "system-unwind" { fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; } diff --git a/library/portable-simd/.github/workflows/ci.yml b/library/portable-simd/.github/workflows/ci.yml index b292be2d6f9..3984d8f0d8d 100644 --- a/library/portable-simd/.github/workflows/ci.yml +++ b/library/portable-simd/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: env: CARGO_NET_RETRY: 10 RUSTUP_MAX_RETRIES: 10 + PROPTEST_CASES: 64 jobs: rustfmt: @@ -16,12 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup component add rustfmt + - uses: actions/checkout@v4 - name: Run rustfmt run: cargo fmt --all -- --check @@ -37,7 +33,9 @@ jobs: - i686-unknown-linux-gnu - i586-unknown-linux-gnu - aarch64-unknown-linux-gnu + - arm64ec-pc-windows-msvc - armv7-unknown-linux-gnueabihf + - loongarch64-unknown-linux-gnu # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu # - mips64-unknown-linux-gnuabi64 @@ -49,13 +47,9 @@ jobs: - wasm32-unknown-unknown steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} - rustup component add clippy + run: rustup target add ${{ matrix.target }} - name: Run Clippy run: cargo clippy --all-targets --target ${{ matrix.target }} @@ -65,26 +59,19 @@ jobs: strategy: fail-fast: false matrix: - target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu, x86_64-apple-darwin] + target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu] # `default` means we use the default target config for the target, # `native` means we run with `-Ctarget-cpu=native`, and anything else is # an arg to `-Ctarget-feature` target_feature: [default, native, +sse3, +ssse3, +sse4.1, +sse4.2, +avx, +avx2] exclude: - # The macos runners seem to only reliably support up to `avx`. - - { target: x86_64-apple-darwin, target_feature: +avx2 } - # These features are statically known to be present for all 64 bit - # macs, and thus are covered by the `default` test - - { target: x86_64-apple-darwin, target_feature: +sse3 } - - { target: x86_64-apple-darwin, target_feature: +ssse3 } # -Ctarget-cpu=native sounds like bad-news if target != host - { target: i686-pc-windows-msvc, target_feature: native } - { target: i586-pc-windows-msvc, target_feature: native } include: # Populate the `matrix.os` field - - { target: x86_64-apple-darwin, os: macos-latest } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } - { target: x86_64-pc-windows-msvc, os: windows-latest } - { target: i686-pc-windows-msvc, os: windows-latest } @@ -98,12 +85,9 @@ jobs: # avx512vl, but occasionally doesn't. Maybe one day we can enable it. steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} + run: rustup target add ${{ matrix.target }} - name: Configure RUSTFLAGS shell: bash @@ -145,6 +129,35 @@ jobs: run: cargo doc --verbose --target=${{ matrix.target }} env: RUSTDOCFLAGS: -Dwarnings + + macos-tests: + name: ${{ matrix.target }} + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + target: + - aarch64-apple-darwin + - x86_64-apple-darwin + steps: + - uses: actions/checkout@v4 + - name: Setup Rust + run: rustup target add ${{ matrix.target }} + + - name: Configure RUSTFLAGS + shell: bash + run: echo "RUSTFLAGS=-Dwarnings" >> $GITHUB_ENV + + - name: Test (debug) + run: cargo test --verbose --target=${{ matrix.target }} + + - name: Test (release) + run: cargo test --verbose --target=${{ matrix.target }} --release + + - name: Generate docs + run: cargo doc --verbose --target=${{ matrix.target }} + env: + RUSTDOCFLAGS: -Dwarnings wasm-tests: name: "wasm (firefox, ${{ matrix.name }})" @@ -155,11 +168,7 @@ jobs: - { name: default, RUSTFLAGS: "" } - { name: simd128, RUSTFLAGS: "-C target-feature=+simd128" } steps: - - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly + - uses: actions/checkout@v4 - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Test (debug) @@ -174,6 +183,8 @@ jobs: cross-tests: name: "${{ matrix.target_feature }} on ${{ matrix.target }} (via cross)" runs-on: ubuntu-latest + env: + PROPTEST_CASES: 16 strategy: fail-fast: false @@ -185,6 +196,7 @@ jobs: - powerpc-unknown-linux-gnu - powerpc64le-unknown-linux-gnu # includes altivec by default - riscv64gc-unknown-linux-gnu + - loongarch64-unknown-linux-gnu # MIPS uses a nonstandard binary representation for NaNs which makes it worth testing # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu @@ -201,24 +213,14 @@ jobs: # - { target: riscv64gc-unknown-linux-gnu, target_feature: "+v,+zvl128b" } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} - rustup component add rust-src + run: rustup target add ${{ matrix.target }} - name: Install Cross - # Equivalent to `cargo install cross`, but downloading a prebuilt - # binary. Ideally we wouldn't hardcode a version, but the version number - # being part of the tarball means we can't just use the download/latest - # URL :( + # Install the latest git version for newer targets. run: | - CROSS_URL=https://github.com/cross-rs/cross/releases/download/v0.2.5/cross-x86_64-unknown-linux-gnu.tar.gz - mkdir -p "$HOME/.bin" - curl -sfSL --retry-delay 10 --retry 5 "${CROSS_URL}" | tar zxf - -C "$HOME/.bin" - echo "$HOME/.bin" >> $GITHUB_PATH + cargo install cross --git https://github.com/cross-rs/cross --rev 4090beca3cfffa44371a5bba524de3a578aa46c3 - name: Configure Emulated CPUs run: | @@ -242,34 +244,11 @@ jobs: - name: Test (release) run: cross test --verbose --target=${{ matrix.target }} --release - features: - name: "Test cargo features (${{ matrix.simd }} × ${{ matrix.features }})" + miri: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - simd: - - "" - - "avx512" - features: - - "" - - "--features std" - - "--features all_lane_counts" - - "--all-features" - + env: + PROPTEST_CASES: 16 steps: - - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - - name: Detect AVX512 - run: echo "CPU_FEATURE=$(lscpu | grep -o avx512[a-z]* | sed s/avx/+avx/ | tr '\n' ',' )" >> $GITHUB_ENV - - name: Check build - if: ${{ matrix.simd == '' }} - run: RUSTFLAGS="-Dwarnings" cargo test --all-targets --no-default-features ${{ matrix.features }} - - name: Check AVX - if: ${{ matrix.simd == 'avx512' && contains(env.CPU_FEATURE, 'avx512') }} - run: | - echo "Found AVX features: $CPU_FEATURE" - RUSTFLAGS="-Dwarnings -Ctarget-feature=$CPU_FEATURE" cargo test --all-targets --no-default-features ${{ matrix.features }} + - uses: actions/checkout@v4 + - name: Test (Miri) + run: cargo miri test diff --git a/library/portable-simd/.github/workflows/doc.yml b/library/portable-simd/.github/workflows/doc.yml index 9d1fa66ccb5..22c2cb3f67f 100644 --- a/library/portable-simd/.github/workflows/doc.yml +++ b/library/portable-simd/.github/workflows/doc.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Setup Rust run: | diff --git a/library/portable-simd/.gitignore b/library/portable-simd/.gitignore index ea8c4bf7f35..9673e52dcad 100644 --- a/library/portable-simd/.gitignore +++ b/library/portable-simd/.gitignore @@ -1 +1,2 @@ /target +git-subtree.sh diff --git a/library/portable-simd/Cargo.toml b/library/portable-simd/Cargo.toml index d1732aaec2f..21d4584a9f4 100644 --- a/library/portable-simd/Cargo.toml +++ b/library/portable-simd/Cargo.toml @@ -5,3 +5,9 @@ members = [ "crates/std_float", "crates/test_helpers", ] + +[profile.test.package."*"] +opt-level = 2 + +[profile.test.package.test_helpers] +opt-level = 2 diff --git a/library/portable-simd/Cross.toml b/library/portable-simd/Cross.toml new file mode 100644 index 00000000000..d21e76b92dd --- /dev/null +++ b/library/portable-simd/Cross.toml @@ -0,0 +1,2 @@ +[build.env] +passthrough = ["PROPTEST_CASES"] diff --git a/library/portable-simd/crates/core_simd/Cargo.toml b/library/portable-simd/crates/core_simd/Cargo.toml index b4a8fd70f4c..a7a6d43b11d 100644 --- a/library/portable-simd/crates/core_simd/Cargo.toml +++ b/library/portable-simd/crates/core_simd/Cargo.toml @@ -9,10 +9,9 @@ categories = ["hardware-support", "no-std"] license = "MIT OR Apache-2.0" [features] -default = ["as_crate"] +default = ["as_crate", "std"] as_crate = [] std = [] -all_lane_counts = [] [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen = "0.2" diff --git a/library/portable-simd/crates/core_simd/src/lane_count.rs b/library/portable-simd/crates/core_simd/src/lane_count.rs index 4cd7265ed67..280b27bc9bc 100644 --- a/library/portable-simd/crates/core_simd/src/lane_count.rs +++ b/library/portable-simd/crates/core_simd/src/lane_count.rs @@ -33,10 +33,8 @@ macro_rules! supported_lane_count { }; } -supported_lane_count!(1, 2, 4, 8, 16, 32, 64); -#[cfg(feature = "all_lane_counts")] supported_lane_count!( - 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 ); diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index 992a7705e3c..7f57847c9c2 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![feature( - const_refs_to_cell, - const_mut_refs, + const_eval_select, convert_float_to_int, core_intrinsics, decl_macro, @@ -26,6 +25,7 @@ all(target_arch = "arm", target_feature = "v7"), feature(stdarch_arm_neon_intrinsics) )] +#![cfg_attr(target_arch = "loongarch64", feature(stdarch_loongarch))] #![cfg_attr( any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index 04de3a96827..b763a7c75a5 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -308,48 +308,6 @@ where Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } - /// Creates a bitmask vector from a mask. - /// - /// Each bit is set if the corresponding element in the mask is `true`. - /// The remaining bits are unset. - /// - /// The bits are packed into the first N bits of the vector: - /// ``` - /// # #![feature(portable_simd)] - /// # #[cfg(feature = "as_crate")] use core_simd::simd; - /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::mask32x8; - /// let mask = mask32x8::from_array([true, false, true, false, false, false, true, false]); - /// assert_eq!(mask.to_bitmask_vector()[0], 0b01000101); - /// ``` - #[inline] - #[must_use = "method returns a new integer and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd<u8, N> { - self.0.to_bitmask_vector() - } - - /// Creates a mask from a bitmask vector. - /// - /// For each bit, if it is set, the corresponding element in the mask is set to `true`. - /// - /// The bits are packed into the first N bits of the vector: - /// ``` - /// # #![feature(portable_simd)] - /// # #[cfg(feature = "as_crate")] use core_simd::simd; - /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{mask32x8, u8x8}; - /// let bitmask = u8x8::from_array([0b01000101, 0, 0, 0, 0, 0, 0, 0]); - /// assert_eq!( - /// mask32x8::from_bitmask_vector(bitmask), - /// mask32x8::from_array([true, false, true, false, false, false, true, false]), - /// ); - /// ``` - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd<u8, N>) -> Self { - Self(mask_impl::Mask::from_bitmask_vector(bitmask)) - } - /// Finds the index of the first set element. /// /// ``` diff --git a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs index 96c553426ee..db4312d5bf8 100644 --- a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs +++ b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs @@ -123,23 +123,6 @@ where } #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd<u8, N> { - let mut bitmask = Simd::splat(0); - bitmask.as_mut_array()[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); - bitmask - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd<u8, N>) -> Self { - let mut bytes = <LaneCount<N> as SupportedLaneCount>::BitMask::default(); - let len = bytes.as_ref().len(); - bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); - Self(bytes, PhantomData) - } - - #[inline] pub fn to_bitmask_integer(self) -> u64 { let mut bitmask = [0u8; 8]; bitmask[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); diff --git a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs index 87f031a9f36..2d01946b574 100644 --- a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs @@ -141,62 +141,6 @@ where } #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd<u8, N> { - let mut bitmask = Simd::splat(0); - - // Safety: Bytes is the right size array - unsafe { - // Compute the bitmask - let mut bytes: <LaneCount<N> as SupportedLaneCount>::BitMask = - core::intrinsics::simd::simd_bitmask(self.0); - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - for x in bytes.as_mut() { - *x = x.reverse_bits() - } - if N % 8 > 0 { - bytes.as_mut()[N / 8] >>= 8 - N % 8; - } - } - - bitmask.as_mut_array()[..bytes.as_ref().len()].copy_from_slice(bytes.as_ref()); - } - - bitmask - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd<u8, N>) -> Self { - let mut bytes = <LaneCount<N> as SupportedLaneCount>::BitMask::default(); - - // Safety: Bytes is the right size array - unsafe { - let len = bytes.as_ref().len(); - bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - for x in bytes.as_mut() { - *x = x.reverse_bits(); - } - if N % 8 > 0 { - bytes.as_mut()[N / 8] >>= 8 - N % 8; - } - } - - // Compute the regular mask - Self::from_int_unchecked(core::intrinsics::simd::simd_select_bitmask( - bytes, - Self::splat(true).to_int(), - Self::splat(false).to_int(), - )) - } - } - - #[inline] unsafe fn to_bitmask_impl<U: ReverseBits, const M: usize>(self) -> U where LaneCount<M>: SupportedLaneCount, @@ -283,7 +227,7 @@ where } #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] + #[must_use = "method returns a new bool and does not mutate the original value"] pub fn all(self) -> bool { // Safety: use `self` as an integer vector unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index dd7303a97b1..d3bd14a3402 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -77,7 +77,7 @@ macro_rules! int_divrem_guard { ( $lhs:ident, $rhs:ident, { const PANIC_ZERO: &'static str = $zero:literal; - $simd_call:ident + $simd_call:ident, $op:tt }, $int:ident ) => { if $rhs.simd_eq(Simd::splat(0 as _)).any() { @@ -96,8 +96,23 @@ macro_rules! int_divrem_guard { // Nice base case to make it easy to const-fold away the other branch. $rhs }; - // Safety: $lhs and rhs are vectors - unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) } + + // aarch64 div fails for arbitrary `v % 0`, mod fails when rhs is MIN, for non-powers-of-two + // these operations aren't vectorized on aarch64 anyway + #[cfg(target_arch = "aarch64")] + { + let mut out = Simd::splat(0 as _); + for i in 0..Self::LEN { + out[i] = $lhs[i] $op rhs[i]; + } + out + } + + #[cfg(not(target_arch = "aarch64"))] + { + // Safety: $lhs and rhs are vectors + unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) } + } } }; } @@ -205,14 +220,14 @@ for_base_ops! { impl Div::div { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to divide by zero"; - simd_div + simd_div, / } } impl Rem::rem { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to calculate the remainder with a divisor of zero"; - simd_rem + simd_rem, % } } diff --git a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs index 5b4615ce51d..93989ce91b8 100644 --- a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs +++ b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs @@ -12,7 +12,7 @@ pub trait SimdPartialEq { #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_eq(self, other: Self) -> Self::Mask; - /// Test if each element is equal to the corresponding element in `other`. + /// Test if each element is not equal to the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_ne(self, other: Self) -> Self::Mask; } diff --git a/library/portable-simd/crates/core_simd/src/simd/num/float.rs b/library/portable-simd/crates/core_simd/src/simd/num/float.rs index 59e43851ea8..79954b937b3 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/float.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/float.rs @@ -255,6 +255,7 @@ macro_rules! impl_trait { type Bits = Simd<$bits_ty, N>; type Cast<T: SimdElement> = Simd<T, N>; + #[cfg(not(target_arch = "aarch64"))] #[inline] fn cast<T: SimdCast>(self) -> Self::Cast<T> { @@ -262,6 +263,33 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_as(self) } } + // https://github.com/llvm/llvm-project/issues/94694 + #[cfg(target_arch = "aarch64")] + #[inline] + fn cast<T: SimdCast>(self) -> Self::Cast<T> + { + const { assert!(N <= 64) }; + if N <= 2 || N == 4 || N == 8 || N == 16 || N == 32 || N == 64 { + // Safety: supported types are guaranteed by SimdCast + unsafe { core::intrinsics::simd::simd_as(self) } + } else if N < 4 { + let x = self.resize::<4>(Default::default()).cast(); + x.resize::<N>(x[0]) + } else if N < 8 { + let x = self.resize::<8>(Default::default()).cast(); + x.resize::<N>(x[0]) + } else if N < 16 { + let x = self.resize::<16>(Default::default()).cast(); + x.resize::<N>(x[0]) + } else if N < 32 { + let x = self.resize::<32>(Default::default()).cast(); + x.resize::<N>(x[0]) + } else { + let x = self.resize::<64>(Default::default()).cast(); + x.resize::<N>(x[0]) + } + } + #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces unsafe fn to_int_unchecked<I: SimdCast>(self) -> Self::Cast<I> @@ -391,7 +419,7 @@ macro_rules! impl_trait { self.as_array().iter().sum() } else { // Safety: `self` is a float vector - unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0.) } + unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, -0.) } } } diff --git a/library/portable-simd/crates/core_simd/src/simd/num/int.rs b/library/portable-simd/crates/core_simd/src/simd/num/int.rs index d7598d9ceaf..3a51235ff95 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/int.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/int.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, + cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, }; @@ -70,11 +70,27 @@ pub trait SimdInt: Copy + Sealed { /// # #[cfg(not(feature = "as_crate"))] use core::simd; /// # use simd::prelude::*; /// use core::i32::{MIN, MAX}; - /// let xs = Simd::from_array([MIN, MIN +1, -5, 0]); + /// let xs = Simd::from_array([MIN, MIN + 1, -5, 0]); /// assert_eq!(xs.abs(), Simd::from_array([MIN, MAX, 5, 0])); /// ``` fn abs(self) -> Self; + /// Lanewise absolute difference. + /// Every element becomes the absolute difference of `self` and `second`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::prelude::*; + /// use core::i32::{MIN, MAX}; + /// let a = Simd::from_array([MIN, MAX, 100, -100]); + /// let b = Simd::from_array([MAX, MIN, -80, -120]); + /// assert_eq!(a.abs_diff(b), Simd::from_array([u32::MAX, u32::MAX, 180, 20])); + /// ``` + fn abs_diff(self, second: Self) -> Self::Unsigned; + /// Lanewise saturating absolute value, implemented in Rust. /// As abs(), except the MIN value becomes MAX instead of itself. /// @@ -203,6 +219,12 @@ pub trait SimdInt: Copy + Sealed { /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. fn reverse_bits(self) -> Self; + /// Returns the number of ones in the binary representation of each element. + fn count_ones(self) -> Self::Unsigned; + + /// Returns the number of zeros in the binary representation of each element. + fn count_zeros(self) -> Self::Unsigned; + /// Returns the number of leading zeros in the binary representation of each element. fn leading_zeros(self) -> Self::Unsigned; @@ -260,6 +282,13 @@ macro_rules! impl_trait { } #[inline] + fn abs_diff(self, second: Self) -> Self::Unsigned { + let max = self.simd_max(second); + let min = self.simd_min(second); + (max - min).cast() + } + + #[inline] fn saturating_abs(self) -> Self { // arith shift for -1 or 0 mask based on sign bit, giving 2s complement const SHR: $ty = <$ty>::BITS as $ty - 1; @@ -345,6 +374,16 @@ macro_rules! impl_trait { } #[inline] + fn count_ones(self) -> Self::Unsigned { + self.cast::<$unsigned>().count_ones() + } + + #[inline] + fn count_zeros(self) -> Self::Unsigned { + self.cast::<$unsigned>().count_zeros() + } + + #[inline] fn leading_zeros(self) -> Self::Unsigned { self.cast::<$unsigned>().leading_zeros() } diff --git a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs index 53dd97f501c..1ab2d8c7b73 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{cmp::SimdOrd, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { @@ -57,6 +57,22 @@ pub trait SimdUint: Copy + Sealed { /// assert_eq!(sat, Simd::splat(0)); fn saturating_sub(self, second: Self) -> Self; + /// Lanewise absolute difference. + /// Every element becomes the absolute difference of `self` and `second`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::prelude::*; + /// use core::u32::MAX; + /// let a = Simd::from_array([0, MAX, 100, 20]); + /// let b = Simd::from_array([MAX, 0, 80, 200]); + /// assert_eq!(a.abs_diff(b), Simd::from_array([MAX, MAX, 20, 180])); + /// ``` + fn abs_diff(self, second: Self) -> Self; + /// Returns the sum of the elements of the vector, with wrapping addition. fn reduce_sum(self) -> Self::Scalar; @@ -85,6 +101,12 @@ pub trait SimdUint: Copy + Sealed { /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. fn reverse_bits(self) -> Self; + /// Returns the number of ones in the binary representation of each element. + fn count_ones(self) -> Self; + + /// Returns the number of zeros in the binary representation of each element. + fn count_zeros(self) -> Self; + /// Returns the number of leading zeros in the binary representation of each element. fn leading_zeros(self) -> Self; @@ -139,6 +161,13 @@ macro_rules! impl_trait { } #[inline] + fn abs_diff(self, second: Self) -> Self { + let max = self.simd_max(second); + let min = self.simd_min(second); + max - min + } + + #[inline] fn reduce_sum(self) -> Self::Scalar { // Safety: `self` is an integer vector unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0) } @@ -193,6 +222,17 @@ macro_rules! impl_trait { } #[inline] + fn count_ones(self) -> Self { + // Safety: `self` is an integer vector + unsafe { core::intrinsics::simd::simd_ctpop(self) } + } + + #[inline] + fn count_zeros(self) -> Self { + (!self).count_ones() + } + + #[inline] fn leading_zeros(self) -> Self { // Safety: `self` is an integer vector unsafe { core::intrinsics::simd::simd_ctlz(self) } diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index be635ea640b..47383809ffb 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -42,6 +42,19 @@ pub trait SimdConstPtr: Copy + Sealed { /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; + /// Converts an address to a pointer without giving it any provenance. + /// + /// Without provenance, this pointer is not associated with any actual allocation. Such a + /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but + /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers + /// are little more than a usize address in disguise. + /// + /// This is different from [`Self::with_exposed_provenance`], which creates a pointer that picks up a + /// previously exposed provenance. + /// + /// Equivalent to calling [`core::ptr::without_provenance`] on each element. + fn without_provenance(addr: Self::Usize) -> Self; + /// Creates a new pointer with the given address. /// /// This performs the same operation as a cast, but copies the *address-space* and @@ -119,6 +132,14 @@ where } #[inline] + fn without_provenance(addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Integer-to-pointer transmutes are valid (if you are okay with not getting any + // provenance). + unsafe { core::mem::transmute_copy(&addr) } + } + + #[inline] fn with_addr(self, addr: Self::Usize) -> Self { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index f6823a949e3..3f20eef21a3 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -39,6 +39,19 @@ pub trait SimdMutPtr: Copy + Sealed { /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; + /// Converts an address to a pointer without giving it any provenance. + /// + /// Without provenance, this pointer is not associated with any actual allocation. Such a + /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but + /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers + /// are little more than a usize address in disguise. + /// + /// This is different from [`Self::with_exposed_provenance`], which creates a pointer that picks up a + /// previously exposed provenance. + /// + /// Equivalent to calling [`core::ptr::without_provenance`] on each element. + fn without_provenance(addr: Self::Usize) -> Self; + /// Creates a new pointer with the given address. /// /// This performs the same operation as a cast, but copies the *address-space* and @@ -116,6 +129,14 @@ where } #[inline] + fn without_provenance(addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Integer-to-pointer transmutes are valid (if you are okay with not getting any + // provenance). + unsafe { core::mem::transmute_copy(&addr) } + } + + #[inline] fn with_addr(self, addr: Self::Usize) -> Self { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. // diff --git a/library/portable-simd/crates/core_simd/src/swizzle.rs b/library/portable-simd/crates/core_simd/src/swizzle.rs index d62642fb906..42425ef37e5 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle.rs @@ -155,8 +155,7 @@ pub trait Swizzle<const N: usize> { /// Creates a new mask from the elements of `mask`. /// - /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of - /// `first` and `second`. + /// Element `i` of the output is `mask[Self::INDEX[i]]`. #[inline] #[must_use = "method returns a new mask and does not mutate the original inputs"] fn swizzle_mask<T, const M: usize>(mask: Mask<T, M>) -> Mask<T, N> @@ -260,6 +259,50 @@ where Rotate::<OFFSET>::swizzle(self) } + /// Shifts the vector elements to the left by `OFFSET`, filling in with + /// `padding` from the right. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn shift_elements_left<const OFFSET: usize>(self, padding: T) -> Self { + struct Shift<const OFFSET: usize>; + + impl<const OFFSET: usize, const N: usize> Swizzle<N> for Shift<OFFSET> { + const INDEX: [usize; N] = const { + let mut index = [N; N]; + let mut i = 0; + while i + OFFSET < N { + index[i] = i + OFFSET; + i += 1; + } + index + }; + } + + Shift::<OFFSET>::concat_swizzle(self, Simd::splat(padding)) + } + + /// Shifts the vector elements to the right by `OFFSET`, filling in with + /// `padding` from the left. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn shift_elements_right<const OFFSET: usize>(self, padding: T) -> Self { + struct Shift<const OFFSET: usize>; + + impl<const OFFSET: usize, const N: usize> Swizzle<N> for Shift<OFFSET> { + const INDEX: [usize; N] = const { + let mut index = [N; N]; + let mut i = OFFSET; + while i < N { + index[i] = i - OFFSET; + i += 1; + } + index + }; + } + + Shift::<OFFSET>::concat_swizzle(self, Simd::splat(padding)) + } + /// Interleave two vectors. /// /// The resulting vectors contain elements taken alternatively from `self` and `other`, first @@ -320,7 +363,9 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::Simd; + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; /// let a = Simd::from_array([0, 4, 1, 5]); /// let b = Simd::from_array([2, 6, 3, 7]); /// let (x, y) = a.deinterleave(b); @@ -391,4 +436,210 @@ where } Resize::<N>::concat_swizzle(self, Simd::splat(value)) } + + /// Extract a vector from another vector. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::u32x4; + /// let x = u32x4::from_array([0, 1, 2, 3]); + /// assert_eq!(x.extract::<1, 2>().to_array(), [1, 2]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn extract<const START: usize, const LEN: usize>(self) -> Simd<T, LEN> + where + LaneCount<LEN>: SupportedLaneCount, + { + struct Extract<const N: usize, const START: usize>; + impl<const N: usize, const START: usize, const LEN: usize> Swizzle<LEN> for Extract<N, START> { + const INDEX: [usize; LEN] = const { + assert!(START + LEN <= N, "index out of bounds"); + let mut index = [0; LEN]; + let mut i = 0; + while i < LEN { + index[i] = START + i; + i += 1; + } + index + }; + } + Extract::<N, START>::swizzle(self) + } +} + +impl<T, const N: usize> Mask<T, N> +where + T: MaskElement, + LaneCount<N>: SupportedLaneCount, +{ + /// Reverse the order of the elements in the mask. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn reverse(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().reverse()) } + } + + /// Rotates the mask such that the first `OFFSET` elements of the slice move to the end + /// while the last `self.len() - OFFSET` elements move to the front. After calling `rotate_elements_left`, + /// the element previously at index `OFFSET` will become the first element in the slice. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn rotate_elements_left<const OFFSET: usize>(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_left::<OFFSET>()) } + } + + /// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to + /// the end while the last `OFFSET` elements move to the front. After calling `rotate_elements_right`, + /// the element previously at index `self.len() - OFFSET` will become the first element in the slice. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn rotate_elements_right<const OFFSET: usize>(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::<OFFSET>()) } + } + + /// Shifts the mask elements to the left by `OFFSET`, filling in with + /// `padding` from the right. + #[inline] + #[must_use = "method returns a new mask and does not mutate the original inputs"] + pub fn shift_elements_left<const OFFSET: usize>(self, padding: bool) -> Self { + // Safety: swizzles are safe for masks + unsafe { + Self::from_int_unchecked(self.to_int().shift_elements_left::<OFFSET>(if padding { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Shifts the mask elements to the right by `OFFSET`, filling in with + /// `padding` from the left. + #[inline] + #[must_use = "method returns a new mask and does not mutate the original inputs"] + pub fn shift_elements_right<const OFFSET: usize>(self, padding: bool) -> Self { + // Safety: swizzles are safe for masks + unsafe { + Self::from_int_unchecked(self.to_int().shift_elements_right::<OFFSET>(if padding { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Interleave two masks. + /// + /// The resulting masks contain elements taken alternatively from `self` and `other`, first + /// filling the first result, and then the second. + /// + /// The reverse of this operation is [`Mask::deinterleave`]. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let a = mask32x4::from_array([false, true, false, true]); + /// let b = mask32x4::from_array([false, false, true, true]); + /// let (x, y) = a.interleave(b); + /// assert_eq!(x.to_array(), [false, false, true, false]); + /// assert_eq!(y.to_array(), [false, true, true, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn interleave(self, other: Self) -> (Self, Self) { + let (lo, hi) = self.to_int().interleave(other.to_int()); + // Safety: swizzles are safe for masks + unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) } + } + + /// Deinterleave two masks. + /// + /// The first result takes every other element of `self` and then `other`, starting with + /// the first element. + /// + /// The second result takes every other element of `self` and then `other`, starting with + /// the second element. + /// + /// The reverse of this operation is [`Mask::interleave`]. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let a = mask32x4::from_array([false, true, false, true]); + /// let b = mask32x4::from_array([false, false, true, true]); + /// let (x, y) = a.deinterleave(b); + /// assert_eq!(x.to_array(), [false, false, false, true]); + /// assert_eq!(y.to_array(), [true, true, false, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn deinterleave(self, other: Self) -> (Self, Self) { + let (even, odd) = self.to_int().deinterleave(other.to_int()); + // Safety: swizzles are safe for masks + unsafe { + ( + Self::from_int_unchecked(even), + Self::from_int_unchecked(odd), + ) + } + } + + /// Resize a mask. + /// + /// If `M` > `N`, extends the length of a mask, setting the new elements to `value`. + /// If `M` < `N`, truncates the mask to the first `M` elements. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let x = mask32x4::from_array([false, true, true, false]); + /// assert_eq!(x.resize::<8>(true).to_array(), [false, true, true, false, true, true, true, true]); + /// assert_eq!(x.resize::<2>(true).to_array(), [false, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn resize<const M: usize>(self, value: bool) -> Mask<T, M> + where + LaneCount<M>: SupportedLaneCount, + { + // Safety: swizzles are safe for masks + unsafe { + Mask::<T, M>::from_int_unchecked(self.to_int().resize::<M>(if value { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Extract a vector from another vector. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let x = mask32x4::from_array([false, true, true, false]); + /// assert_eq!(x.extract::<1, 2>().to_array(), [true, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn extract<const START: usize, const LEN: usize>(self) -> Mask<T, LEN> + where + LaneCount<LEN>: SupportedLaneCount, + { + // Safety: swizzles are safe for masks + unsafe { Mask::<T, LEN>::from_int_unchecked(self.to_int().extract::<START, LEN>()) } + } } diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index 3b6388d0f27..773bd028bae 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -59,15 +59,40 @@ where target_endian = "little" ))] 16 => transize(vqtbl1q_u8, self, idxs), + #[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" + ))] + 16 => transize(armv7_neon_swizzle_u8x16, self, idxs), #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] 32 => transize(avx2_pshufb, self, idxs), #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] - 32 => transize(x86::_mm256_permutexvar_epi8, zeroing_idxs(idxs), self), - // Notable absence: avx512bw shuffle - // If avx512bw is available, odds of avx512vbmi are good - // FIXME: initial AVX512VBMI variant didn't actually pass muster - // #[cfg(target_feature = "avx512vbmi")] - // 64 => transize(x86::_mm512_permutexvar_epi8, self, idxs), + 32 => { + // Unlike vpshufb, vpermb doesn't zero out values in the result based on the index high bit + let swizzler = |bytes, idxs| { + let mask = x86::_mm256_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>( + idxs, + Simd::<u8, 32>::splat(N as u8).into(), + ); + x86::_mm256_maskz_permutexvar_epi8(mask, idxs, bytes) + }; + transize(swizzler, self, idxs) + } + // Notable absence: avx512bw pshufb shuffle + #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] + 64 => { + // Unlike vpshufb, vpermb doesn't zero out values in the result based on the index high bit + let swizzler = |bytes, idxs| { + let mask = x86::_mm512_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>( + idxs, + Simd::<u8, 64>::splat(N as u8).into(), + ); + x86::_mm512_maskz_permutexvar_epi8(mask, idxs, bytes) + }; + transize(swizzler, self, idxs) + } _ => { let mut array = [0; N]; for (i, k) in idxs.to_array().into_iter().enumerate() { @@ -82,6 +107,28 @@ where } } +/// armv7 neon supports swizzling `u8x16` by swizzling two u8x8 blocks +/// with a u8x8x2 lookup table. +/// +/// # Safety +/// This requires armv7 neon to work +#[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" +))] +unsafe fn armv7_neon_swizzle_u8x16(bytes: Simd<u8, 16>, idxs: Simd<u8, 16>) -> Simd<u8, 16> { + use core::arch::arm::{uint8x8x2_t, vcombine_u8, vget_high_u8, vget_low_u8, vtbl2_u8}; + // SAFETY: Caller promised arm neon support + unsafe { + let bytes = uint8x8x2_t(vget_low_u8(bytes.into()), vget_high_u8(bytes.into())); + let lo = vtbl2_u8(bytes, vget_low_u8(idxs.into())); + let hi = vtbl2_u8(bytes, vget_high_u8(idxs.into())); + vcombine_u8(lo, hi).into() + } +} + /// "vpshufb like it was meant to be" on AVX2 /// /// # Safety diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 3e239169149..9c4dd36c24f 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -99,7 +99,7 @@ use crate::simd::{ // directly constructing an instance of the type (i.e. `let vector = Simd(array)`) should be // avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also // causes rustc to emit illegal LLVM IR in some cases. -#[repr(simd)] +#[repr(simd, packed)] pub struct Simd<T, const N: usize>([T; N]) where LaneCount<N>: SupportedLaneCount, @@ -144,14 +144,32 @@ where /// assert_eq!(v.as_array(), &[8, 8, 8, 8]); /// ``` #[inline] - pub fn splat(value: T) -> Self { - // This is preferred over `[value; N]`, since it's explicitly a splat: - // https://github.com/rust-lang/rust/issues/97804 - struct Splat; - impl<const N: usize> Swizzle<N> for Splat { - const INDEX: [usize; N] = [0; N]; + #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] + pub const fn splat(value: T) -> Self { + const fn splat_const<T, const N: usize>(value: T) -> Simd<T, N> + where + T: SimdElement, + LaneCount<N>: SupportedLaneCount, + { + Simd::from_array([value; N]) } - Splat::swizzle::<T, 1>(Simd::<T, 1>::from([value])) + + fn splat_rt<T, const N: usize>(value: T) -> Simd<T, N> + where + T: SimdElement, + LaneCount<N>: SupportedLaneCount, + { + // This is preferred over `[value; N]`, since it's explicitly a splat: + // https://github.com/rust-lang/rust/issues/97804 + struct Splat; + impl<const N: usize> Swizzle<N> for Splat { + const INDEX: [usize; N] = [0; N]; + } + + Splat::swizzle::<T, 1>(Simd::<T, 1>::from([value])) + } + + core::intrinsics::const_eval_select((value,), splat_const, splat_rt) } /// Returns an array reference containing the entire SIMD vector. @@ -425,6 +443,9 @@ where /// /// When the element is disabled, that memory location is not accessed and the corresponding /// value from `or` is passed through. + /// + /// # Safety + /// Enabled loads must not exceed the length of `slice`. #[must_use] #[inline] pub unsafe fn load_select_unchecked( @@ -442,6 +463,9 @@ where /// /// When the element is disabled, that memory location is not accessed and the corresponding /// value from `or` is passed through. + /// + /// # Safety + /// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`. #[must_use] #[inline] pub unsafe fn load_select_ptr( @@ -924,6 +948,7 @@ where } } +/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl<T, const N: usize> PartialOrd for Simd<T, N> where LaneCount<N>: SupportedLaneCount, @@ -943,6 +968,7 @@ where { } +/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl<T, const N: usize> Ord for Simd<T, N> where LaneCount<N>: SupportedLaneCount, @@ -1195,6 +1221,7 @@ fn lane_indices<const N: usize>() -> Simd<usize, N> where LaneCount<N>: SupportedLaneCount, { + #![allow(clippy::needless_range_loop)] let mut index = [0; N]; for i in 0..N { index[i] = i; diff --git a/library/portable-simd/crates/core_simd/src/vendor.rs b/library/portable-simd/crates/core_simd/src/vendor.rs index 1a34a3a8de5..57536e4fc77 100644 --- a/library/portable-simd/crates/core_simd/src/vendor.rs +++ b/library/portable-simd/crates/core_simd/src/vendor.rs @@ -29,3 +29,6 @@ mod arm; #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] mod powerpc; + +#[cfg(target_arch = "loongarch64")] +mod loongarch64; diff --git a/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs b/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs new file mode 100644 index 00000000000..1290bc166b2 --- /dev/null +++ b/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs @@ -0,0 +1,31 @@ +use crate::simd::*; +use core::arch::loongarch64::*; + +from_transmute! { unsafe u8x16 => v16u8 } +from_transmute! { unsafe u8x32 => v32u8 } +from_transmute! { unsafe i8x16 => v16i8 } +from_transmute! { unsafe i8x32 => v32i8 } + +from_transmute! { unsafe u16x8 => v8u16 } +from_transmute! { unsafe u16x16 => v16u16 } +from_transmute! { unsafe i16x8 => v8i16 } +from_transmute! { unsafe i16x16 => v16i16 } + +from_transmute! { unsafe u32x4 => v4u32 } +from_transmute! { unsafe u32x8 => v8u32 } +from_transmute! { unsafe i32x4 => v4i32 } +from_transmute! { unsafe i32x8 => v8i32 } +from_transmute! { unsafe f32x4 => v4f32 } +from_transmute! { unsafe f32x8 => v8f32 } + +from_transmute! { unsafe u64x2 => v2u64 } +from_transmute! { unsafe u64x4 => v4u64 } +from_transmute! { unsafe i64x2 => v2i64 } +from_transmute! { unsafe i64x4 => v4i64 } +from_transmute! { unsafe f64x2 => v2f64 } +from_transmute! { unsafe f64x4 => v4f64 } + +from_transmute! { unsafe usizex2 => v2u64 } +from_transmute! { unsafe usizex4 => v4u64 } +from_transmute! { unsafe isizex2 => v2i64 } +from_transmute! { unsafe isizex4 => v4i64 } diff --git a/library/portable-simd/crates/core_simd/tests/layout.rs b/library/portable-simd/crates/core_simd/tests/layout.rs new file mode 100644 index 00000000000..24114c2d261 --- /dev/null +++ b/library/portable-simd/crates/core_simd/tests/layout.rs @@ -0,0 +1,35 @@ +#![feature(portable_simd)] + +macro_rules! layout_tests { + { $($mod:ident, $ty:ty,)* } => { + $( + mod $mod { + test_helpers::test_lanes! { + fn no_padding<const LANES: usize>() { + assert_eq!( + core::mem::size_of::<core_simd::simd::Simd::<$ty, LANES>>(), + core::mem::size_of::<[$ty; LANES]>(), + ); + } + } + } + )* + } +} + +layout_tests! { + i8, i8, + i16, i16, + i32, i32, + i64, i64, + isize, isize, + u8, u8, + u16, u16, + u32, u32, + u64, u64, + usize, usize, + f32, f32, + f64, f64, + mut_ptr, *mut (), + const_ptr, *const (), +} diff --git a/library/portable-simd/crates/core_simd/tests/masks.rs b/library/portable-simd/crates/core_simd/tests/masks.rs index fc6a3476b7c..48786d02440 100644 --- a/library/portable-simd/crates/core_simd/tests/masks.rs +++ b/library/portable-simd/crates/core_simd/tests/masks.rs @@ -99,7 +99,6 @@ macro_rules! test_mask_api { assert_eq!(Mask::<$type, 2>::from_bitmask(bitmask), mask); } - #[cfg(feature = "all_lane_counts")] #[test] fn roundtrip_bitmask_conversion_odd() { let values = [ @@ -134,48 +133,6 @@ macro_rules! test_mask_api { cast_impl::<i64>(); cast_impl::<isize>(); } - - #[test] - fn roundtrip_bitmask_vector_conversion() { - use core_simd::simd::ToBytes; - let values = [ - true, false, false, true, false, false, true, false, - true, true, false, false, false, false, false, true, - ]; - let mask = Mask::<$type, 16>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b01001001, 0b10000011]); - assert_eq!(Mask::<$type, 16>::from_bitmask_vector(bitmask), mask); - } - - // rust-lang/portable-simd#379 - #[test] - fn roundtrip_bitmask_vector_conversion_small() { - use core_simd::simd::ToBytes; - let values = [ - true, false, true, true - ]; - let mask = Mask::<$type, 4>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<1>(0).to_ne_bytes()[0], 0b00001101); - assert_eq!(Mask::<$type, 4>::from_bitmask_vector(bitmask), mask); - } - - /* FIXME doesn't work with non-powers-of-two, yet - // rust-lang/portable-simd#379 - #[cfg(feature = "all_lane_counts")] - #[test] - fn roundtrip_bitmask_vector_conversion_odd() { - use core_simd::simd::ToBytes; - let values = [ - true, false, true, false, true, true, false, false, false, true, true, - ]; - let mask = Mask::<$type, 11>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b00110101, 0b00000110]); - assert_eq!(Mask::<$type, 11>::from_bitmask_vector(bitmask), mask); - } - */ } } } diff --git a/library/portable-simd/crates/core_simd/tests/ops_macros.rs b/library/portable-simd/crates/core_simd/tests/ops_macros.rs index aa565a13752..6de78f51e59 100644 --- a/library/portable-simd/crates/core_simd/tests/ops_macros.rs +++ b/library/portable-simd/crates/core_simd/tests/ops_macros.rs @@ -216,6 +216,22 @@ macro_rules! impl_common_integer_tests { ) } + fn count_ones<const LANES: usize>() { + test_helpers::test_unary_elementwise( + &$vector::<LANES>::count_ones, + &|x| x.count_ones() as _, + &|_| true, + ) + } + + fn count_zeros<const LANES: usize>() { + test_helpers::test_unary_elementwise( + &$vector::<LANES>::count_zeros, + &|x| x.count_zeros() as _, + &|_| true, + ) + } + fn leading_zeros<const LANES: usize>() { test_helpers::test_unary_elementwise( &$vector::<LANES>::leading_zeros, @@ -307,6 +323,14 @@ macro_rules! impl_signed_tests { assert_eq!(a % b, Vector::<LANES>::splat(0)); } + fn abs_diff<const LANES: usize>() { + test_helpers::test_binary_elementwise( + &Vector::<LANES>::abs_diff, + &Scalar::abs_diff, + &|_, _| true, + ) + } + fn simd_min<const LANES: usize>() { use core_simd::simd::cmp::SimdOrd; let a = Vector::<LANES>::splat(Scalar::MIN); @@ -419,6 +443,14 @@ macro_rules! impl_unsigned_tests { &|_| true, ); } + + fn abs_diff<const LANES: usize>() { + test_helpers::test_binary_elementwise( + &Vector::<LANES>::abs_diff, + &Scalar::abs_diff, + &|_, _| true, + ) + } } impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add); @@ -495,6 +527,9 @@ macro_rules! impl_float_tests { } fn is_normal<const LANES: usize>() { + // Arm v7 Neon violates float opsem re: subnormals, see + // https://github.com/rust-lang/portable-simd/issues/439 + #[cfg(not(target_arch = "arm"))] test_helpers::test_unary_mask_elementwise( &Vector::<LANES>::is_normal, &Scalar::is_normal, @@ -503,6 +538,9 @@ macro_rules! impl_float_tests { } fn is_subnormal<const LANES: usize>() { + // Arm v7 Neon violates float opsem re: subnormals, see + // https://github.com/rust-lang/portable-simd/issues/439 + #[cfg(not(target_arch = "arm"))] test_helpers::test_unary_mask_elementwise( &Vector::<LANES>::is_subnormal, &Scalar::is_subnormal, diff --git a/library/portable-simd/crates/core_simd/tests/swizzle.rs b/library/portable-simd/crates/core_simd/tests/swizzle.rs index 522d71439b7..7001e5f6bf8 100644 --- a/library/portable-simd/crates/core_simd/tests/swizzle.rs +++ b/library/portable-simd/crates/core_simd/tests/swizzle.rs @@ -50,6 +50,24 @@ fn rotate() { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn shift() { + let a = Simd::from_array([1, 2, 3, 4]); + assert_eq!(a.shift_elements_left::<0>(0).to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_left::<1>(0).to_array(), [2, 3, 4, 0]); + assert_eq!(a.shift_elements_left::<2>(9).to_array(), [3, 4, 9, 9]); + assert_eq!(a.shift_elements_left::<3>(8).to_array(), [4, 8, 8, 8]); + assert_eq!(a.shift_elements_left::<4>(7).to_array(), [7, 7, 7, 7]); + assert_eq!(a.shift_elements_left::<5>(6).to_array(), [6, 6, 6, 6]); + assert_eq!(a.shift_elements_right::<0>(0).to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_right::<1>(0).to_array(), [0, 1, 2, 3]); + assert_eq!(a.shift_elements_right::<2>(-1).to_array(), [-1, -1, 1, 2]); + assert_eq!(a.shift_elements_right::<3>(-2).to_array(), [-2, -2, -2, 1]); + assert_eq!(a.shift_elements_right::<4>(-3).to_array(), [-3, -3, -3, -3]); + assert_eq!(a.shift_elements_right::<5>(-4).to_array(), [-4, -4, -4, -4]); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn interleave() { let a = Simd::from_array([0, 1, 2, 3, 4, 5, 6, 7]); let b = Simd::from_array([8, 9, 10, 11, 12, 13, 14, 15]); diff --git a/library/portable-simd/crates/test_helpers/Cargo.toml b/library/portable-simd/crates/test_helpers/Cargo.toml index 23dae7c9338..a5359b9abc8 100644 --- a/library/portable-simd/crates/test_helpers/Cargo.toml +++ b/library/portable-simd/crates/test_helpers/Cargo.toml @@ -6,6 +6,3 @@ publish = false [dependencies] proptest = { version = "0.10", default-features = false, features = ["alloc"] } - -[features] -all_lane_counts = [] diff --git a/library/portable-simd/crates/test_helpers/src/lib.rs b/library/portable-simd/crates/test_helpers/src/lib.rs index 51b860a8635..197c920e11e 100644 --- a/library/portable-simd/crates/test_helpers/src/lib.rs +++ b/library/portable-simd/crates/test_helpers/src/lib.rs @@ -539,32 +539,22 @@ macro_rules! test_lanes { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_1 1; lanes_2 2; - lanes_4 4; - ); - - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - $crate::test_lanes_helper!( - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; - lanes_8 8; - lanes_16 16; - lanes_32 32; - lanes_64 64; - ); - - #[cfg(feature = "all_lane_counts")] - $crate::test_lanes_helper!( - // test some odd and even non-power-of-2 lengths on miri - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + // Cover an odd and an even non-power-of-2 length in Miri. + // (Even non-power-of-2 vectors have alignment between element + // and vector size, so we want to cover that case as well.) lanes_3 3; - lanes_5 5; + lanes_6 6; ); - #[cfg(feature = "all_lane_counts")] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + lanes_4 4; + lanes_5 5; + lanes_7 7; + lanes_8 8; lanes_9 9; lanes_10 10; lanes_11 11; @@ -572,52 +562,55 @@ macro_rules! test_lanes { lanes_13 13; lanes_14 14; lanes_15 15; + lanes_16 16; lanes_17 17; - lanes_18 18; - lanes_19 19; - lanes_20 20; - lanes_21 21; - lanes_22 22; - lanes_23 23; + //lanes_18 18; + //lanes_19 19; + //lanes_20 20; + //lanes_21 21; + //lanes_22 22; + //lanes_23 23; lanes_24 24; - lanes_25 25; - lanes_26 26; - lanes_27 27; - lanes_28 28; - lanes_29 29; - lanes_30 30; - lanes_31 31; - lanes_33 33; - lanes_34 34; - lanes_35 35; - lanes_36 36; - lanes_37 37; - lanes_38 38; - lanes_39 39; - lanes_40 40; - lanes_41 41; - lanes_42 42; - lanes_43 43; - lanes_44 44; - lanes_45 45; - lanes_46 46; + //lanes_25 25; + //lanes_26 26; + //lanes_27 27; + //lanes_28 28; + //lanes_29 29; + //lanes_30 30; + //lanes_31 31; + lanes_32 32; + //lanes_33 33; + //lanes_34 34; + //lanes_35 35; + //lanes_36 36; + //lanes_37 37; + //lanes_38 38; + //lanes_39 39; + //lanes_40 40; + //lanes_41 41; + //lanes_42 42; + //lanes_43 43; + //lanes_44 44; + //lanes_45 45; + //lanes_46 46; lanes_47 47; - lanes_48 48; - lanes_49 49; - lanes_50 50; - lanes_51 51; - lanes_52 52; - lanes_53 53; - lanes_54 54; - lanes_55 55; + //lanes_48 48; + //lanes_49 49; + //lanes_50 50; + //lanes_51 51; + //lanes_52 52; + //lanes_53 53; + //lanes_54 54; + //lanes_55 55; lanes_56 56; lanes_57 57; - lanes_58 58; - lanes_59 59; - lanes_60 60; - lanes_61 61; - lanes_62 62; + //lanes_58 58; + //lanes_59 59; + //lanes_60 60; + //lanes_61 61; + //lanes_62 62; lanes_63 63; + lanes_64 64; ); } )* @@ -639,36 +632,24 @@ macro_rules! test_lanes_panic { core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body + // test some odd and even non-power-of-2 lengths on miri $crate::test_lanes_helper!( #[should_panic]; lanes_1 1; lanes_2 2; - lanes_4 4; - ); - - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - $crate::test_lanes_helper!( - #[should_panic]; - lanes_8 8; - lanes_16 16; - lanes_32 32; - lanes_64 64; - ); - - #[cfg(feature = "all_lane_counts")] - $crate::test_lanes_helper!( - // test some odd and even non-power-of-2 lengths on miri - #[should_panic]; lanes_3 3; - lanes_5 5; + lanes_6 6; ); - #[cfg(feature = "all_lane_counts")] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[should_panic]; + lanes_4 4; + lanes_5 5; + lanes_7 7; + lanes_8 8; lanes_9 9; lanes_10 10; lanes_11 11; @@ -676,52 +657,55 @@ macro_rules! test_lanes_panic { lanes_13 13; lanes_14 14; lanes_15 15; + lanes_16 16; lanes_17 17; - lanes_18 18; - lanes_19 19; - lanes_20 20; - lanes_21 21; - lanes_22 22; - lanes_23 23; + //lanes_18 18; + //lanes_19 19; + //lanes_20 20; + //lanes_21 21; + //lanes_22 22; + //lanes_23 23; lanes_24 24; - lanes_25 25; - lanes_26 26; - lanes_27 27; - lanes_28 28; - lanes_29 29; - lanes_30 30; - lanes_31 31; - lanes_33 33; - lanes_34 34; - lanes_35 35; - lanes_36 36; - lanes_37 37; - lanes_38 38; - lanes_39 39; - lanes_40 40; - lanes_41 41; - lanes_42 42; - lanes_43 43; - lanes_44 44; - lanes_45 45; - lanes_46 46; + //lanes_25 25; + //lanes_26 26; + //lanes_27 27; + //lanes_28 28; + //lanes_29 29; + //lanes_30 30; + //lanes_31 31; + lanes_32 32; + //lanes_33 33; + //lanes_34 34; + //lanes_35 35; + //lanes_36 36; + //lanes_37 37; + //lanes_38 38; + //lanes_39 39; + //lanes_40 40; + //lanes_41 41; + //lanes_42 42; + //lanes_43 43; + //lanes_44 44; + //lanes_45 45; + //lanes_46 46; lanes_47 47; - lanes_48 48; - lanes_49 49; - lanes_50 50; - lanes_51 51; - lanes_52 52; - lanes_53 53; - lanes_54 54; - lanes_55 55; + //lanes_48 48; + //lanes_49 49; + //lanes_50 50; + //lanes_51 51; + //lanes_52 52; + //lanes_53 53; + //lanes_54 54; + //lanes_55 55; lanes_56 56; lanes_57 57; - lanes_58 58; - lanes_59 59; - lanes_60 60; - lanes_61 61; - lanes_62 62; + //lanes_58 58; + //lanes_59 59; + //lanes_60 60; + //lanes_61 61; + //lanes_62 62; lanes_63 63; + lanes_64 64; ); } )* diff --git a/library/portable-simd/rust-toolchain.toml b/library/portable-simd/rust-toolchain.toml new file mode 100644 index 00000000000..d17c6d2e889 --- /dev/null +++ b/library/portable-simd/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2025-01-16" +components = ["rustfmt", "clippy", "miri", "rust-src"] diff --git a/library/portable-simd/subtree-sync.sh b/library/portable-simd/subtree-sync.sh new file mode 100755 index 00000000000..18360077623 --- /dev/null +++ b/library/portable-simd/subtree-sync.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +set -eou pipefail + +git fetch origin +pushd $2 +git fetch origin +popd + +if [ "$(git rev-parse --show-prefix)" != "" ]; then + echo "Run this script from the git root" >&2 + exit 1 +fi + +if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/master)" ]; then + echo "$(pwd) is not at origin/master" >&2 + exit 1 +fi + +if [ ! -f library/portable-simd/git-subtree.sh ]; then + curl -sS https://raw.githubusercontent.com/bjorn3/git/tqc-subtree-portable/contrib/subtree/git-subtree.sh -o library/portable-simd/git-subtree.sh + chmod +x library/portable-simd/git-subtree.sh +fi + +today=$(date +%Y-%m-%d) + +case $1 in + "push") + upstream=rust-upstream-$today + merge=sync-from-rust-$today + + pushd $2 + git checkout master + git pull + popd + + library/portable-simd/git-subtree.sh push -P library/portable-simd $2 $upstream + + pushd $2 + git checkout -B $merge origin/master + git merge $upstream + popd + echo "Branch \`$merge\` created in \`$2\`. You may need to resolve merge conflicts." + ;; + "pull") + branch=sync-from-portable-simd-$today + + git checkout -B $branch + echo "Creating branch \`$branch\`... You may need to resolve merge conflicts." + library/portable-simd/git-subtree.sh pull -P library/portable-simd $2 origin/master + ;; +esac diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index d371ae3cea0..524fdf53d6b 100644 --- a/library/proc_macro/src/bridge/closure.rs +++ b/library/proc_macro/src/bridge/closure.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; #[repr(C)] -pub struct Closure<'a, A, R> { +pub(super) struct Closure<'a, A, R> { call: unsafe extern "C" fn(*mut Env, A) -> R, env: *mut Env, // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing @@ -26,7 +26,7 @@ impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { } impl<'a, A, R> Closure<'a, A, R> { - pub fn call(&mut self, arg: A) -> R { + pub(super) fn call(&mut self, arg: A) -> R { unsafe { (self.call)(self.env, arg) } } } diff --git a/library/proc_macro/src/bridge/fxhash.rs b/library/proc_macro/src/bridge/fxhash.rs index 3345e099a37..5f6b3d1b929 100644 --- a/library/proc_macro/src/bridge/fxhash.rs +++ b/library/proc_macro/src/bridge/fxhash.rs @@ -9,7 +9,7 @@ use std::hash::{BuildHasherDefault, Hasher}; use std::ops::BitXor; /// Type alias for a hashmap using the `fx` hash algorithm. -pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>; +pub(super) type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>; /// A speedy hash algorithm for use within rustc. The hashmap in alloc by /// default uses SipHash which isn't quite as speedy as we want. In the compiler @@ -23,7 +23,7 @@ pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>; /// similar or slightly worse than FNV, but the speed of the hash function /// itself is much higher because it works on up to 8 bytes at a time. #[derive(Default)] -pub struct FxHasher { +pub(super) struct FxHasher { hash: usize, } diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 202a8e04543..85fd7d13858 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -67,7 +67,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match self { @@ -89,7 +89,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match u8::decode(r, s) { diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index 907ad256e4b..312a79152e2 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -44,7 +44,7 @@ macro_rules! define_reify_functions { fn $name:ident $(<$($param:ident),*>)? for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; )+) => { - $(pub const fn $name< + $(pub(super) const fn $name< $($($param,)*)? F: Fn($($arg_ty),*) -> $ret_ty + Copy >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index b19c9cee75a..6611ce30a1b 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -32,6 +32,7 @@ #![allow(internal_features)] #![deny(ffi_unwind_calls)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index d3ff5c14aa4..67b09599d9d 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -52,7 +52,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { #[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))] pub mod eh_frames { #[no_mangle] - #[link_section = ".eh_frame"] + #[unsafe(link_section = ".eh_frame")] // Marks beginning of the stack frame unwind info section pub static __EH_FRAME_BEGIN__: [u8; 0] = []; @@ -76,7 +76,7 @@ pub mod eh_frames { } // Unwind info registration/deregistration routines. - extern "C" { + unsafe extern "C" { fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8); fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8); } @@ -101,10 +101,10 @@ pub mod eh_frames { // end of the list. Since constructors are run in reverse order, this ensures that our // callbacks are the first and last ones executed. - #[link_section = ".ctors.65535"] // .ctors.* : C initialization callbacks + #[unsafe(link_section = ".ctors.65535")] // .ctors.* : C initialization callbacks pub static P_INIT: unsafe extern "C" fn() = super::init; - #[link_section = ".dtors.65535"] // .dtors.* : C termination callbacks + #[unsafe(link_section = ".dtors.65535")] // .dtors.* : C termination callbacks pub static P_UNINIT: unsafe extern "C" fn() = super::uninit; } } diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs index 81acfbed447..a6f7d103356 100644 --- a/library/rtstartup/rsend.rs +++ b/library/rtstartup/rsend.rs @@ -32,6 +32,6 @@ pub mod eh_frames { // Terminate the frame unwind info section with a 0 as a sentinel; // this would be the 'length' field in a real FDE. #[no_mangle] - #[link_section = ".eh_frame"] + #[unsafe(link_section = ".eh_frame")] pub static __EH_FRAME_END__: u32 = 0; } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index da58d7c13bd..add5a20d179 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "The Rust Standard Library" edition = "2021" +autobenches = false [lib] crate-type = ["dylib", "rlib"] @@ -17,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.143" } +compiler_builtins = { version = "=0.1.146" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', @@ -130,6 +131,18 @@ name = "pipe-subprocess" path = "tests/pipe_subprocess.rs" harness = false +[[test]] +name = "sync" +path = "tests/sync/lib.rs" + +[[test]] +name = "floats" +path = "tests/floats/lib.rs" + +[[test]] +name = "thread_local" +path = "tests/thread_local/lib.rs" + [[bench]] name = "stdbenches" path = "benches/lib.rs" @@ -139,7 +152,8 @@ test = true level = "warn" check-cfg = [ 'cfg(bootstrap)', - 'cfg(target_arch, values("xtensa"))', + 'cfg(target_arch, values("xtensa", "aarch64-unknown-nto-qnx710_iosock", "x86_64-pc-nto-qnx710_iosock", "x86_64-pc-nto-qnx800","aarch64-unknown-nto-qnx800"))', + 'cfg(target_env, values("nto71_iosock", "nto80"))', # std use #[path] imports to portable-simd `std_float` crate # and to the `backtrace` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg diff --git a/library/std/benches/lib.rs b/library/std/benches/lib.rs index 1b21c230a0b..e749d9c0f79 100644 --- a/library/std/benches/lib.rs +++ b/library/std/benches/lib.rs @@ -5,3 +5,5 @@ extern crate test; mod hash; +mod path; +mod time; diff --git a/library/std/benches/path.rs b/library/std/benches/path.rs new file mode 100644 index 00000000000..094c00894a8 --- /dev/null +++ b/library/std/benches/path.rs @@ -0,0 +1,114 @@ +use core::hint::black_box; +use std::collections::{BTreeSet, HashSet}; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::path::*; + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { + let prefix = "my/home"; + let mut paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + paths.sort(); + + b.iter(|| { + black_box(paths.as_mut_slice()).sort_unstable(); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { + let prefix = "my/home"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_hashset(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(black_box(paths[500].as_path())) + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_hashset_miss(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + let probe = PathBuf::from(prefix).join("other"); + + b.iter(|| set.remove(black_box(probe.as_path()))); +} + +#[bench] +fn bench_hash_path_short(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = Path::new("explorer.exe"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} + +#[bench] +fn bench_hash_path_long(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = + Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} diff --git a/library/std/benches/time.rs b/library/std/benches/time.rs new file mode 100644 index 00000000000..552481cad92 --- /dev/null +++ b/library/std/benches/time.rs @@ -0,0 +1,47 @@ +use std::time::Instant; + +#[cfg(not(target_arch = "wasm32"))] +use test::{Bencher, black_box}; + +macro_rules! bench_instant_threaded { + ($bench_name:ident, $thread_count:expr) => { + #[bench] + #[cfg(not(target_arch = "wasm32"))] + fn $bench_name(b: &mut Bencher) -> std::thread::Result<()> { + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, Ordering}; + + let running = Arc::new(AtomicBool::new(true)); + + let threads: Vec<_> = (0..$thread_count) + .map(|_| { + let flag = Arc::clone(&running); + std::thread::spawn(move || { + while flag.load(Ordering::Relaxed) { + black_box(Instant::now()); + } + }) + }) + .collect(); + + b.iter(|| { + let a = Instant::now(); + let b = Instant::now(); + assert!(b >= a); + }); + + running.store(false, Ordering::Relaxed); + + for t in threads { + t.join()?; + } + Ok(()) + } + }; +} + +bench_instant_threaded!(instant_contention_01_threads, 0); +bench_instant_threaded!(instant_contention_02_threads, 1); +bench_instant_threaded!(instant_contention_04_threads, 3); +bench_instant_threaded!(instant_contention_08_threads, 7); +bench_instant_threaded!(instant_contention_16_threads, 15); diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 5d51d6a0c78..3936ed057e6 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -345,7 +345,7 @@ pub fn take_alloc_error_hook() -> fn(Layout) { } fn default_alloc_error_hook(layout: Layout) { - extern "Rust" { + unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. static __rust_alloc_error_handler_should_panic: u8; diff --git a/library/std/src/bstr.rs b/library/std/src/bstr.rs new file mode 100644 index 00000000000..dd491771628 --- /dev/null +++ b/library/std/src/bstr.rs @@ -0,0 +1,4 @@ +//! The `ByteStr` and `ByteString` types and trait implementations. + +#[unstable(feature = "bstr", issue = "134915")] +pub use alloc::bstr::{ByteStr, ByteString}; diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index d2342d8fd51..6a0ff3a29e0 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -282,7 +282,7 @@ impl<K, V, S> HashMap<K, V, S> { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashMap` to be useful, see its documentation for details. /// /// # Examples /// @@ -314,7 +314,7 @@ impl<K, V, S> HashMap<K, V, S> { /// manually using this function can expose a DoS attack vector. /// /// The `hasher` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashMap` to be useful, see its documentation for details. /// /// # Examples /// @@ -969,7 +969,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -979,13 +978,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // Get Athenæum and Bodleian Library - /// let [Some(a), Some(b)] = libraries.get_many_mut([ + /// let [Some(a), Some(b)] = libraries.get_disjoint_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) else { panic!() }; /// /// // Assert values of Athenæum and Library of Congress - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Library of Congress", /// ]); @@ -998,7 +997,7 @@ where /// ); /// /// // Missing keys result in None - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "New York Public Library", /// ]); @@ -1012,21 +1011,24 @@ where /// ``` /// /// ```should_panic - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); /// libraries.insert("Athenæum".to_string(), 1807); /// /// // Duplicate keys panic! - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Athenæum", /// ]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub fn get_many_mut<Q: ?Sized, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N] + #[doc(alias = "get_many_mut")] + #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] + pub fn get_disjoint_mut<Q: ?Sized, const N: usize>( + &mut self, + ks: [&Q; N], + ) -> [Option<&'_ mut V>; N] where K: Borrow<Q>, Q: Hash + Eq, @@ -1040,7 +1042,7 @@ where /// Returns an array of length `N` with the results of each query. `None` will be used if /// the key is missing. /// - /// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`). + /// For a safe alternative see [`get_disjoint_mut`](`HashMap::get_disjoint_mut`). /// /// # Safety /// @@ -1052,7 +1054,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -1062,13 +1063,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // SAFETY: The keys do not overlap. - /// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([ + /// let [Some(a), Some(b)] = (unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) }) else { panic!() }; /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Library of Congress", /// ]) }; @@ -1081,7 +1082,7 @@ where /// ); /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "New York Public Library", /// ]) }; @@ -1089,8 +1090,9 @@ where /// assert_eq!(got, [Some(&mut 1807), None]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub unsafe fn get_many_unchecked_mut<Q: ?Sized, const N: usize>( + #[doc(alias = "get_many_unchecked_mut")] + #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] + pub unsafe fn get_disjoint_unchecked_mut<Q: ?Sized, const N: usize>( &mut self, ks: [&Q; N], ) -> [Option<&'_ mut V>; N] @@ -1281,7 +1283,7 @@ impl<K, V, S> HashMap<K, V, S> where S: BuildHasher, { - /// Creates a raw entry builder for the HashMap. + /// Creates a raw entry builder for the `HashMap`. /// /// Raw entries provide the lowest level of control for searching and /// manipulating a map. They must be manually initialized with a hash and @@ -1296,13 +1298,13 @@ where /// * Using custom comparison logic without newtype wrappers /// /// Because raw entries provide much more low-level control, it's much easier - /// to put the HashMap into an inconsistent state which, while memory-safe, + /// to put the `HashMap` into an inconsistent state which, while memory-safe, /// will cause the map to produce seemingly random results. Higher-level and /// more foolproof APIs like `entry` should be preferred when possible. /// /// In particular, the hash used to initialize the raw entry must still be /// consistent with the hash of the key that is ultimately stored in the entry. - /// This is because implementations of HashMap may need to recompute hashes + /// This is because implementations of `HashMap` may need to recompute hashes /// when resizing, at which point only the keys are available. /// /// Raw entries give mutable access to the keys. This must not be used @@ -1318,7 +1320,7 @@ where RawEntryBuilderMut { map: self } } - /// Creates a raw immutable entry builder for the HashMap. + /// Creates a raw immutable entry builder for the `HashMap`. /// /// Raw entries provide the lowest level of control for searching and /// manipulating a map. They must be manually initialized with a hash and diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index bbb6ca23521..c265d42d06a 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -374,7 +374,7 @@ impl<T, S> HashSet<T, S> { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashSet` to be useful, see its documentation for details. /// /// # Examples /// @@ -406,7 +406,7 @@ impl<T, S> HashSet<T, S> { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashSet` to be useful, see its documentation for details. /// /// # Examples /// diff --git a/library/std/src/env.rs b/library/std/src/env.rs index bbd506127fb..c665dfd3624 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -10,9 +10,6 @@ #![stable(feature = "env", since = "1.0.0")] -#[cfg(test)] -mod tests; - use crate::error::Error; use crate::ffi::{OsStr, OsString}; use crate::path::{Path, PathBuf}; diff --git a/library/std/src/env/tests.rs b/library/std/src/env/tests.rs deleted file mode 100644 index d0217261068..00000000000 --- a/library/std/src/env/tests.rs +++ /dev/null @@ -1,120 +0,0 @@ -use super::*; - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)] -fn test_self_exe_path() { - let path = current_exe(); - assert!(path.is_ok()); - let path = path.unwrap(); - - // Hard to test this function - assert!(path.is_absolute()); -} - -#[test] -fn test() { - assert!((!Path::new("test-path").is_absolute())); - - #[cfg(not(target_env = "sgx"))] - current_dir().unwrap(); -} - -#[test] -#[cfg(windows)] -fn split_paths_windows() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::<Vec<_>>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse(r#""""#, &mut [""])); - assert!(check_parse(";;", &mut ["", "", ""])); - assert!(check_parse(r"c:\", &mut [r"c:\"])); - assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); - assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); - assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); - assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"])); -} - -#[test] -#[cfg(unix)] -fn split_paths_unix() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::<Vec<_>>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse("::", &mut ["", "", ""])); - assert!(check_parse("/", &mut ["/"])); - assert!(check_parse("/:", &mut ["/", ""])); - assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); -} - -#[test] -#[cfg(unix)] -fn join_paths_unix() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); - assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); - assert!(join_paths(["/te:st"].iter().cloned()).is_err()); -} - -#[test] -#[cfg(windows)] -fn join_paths_windows() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); - assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); - assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); - assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); -} - -#[test] -fn args_debug() { - assert_eq!( - format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()), - format!("{:?}", args()) - ); -} - -#[test] -fn args_os_debug() { - assert_eq!( - format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()), - format!("{:?}", args_os()) - ); -} - -#[test] -fn vars_debug() { - assert_eq!( - format!("Vars {{ inner: {:?} }}", vars().collect::<Vec<_>>()), - format!("{:?}", vars()) - ); -} - -#[test] -fn vars_os_debug() { - assert_eq!( - format!("VarsOs {{ inner: {:?} }}", vars_os().collect::<Vec<_>>()), - format!("{:?}", vars_os()) - ); -} diff --git a/library/std/src/error.rs b/library/std/src/error.rs index b3e63aaf1c5..def5f984c88 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -1,9 +1,6 @@ #![doc = include_str!("../../core/src/error.md")] #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] pub use core::error::Error; #[unstable(feature = "error_generic_member_access", issue = "99301")] diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 4f37e18a8cd..89612fa7475 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -4,9 +4,6 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. -#[cfg(test)] -mod tests; - #[unstable(feature = "f128", issue = "116909")] pub use core::f128::consts; @@ -324,6 +321,20 @@ impl f128 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -347,8 +358,10 @@ impl f128 { /// /// let x = 2.0_f128; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powf(1.0, f128::NAN), 1.0); + /// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 42cd6e3fe2a..cc523c93b4d 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -4,9 +4,6 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. -#[cfg(test)] -mod tests; - #[unstable(feature = "f16", issue = "116909")] pub use core::f16::consts; @@ -324,6 +321,20 @@ impl f16 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -347,8 +358,10 @@ impl f16 { /// /// let x = 2.0_f16; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powf(1.0, f16::NAN), 1.0); + /// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 438d77b1626..260c499b7f4 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -12,9 +12,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f32::{ @@ -306,8 +303,9 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powi(f32::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +327,10 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powf(1.0, f32::NAN), 1.0); + /// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 9bb4bfbab2a..7af646f8cfd 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -12,9 +12,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f64::{ @@ -306,8 +303,9 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +327,10 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); + /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 9b752ed1443..83b009c86dc 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -629,9 +629,9 @@ impl File { /// This acquires an exclusive advisory lock; no other file handle to this file may acquire /// another lock. /// - /// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then an exclusive lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns, then an exclusive lock is held. /// /// If the file not open for writing, it is unspecified whether this function returns an error. /// @@ -639,6 +639,9 @@ impl File { /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag, @@ -661,7 +664,7 @@ impl File { /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { - /// let f = File::open("foo.txt")?; + /// let f = File::create("foo.txt")?; /// f.lock()?; /// Ok(()) /// } @@ -671,19 +674,22 @@ impl File { self.inner.lock() } - /// Acquire a shared advisory lock on the file. Blocks until the lock can be acquired. + /// Acquire a shared (non-exclusive) advisory lock on the file. Blocks until the lock can be acquired. /// /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but - /// none may hold an exclusive lock. + /// none may hold an exclusive lock at the same time. /// - /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then a shared lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns, then a shared lock is held. /// /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag, @@ -716,14 +722,18 @@ impl File { self.inner.lock_shared() } - /// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked. + /// Try to acquire an exclusive advisory lock on the file. + /// + /// Returns `Ok(false)` if a different lock is already held on this file (via another + /// handle/descriptor). /// /// This acquires an exclusive advisory lock; no other file handle to this file may acquire /// another lock. /// - /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then an exclusive lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns `Ok(true)`, then it has acquired an exclusive + /// lock. /// /// If the file not open for writing, it is unspecified whether this function returns an error. /// @@ -731,6 +741,9 @@ impl File { /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and @@ -754,7 +767,7 @@ impl File { /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { - /// let f = File::open("foo.txt")?; + /// let f = File::create("foo.txt")?; /// f.try_lock()?; /// Ok(()) /// } @@ -764,20 +777,25 @@ impl File { self.inner.try_lock() } - /// Acquire a shared advisory lock on the file. - /// Returns `Ok(false)` if the file is exclusively locked. + /// Try to acquire a shared (non-exclusive) advisory lock on the file. + /// + /// Returns `Ok(false)` if an exclusive lock is already held on this file (via another + /// handle/descriptor). /// /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but - /// none may hold an exclusive lock. + /// none may hold an exclusive lock at the same time. /// /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then a shared lock is held. + /// However, if this method returns `Ok(true)`, then it has acquired a shared lock. /// /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], /// [`try_lock`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and @@ -813,7 +831,12 @@ impl File { /// Release all locks on the file. /// - /// All remaining locks are released when the file handle, and all clones of it, are dropped. + /// All locks are released when the file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed. This method allows releasing locks without + /// closing the file. + /// + /// If no lock is currently held via this file descriptor/handle, this method may return an + /// error, or may return successfully without taking any action. /// /// # Platform-specific behavior /// @@ -2284,8 +2307,8 @@ impl AsInner<fs_imp::DirEntry> for DirEntry { /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `unlink` function on Unix -/// and the `DeleteFile` function on Windows. +/// This function currently corresponds to the `unlink` function on Unix. +/// On Windows, `DeleteFile` is used or `CreateFileW` and `SetInformationByHandle` for readonly files. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -2529,6 +2552,7 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { /// limited to just these cases: /// /// * The `original` path is not a file or doesn't exist. +/// * The 'link' path already exists. /// /// # Examples /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 28f16da1ed8..8e307f57cf9 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -14,7 +14,7 @@ use crate::os::unix::fs::symlink as junction_point; use crate::os::windows::fs::{OpenOptionsExt, junction_point, symlink_dir, symlink_file}; use crate::path::Path; use crate::sync::Arc; -use crate::sys_common::io::test::{TempDir, tmpdir}; +use crate::test_helpers::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; use crate::{env, str, thread}; @@ -1384,7 +1384,7 @@ fn file_try_clone() { } #[test] -#[cfg(not(windows))] +#[cfg(not(target_vendor = "win7"))] fn unlink_readonly() { let tmpdir = tmpdir(); let path = tmpdir.join("file"); diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index 2e0eb6cdce6..25b1ece2745 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -126,6 +126,7 @@ mod io_benches { use crate::io::prelude::*; #[bench] + #[cfg_attr(target_os = "emscripten", ignore)] // no /dev fn bench_copy_buf_reader(b: &mut Bencher) { let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); // use dyn to avoid specializations unrelated to readbuf diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index b2ffeb0f95d..606099c8bc6 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -153,7 +153,7 @@ impl<T> Cursor<T> { /// let reference = buff.get_mut(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + #[rustc_const_stable(feature = "const_mut_cursor", since = "CURRENT_RUSTC_VERSION")] pub const fn get_mut(&mut self) -> &mut T { &mut self.inner } @@ -201,7 +201,7 @@ impl<T> Cursor<T> { /// assert_eq!(buff.position(), 4); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + #[rustc_const_stable(feature = "const_mut_cursor", since = "CURRENT_RUSTC_VERSION")] pub const fn set_position(&mut self, pos: u64) { self.pos = pos; } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 231c8712ebd..0ffad2c27a4 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -310,6 +310,8 @@ pub use self::error::RawOsError; pub use self::error::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] pub use self::error::const_error; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; pub(crate) use self::stdio::attempt_print_to_stderr; @@ -330,7 +332,6 @@ pub use self::{ }; use crate::mem::take; use crate::ops::{Deref, DerefMut}; -use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; use crate::{cmp, fmt, slice, str, sys}; mod buffered; @@ -338,11 +339,12 @@ pub(crate) mod copy; mod cursor; mod error; mod impls; +mod pipe; pub mod prelude; mod stdio; mod util; -const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub(crate) use stdio::cleanup; @@ -3251,251 +3253,3 @@ impl<B: BufRead> Iterator for Lines<B> { } } } - -/// Create anonymous pipe that is close-on-exec and blocking. -/// -/// # Behavior -/// -/// A pipe is a synchronous, unidirectional data channel between two or more processes, like an -/// interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular: -/// -/// * A read on a [`PipeReader`] blocks until the pipe is non-empty. -/// * A write on a [`PipeWriter`] blocks when the pipe is full. -/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`] -/// returns EOF. -/// * [`PipeReader`] can be shared, but only one process will consume the data in the pipe. -/// -/// # Capacity -/// -/// Pipe capacity is platform dependent. To quote the Linux [man page]: -/// -/// > Different implementations have different limits for the pipe capacity. Applications should -/// > not rely on a particular capacity: an application should be designed so that a reading process -/// > consumes data as soon as it is available, so that a writing process does not remain blocked. -/// -/// # Examples -/// -/// ```no_run -/// #![feature(anonymous_pipe)] -/// # #[cfg(miri)] fn main() {} -/// # #[cfg(not(miri))] -/// # fn main() -> std::io::Result<()> { -/// # use std::process::Command; -/// # use std::io::{Read, Write}; -/// let (ping_rx, mut ping_tx) = std::io::pipe()?; -/// let (mut pong_rx, pong_tx) = std::io::pipe()?; -/// -/// // Spawn a process that echoes its input. -/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; -/// -/// ping_tx.write_all(b"hello")?; -/// // Close to unblock echo_server's reader. -/// drop(ping_tx); -/// -/// let mut buf = String::new(); -/// // Block until echo_server's writer is closed. -/// pong_rx.read_to_string(&mut buf)?; -/// assert_eq!(&buf, "hello"); -/// -/// echo_server.wait()?; -/// # Ok(()) -/// # } -/// ``` -/// [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html -/// [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe -/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[inline] -pub fn pipe() -> Result<(PipeReader, PipeWriter)> { - pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) -} - -/// Read end of the anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[derive(Debug)] -pub struct PipeReader(pub(crate) AnonPipe); - -/// Write end of the anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[derive(Debug)] -pub struct PipeWriter(pub(crate) AnonPipe); - -impl PipeReader { - /// Create a new [`PipeReader`] instance that shares the same underlying file description. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(anonymous_pipe)] - /// # #[cfg(miri)] fn main() {} - /// # #[cfg(not(miri))] - /// # fn main() -> std::io::Result<()> { - /// # use std::fs; - /// # use std::io::Write; - /// # use std::process::Command; - /// const NUM_SLOT: u8 = 2; - /// const NUM_PROC: u8 = 5; - /// const OUTPUT: &str = "work.txt"; - /// - /// let mut jobs = vec![]; - /// let (reader, mut writer) = std::io::pipe()?; - /// - /// // Write NUM_SLOT characters the pipe. - /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; - /// - /// // Spawn several processes that read a character from the pipe, do some work, then - /// // write back to the pipe. When the pipe is empty, the processes block, so only - /// // NUM_SLOT processes can be working at any given time. - /// for _ in 0..NUM_PROC { - /// jobs.push( - /// Command::new("bash") - /// .args(["-c", - /// &format!( - /// "read -n 1\n\ - /// echo -n 'x' >> '{OUTPUT}'\n\ - /// echo -n '|'", - /// ), - /// ]) - /// .stdin(reader.try_clone()?) - /// .stdout(writer.try_clone()?) - /// .spawn()?, - /// ); - /// } - /// - /// // Wait for all jobs to finish. - /// for mut job in jobs { - /// job.wait()?; - /// } - /// - /// // Check our work and clean up. - /// let xs = fs::read_to_string(OUTPUT)?; - /// fs::remove_file(OUTPUT)?; - /// assert_eq!(xs, "x".repeat(NUM_PROC.into())); - /// # Ok(()) - /// # } - /// ``` - #[unstable(feature = "anonymous_pipe", issue = "127154")] - pub fn try_clone(&self) -> Result<Self> { - self.0.try_clone().map(Self) - } -} - -impl PipeWriter { - /// Create a new [`PipeWriter`] instance that shares the same underlying file description. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(anonymous_pipe)] - /// # #[cfg(miri)] fn main() {} - /// # #[cfg(not(miri))] - /// # fn main() -> std::io::Result<()> { - /// # use std::process::Command; - /// # use std::io::Read; - /// let (mut reader, writer) = std::io::pipe()?; - /// - /// // Spawn a process that writes to stdout and stderr. - /// let mut peer = Command::new("bash") - /// .args([ - /// "-c", - /// "echo -n foo\n\ - /// echo -n bar >&2" - /// ]) - /// .stdout(writer.try_clone()?) - /// .stderr(writer) - /// .spawn()?; - /// - /// // Read and check the result. - /// let mut msg = String::new(); - /// reader.read_to_string(&mut msg)?; - /// assert_eq!(&msg, "foobar"); - /// - /// peer.wait()?; - /// # Ok(()) - /// # } - /// ``` - #[unstable(feature = "anonymous_pipe", issue = "127154")] - pub fn try_clone(&self) -> Result<Self> { - self.0.try_clone().map(Self) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl Read for &PipeReader { - fn read(&mut self, buf: &mut [u8]) -> Result<usize> { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { - self.0.read_buf(buf) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl Read for PipeReader { - fn read(&mut self, buf: &mut [u8]) -> Result<usize> { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { - self.0.read_buf(buf) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl Write for &PipeWriter { - fn write(&mut self, buf: &[u8]) -> Result<usize> { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> Result<()> { - Ok(()) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl Write for PipeWriter { - fn write(&mut self, buf: &[u8]) -> Result<usize> { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> Result<()> { - Ok(()) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs new file mode 100644 index 00000000000..266c7bc9638 --- /dev/null +++ b/library/std/src/io/pipe.rs @@ -0,0 +1,260 @@ +use crate::io; +use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; + +/// Create an anonymous pipe. +/// +/// # Behavior +/// +/// A pipe is a one-way data channel provided by the OS, which works across processes. A pipe is +/// typically used to communicate between two or more separate processes, as there are better, +/// faster ways to communicate within a single process. +/// +/// In particular: +/// +/// * A read on a [`PipeReader`] blocks until the pipe is non-empty. +/// * A write on a [`PipeWriter`] blocks when the pipe is full. +/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`] +/// returns EOF. +/// * [`PipeWriter`] can be shared, and multiple processes or threads can write to it at once, but +/// writes (above a target-specific threshold) may have their data interleaved. +/// * [`PipeReader`] can be shared, and multiple processes or threads can read it at once. Any +/// given byte will only get consumed by one reader. There are no guarantees about data +/// interleaving. +/// * Portable applications cannot assume any atomicity of messages larger than a single byte. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `pipe` function on Unix and the +/// `CreatePipe` function on Windows. +/// +/// Note that this [may change in the future][changes]. +/// +/// # Capacity +/// +/// Pipe capacity is platform dependent. To quote the Linux [man page]: +/// +/// > Different implementations have different limits for the pipe capacity. Applications should +/// > not rely on a particular capacity: an application should be designed so that a reading process +/// > consumes data as soon as it is available, so that a writing process does not remain blocked. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(anonymous_pipe)] +/// # #[cfg(miri)] fn main() {} +/// # #[cfg(not(miri))] +/// # fn main() -> std::io::Result<()> { +/// use std::process::Command; +/// use std::io::{pipe, Read, Write}; +/// let (ping_rx, mut ping_tx) = pipe()?; +/// let (mut pong_rx, pong_tx) = pipe()?; +/// +/// // Spawn a process that echoes its input. +/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; +/// +/// ping_tx.write_all(b"hello")?; +/// // Close to unblock echo_server's reader. +/// drop(ping_tx); +/// +/// let mut buf = String::new(); +/// // Block until echo_server's writer is closed. +/// pong_rx.read_to_string(&mut buf)?; +/// assert_eq!(&buf, "hello"); +/// +/// echo_server.wait()?; +/// # Ok(()) +/// # } +/// ``` +/// [changes]: io#platform-specific-behavior +/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) +} + +/// Read end of an anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(pub(crate) AnonPipe); + +/// Write end of an anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(pub(crate) AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// use std::fs; + /// use std::io::{pipe, Write}; + /// use std::process::Command; + /// const NUM_SLOT: u8 = 2; + /// const NUM_PROC: u8 = 5; + /// const OUTPUT: &str = "work.txt"; + /// + /// let mut jobs = vec![]; + /// let (reader, mut writer) = pipe()?; + /// + /// // Write NUM_SLOT characters the pipe. + /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; + /// + /// // Spawn several processes that read a character from the pipe, do some work, then + /// // write back to the pipe. When the pipe is empty, the processes block, so only + /// // NUM_SLOT processes can be working at any given time. + /// for _ in 0..NUM_PROC { + /// jobs.push( + /// Command::new("bash") + /// .args(["-c", + /// &format!( + /// "read -n 1\n\ + /// echo -n 'x' >> '{OUTPUT}'\n\ + /// echo -n '|'", + /// ), + /// ]) + /// .stdin(reader.try_clone()?) + /// .stdout(writer.try_clone()?) + /// .spawn()?, + /// ); + /// } + /// + /// // Wait for all jobs to finish. + /// for mut job in jobs { + /// job.wait()?; + /// } + /// + /// // Check our work and clean up. + /// let xs = fs::read_to_string(OUTPUT)?; + /// fs::remove_file(OUTPUT)?; + /// assert_eq!(xs, "x".repeat(NUM_PROC.into())); + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result<Self> { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// use std::process::Command; + /// use std::io::{pipe, Read}; + /// let (mut reader, writer) = pipe()?; + /// + /// // Spawn a process that writes to stdout and stderr. + /// let mut peer = Command::new("bash") + /// .args([ + /// "-c", + /// "echo -n foo\n\ + /// echo -n bar >&2" + /// ]) + /// .stdout(writer.try_clone()?) + /// .stderr(writer) + /// .spawn()?; + /// + /// // Read and check the result. + /// let mut msg = String::new(); + /// reader.read_to_string(&mut msg)?; + /// assert_eq!(&msg, "foobar"); + /// + /// peer.wait()?; + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result<Self> { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} diff --git a/library/std/src/io/pipe/tests.rs b/library/std/src/io/pipe/tests.rs new file mode 100644 index 00000000000..f113b157459 --- /dev/null +++ b/library/std/src/io/pipe/tests.rs @@ -0,0 +1,18 @@ +use crate::io::{Read, Write, pipe}; + +#[test] +#[cfg(all(any(unix, windows), not(miri)))] +fn pipe_creation_clone_and_rw() { + let (rx, tx) = pipe().unwrap(); + + tx.try_clone().unwrap().write_all(b"12345").unwrap(); + drop(tx); + + let mut rx2 = rx.try_clone().unwrap(); + drop(rx); + + let mut s = String::new(); + rx2.read_to_string(&mut s).unwrap(); + drop(rx2); + assert_eq!(s, "12345"); +} diff --git a/library/std/src/io/stdio/tests.rs b/library/std/src/io/stdio/tests.rs index bf8f3a5adfb..e68d8c29fbc 100644 --- a/library/std/src/io/stdio/tests.rs +++ b/library/std/src/io/stdio/tests.rs @@ -159,7 +159,8 @@ where assert_eq!(rx2.recv().unwrap(), Release2); // release th2 th2.join().unwrap(); th1.join().unwrap(); - assert_eq!(*log.lock().unwrap(), [ - Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1 - ]); + assert_eq!( + *log.lock().unwrap(), + [Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1] + ); } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 85098b3bb18..f64f034cce7 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -7,7 +7,6 @@ use crate::mem::MaybeUninit; use crate::ops::Deref; #[test] -#[cfg_attr(target_os = "emscripten", ignore)] fn read_until() { let mut buf = Cursor::new(&b"12"[..]); let mut v = Vec::new(); @@ -359,7 +358,6 @@ fn chain_zero_length_read_is_not_eof() { } #[bench] -#[cfg_attr(target_os = "emscripten", ignore)] #[cfg_attr(miri, ignore)] // Miri isn't fast... fn bench_read_to_end(b: &mut test::Bencher) { b.iter(|| { @@ -823,20 +821,3 @@ fn try_oom_error() { let io_err = io::Error::from(reserve_err); assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind()); } - -#[test] -#[cfg(all(windows, unix, not(miri)))] -fn pipe_creation_clone_and_rw() { - let (rx, tx) = std::io::pipe().unwrap(); - - tx.try_clone().unwrap().write_all(b"12345").unwrap(); - drop(tx); - - let mut rx2 = rx.try_clone().unwrap(); - drop(rx); - - let mut s = String::new(); - rx2.read_to_string(&mut s).unwrap(); - drop(rx2); - assert_eq!(s, "12345"); -} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 39f234e4ba6..954a4182fbd 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -320,6 +320,8 @@ // Library features (core): // tidy-alphabetical-start #![feature(array_chunks)] +#![feature(bstr)] +#![feature(bstr_internals)] #![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -528,6 +530,8 @@ pub use core::option; pub use core::pin; #[stable(feature = "rust1", since = "1.0.0")] pub use core::ptr; +#[unstable(feature = "new_range_api", issue = "125687")] +pub use core::range; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; #[stable(feature = "rust1", since = "1.0.0")] @@ -581,6 +585,8 @@ pub mod f64; pub mod thread; pub mod ascii; pub mod backtrace; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod collections; pub mod env; pub mod error; @@ -733,27 +739,4 @@ mod sealed { #[cfg(test)] #[allow(dead_code)] // Not used in all configurations. -pub(crate) mod test_helpers { - /// Test-only replacement for `rand::thread_rng()`, which is unusable for - /// us, as we want to allow running stdlib tests on tier-3 targets which may - /// not have `getrandom` support. - /// - /// Does a bit of a song and dance to ensure that the seed is different on - /// each call (as some tests sadly rely on this), but doesn't try that hard. - /// - /// This is duplicated in the `core`, `alloc` test suites (as well as - /// `std`'s integration tests), but figuring out a mechanism to share these - /// seems far more painful than copy-pasting a 7 line function a couple - /// times, given that even under a perma-unstable feature, I don't think we - /// want to expose types from `rand` from `std`. - #[track_caller] - pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { - use core::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = crate::hash::RandomState::new().build_hasher(); - core::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - rand::SeedableRng::from_seed(seed) - } -} +pub(crate) mod test_helpers; diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 1b0d7f3dbf2..e0f9f0bb5ce 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -372,18 +372,3 @@ macro_rules! dbg { ($($crate::dbg!($val)),+,) }; } - -/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. -#[cfg(test)] -macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; - ($a:expr, $b:expr, $lim:expr) => {{ - let (a, b) = (&$a, &$b); - let diff = (*a - *b).abs(); - assert!( - diff < $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", - lim = $lim - ); - }}; -} diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index ba9c948a2e9..e8355cc31d7 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -6,8 +6,7 @@ mod tests; pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::sys::net::netc as c; -use crate::sys_common::net::LookupHost; +use crate::sys::net::{LookupHost, netc as c}; use crate::sys_common::{FromInner, IntoInner}; use crate::{io, iter, mem, option, slice, vec}; diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 67a0f7e439d..9b68f872955 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -15,7 +15,8 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index d318d457f35..a5c3983cd89 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -5,14 +5,15 @@ use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToS use crate::sync::atomic::{AtomicUsize, Ordering}; static PORT: AtomicUsize = AtomicUsize::new(0); +const BASE_PORT: u16 = 19600; pub fn next_test_ip4() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) } pub fn next_test_ip6() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0)) } @@ -30,31 +31,3 @@ pub fn tsa<A: ToSocketAddrs>(a: A) -> Result<Vec<SocketAddr>, String> { Err(e) => Err(e.to_string()), } } - -// The bots run multiple builds at the same time, and these builds -// all want to use ports. This function figures out which workspace -// it is running in and assigns a port range based on it. -fn base_port() -> u16 { - let cwd = if cfg!(target_env = "sgx") { - String::from("sgx") - } else { - env::current_dir().unwrap().into_os_string().into_string().unwrap() - }; - let dirs = [ - "32-opt", - "32-nopt", - "musl-64-opt", - "cross-opt", - "64-opt", - "64-nopt", - "64-opt-vg", - "64-debug-opt", - "all-opt", - "snap3", - "dist", - "sgx", - ]; - dirs.iter().enumerate().find(|&(_, dir)| cwd.contains(dir)).map(|p| p.0).unwrap_or(0) as u16 - * 1000 - + 19600 -} diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 674c5fb7d6e..3eb798ad34a 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -12,7 +12,8 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A UDP socket. diff --git a/library/std/src/num.rs b/library/std/src/num.rs index d2f679e7dde..ffb8789c906 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -6,9 +6,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "int_error_matching", since = "1.55.0")] pub use core::num::IntErrorKind; #[stable(feature = "generic_nonzero", since = "1.79.0")] @@ -29,28 +26,3 @@ pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError} pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize}; #[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; - -#[cfg(test)] -use crate::fmt; -#[cfg(test)] -use crate::ops::{Add, Div, Mul, Rem, Sub}; - -/// Helper function for testing numeric operations -#[cfg(test)] -pub fn test_num<T>(ten: T, two: T) -where - T: PartialEq - + Add<Output = T> - + Sub<Output = T> - + Mul<Output = T> - + Div<Output = T> - + Rem<Output = T> - + fmt::Debug - + Copy, -{ - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); - assert_eq!(ten.rem(two), ten % two); -} diff --git a/library/std/src/os/fd/net.rs b/library/std/src/os/fd/net.rs index 843f45f7f5f..34479ca0e19 100644 --- a/library/std/src/os/fd/net.rs +++ b/library/std/src/os/fd/net.rs @@ -1,6 +1,6 @@ use crate::os::fd::owned::OwnedFd; use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{net, sys}; macro_rules! impl_as_raw_fd { @@ -24,7 +24,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs index 7a774345b23..233bc885fc7 100644 --- a/library/std/src/os/hermit/io/net.rs +++ b/library/std/src/os/hermit/io/net.rs @@ -23,7 +23,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index b8601b533fe..b8c3440542d 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -48,7 +48,7 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, net, sys}; /// Raw file descriptors. @@ -387,7 +387,7 @@ macro_rules! impl_from_raw_fd { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> net::$t { let socket = unsafe { sys::net::Socket::from_raw_fd(fd) }; - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } )*}; diff --git a/library/std/src/os/unix/fs/tests.rs b/library/std/src/os/unix/fs/tests.rs index 67f607bd468..db9621c8c20 100644 --- a/library/std/src/os/unix/fs/tests.rs +++ b/library/std/src/os/unix/fs/tests.rs @@ -3,7 +3,7 @@ use super::*; #[test] fn read_vectored_at() { let msg = b"preadv is working!"; - let dir = crate::sys_common::io::test::tmpdir(); + let dir = crate::test_helpers::tmpdir(); let filename = dir.join("preadv.txt"); { @@ -31,7 +31,7 @@ fn read_vectored_at() { #[test] fn write_vectored_at() { let msg = b"pwritev is not working!"; - let dir = crate::sys_common::io::test::tmpdir(); + let dir = crate::test_helpers::tmpdir(); let filename = dir.join("preadv.txt"); { diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 21e2176185d..0398a535eb5 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -7,7 +7,7 @@ use crate::os::android::net::{SocketAddrExt, UnixSocketExt}; use crate::os::linux::net::{SocketAddrExt, UnixSocketExt}; #[cfg(any(target_os = "android", target_os = "linux"))] use crate::os::unix::io::AsRawFd; -use crate::sys_common::io::test::tmpdir; +use crate::test_helpers::tmpdir; use crate::thread; use crate::time::Duration; diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 6658248d574..c0517fab950 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -6,7 +6,7 @@ use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fs, io, net, ptr, sys}; /// Raw HANDLEs. @@ -262,7 +262,7 @@ impl FromRawSocket for net::TcpStream { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) + net::TcpStream::from_inner(sys::net::TcpStream::from_inner(sock)) } } } @@ -272,7 +272,7 @@ impl FromRawSocket for net::TcpListener { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) + net::TcpListener::from_inner(sys::net::TcpListener::from_inner(sock)) } } } @@ -282,7 +282,7 @@ impl FromRawSocket for net::UdpSocket { unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) + net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(sock)) } } } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 0277b79b8b6..201274cf03a 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -590,10 +590,10 @@ impl<'a> ProcThreadAttributeListBuilder<'a> { value_ptr: *const T, value_size: usize, ) -> Self { - self.attributes.insert(attribute, ProcThreadAttributeValue { - ptr: value_ptr.cast::<c_void>(), - size: value_size, - }); + self.attributes.insert( + attribute, + ProcThreadAttributeValue { ptr: value_ptr.cast::<c_void>(), size: value_size }, + ); self } diff --git a/library/std/src/os/xous/ffi/definitions.rs b/library/std/src/os/xous/ffi/definitions.rs index 1b16849af03..345005bcc78 100644 --- a/library/std/src/os/xous/ffi/definitions.rs +++ b/library/std/src/os/xous/ffi/definitions.rs @@ -126,36 +126,42 @@ impl From<i32> for Error { #[stable(feature = "rust1", since = "1.0.0")] impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", match self { - Error::NoError => "no error occurred", - Error::BadAlignment => "memory was not properly aligned", - Error::BadAddress => "an invalid address was supplied", - Error::OutOfMemory => "the process or service has run out of memory", - Error::MemoryInUse => "the requested address is in use", - Error::InterruptNotFound => "the requested interrupt does not exist on this platform", - Error::InterruptInUse => "the requested interrupt is currently in use", - Error::InvalidString => "the specified string was not formatted correctly", - Error::ServerExists => "a server with that address already exists", - Error::ServerNotFound => "the requetsed server could not be found", - Error::ProcessNotFound => "the target process does not exist", - Error::ProcessNotChild => "the requested operation can only be done on child processes", - Error::ProcessTerminated => "the target process has crashed", - Error::Timeout => "the requested operation timed out", - Error::InternalError => "an internal error occurred", - Error::ServerQueueFull => "the server has too many pending messages", - Error::ThreadNotAvailable => "the specified thread does not exist", - Error::UnhandledSyscall => "the kernel did not recognize that syscall", - Error::InvalidSyscall => "the syscall had incorrect parameters", - Error::ShareViolation => "an attempt was made to share memory twice", - Error::InvalidThread => "tried to resume a thread that was not ready", - Error::InvalidPid => "kernel attempted to use a pid that was not valid", - Error::AccessDenied => "no permission to perform the requested operation", - Error::UseBeforeInit => "attempt to use a service before initialization finished", - Error::DoubleFree => "the requested resource was freed twice", - Error::DebugInProgress => "kernel attempted to activate a thread being debugged", - Error::InvalidLimit => "process attempted to adjust an invalid limit", - Error::UnknownError => "an unknown error occurred", - }) + write!( + f, + "{}", + match self { + Error::NoError => "no error occurred", + Error::BadAlignment => "memory was not properly aligned", + Error::BadAddress => "an invalid address was supplied", + Error::OutOfMemory => "the process or service has run out of memory", + Error::MemoryInUse => "the requested address is in use", + Error::InterruptNotFound => + "the requested interrupt does not exist on this platform", + Error::InterruptInUse => "the requested interrupt is currently in use", + Error::InvalidString => "the specified string was not formatted correctly", + Error::ServerExists => "a server with that address already exists", + Error::ServerNotFound => "the requetsed server could not be found", + Error::ProcessNotFound => "the target process does not exist", + Error::ProcessNotChild => + "the requested operation can only be done on child processes", + Error::ProcessTerminated => "the target process has crashed", + Error::Timeout => "the requested operation timed out", + Error::InternalError => "an internal error occurred", + Error::ServerQueueFull => "the server has too many pending messages", + Error::ThreadNotAvailable => "the specified thread does not exist", + Error::UnhandledSyscall => "the kernel did not recognize that syscall", + Error::InvalidSyscall => "the syscall had incorrect parameters", + Error::ShareViolation => "an attempt was made to share memory twice", + Error::InvalidThread => "tried to resume a thread that was not ready", + Error::InvalidPid => "kernel attempted to use a pid that was not valid", + Error::AccessDenied => "no permission to perform the requested operation", + Error::UseBeforeInit => "attempt to use a service before initialization finished", + Error::DoubleFree => "the requested resource was freed twice", + Error::DebugInProgress => "kernel attempted to activate a thread being debugged", + Error::InvalidLimit => "process attempted to adjust an invalid limit", + Error::UnknownError => "an unknown error occurred", + } + ) } } diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index d649357a56d..153189b8b03 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -529,6 +529,3 @@ pub fn get_backtrace_style() -> Option<BacktraceStyle> { Err(new) => BacktraceStyle::from_u8(new), } } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8e50bf11dd0..b47b41d4bc5 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -54,11 +54,11 @@ pub static EMPTY_PANIC: fn(&'static str) -> ! = // One day this may look a little less ad-hoc with the compiler helping out to // hook up these functions, but it is not this day! #[allow(improper_ctypes)] -extern "C" { +unsafe extern "C" { fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); } -extern "Rust" { +unsafe extern "Rust" { /// `PanicPayload` lazily performs allocation only when needed (this avoids /// allocations when using the "abort" panic runtime). fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32; diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 7fd08a97f1f..97e17acadea 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -67,9 +67,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![deny(unsafe_op_in_unsafe_fn)] -#[cfg(test)] -mod tests; - use core::clone::CloneToUninit; use crate::borrow::{Borrow, Cow}; diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 4ec328208f0..14e6c2715df 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -120,16 +120,6 @@ mod common; pub mod v1 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - - // Do not `doc(inline)` these `doc(hidden)` items. - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[allow(deprecated)] - pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; } /// The 2015 version of the prelude of The Rust Standard Library. diff --git a/library/std/src/process.rs b/library/std/src/process.rs index e0dd2e14817..fd0fd1cb755 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -2318,14 +2318,10 @@ pub fn exit(code: i32) -> ! { /// Terminates the process in an abnormal fashion. /// /// The function will never return and will immediately terminate the current -/// process in a platform specific "abnormal" manner. -/// -/// Note that because this function never returns, and that it terminates the -/// process, no destructors on the current stack or any other thread's stack -/// will be run. -/// -/// Rust IO buffers (eg, from `BufWriter`) will not be flushed. -/// Likewise, C stdio buffers will (on most platforms) not be flushed. +/// process in a platform specific "abnormal" manner. As a consequence, +/// no destructors on the current stack or any other thread's stack +/// will be run, Rust IO buffers (eg, from `BufWriter`) will not be flushed, +/// and C stdio buffers will (on most platforms) not be flushed. /// /// This is in contrast to the default behavior of [`panic!`] which unwinds /// the current thread's stack and calls all destructors. diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index e8cbfe337bc..69273d863eb 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -391,145 +391,6 @@ fn test_interior_nul_in_env_value_is_error() { } } -/// Tests that process creation flags work by debugging a process. -/// Other creation flags make it hard or impossible to detect -/// behavioral changes in the process. -#[test] -#[cfg(windows)] -fn test_creation_flags() { - use crate::os::windows::process::CommandExt; - use crate::sys::c::{BOOL, INFINITE}; - #[repr(C)] - struct DEBUG_EVENT { - pub event_code: u32, - pub process_id: u32, - pub thread_id: u32, - // This is a union in the real struct, but we don't - // need this data for the purposes of this test. - pub _junk: [u8; 164], - } - - extern "system" { - fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: u32) -> BOOL; - fn ContinueDebugEvent(dwProcessId: u32, dwThreadId: u32, dwContinueStatus: u32) -> BOOL; - } - - const DEBUG_PROCESS: u32 = 1; - const EXIT_PROCESS_DEBUG_EVENT: u32 = 5; - const DBG_EXCEPTION_NOT_HANDLED: u32 = 0x80010001; - - let mut child = - Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap(); - child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); - let mut events = 0; - let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] }; - loop { - if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 { - panic!("WaitForDebugEvent failed!"); - } - events += 1; - - if event.event_code == EXIT_PROCESS_DEBUG_EVENT { - break; - } - - if unsafe { - ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED) - } == 0 - { - panic!("ContinueDebugEvent failed!"); - } - } - assert!(events > 0); -} - -/// Tests proc thread attributes by spawning a process with a custom parent process, -/// then comparing the parent process ID with the expected parent process ID. -#[test] -#[cfg(windows)] -fn test_proc_thread_attributes() { - use crate::mem; - use crate::os::windows::io::AsRawHandle; - use crate::os::windows::process::{CommandExt, ProcThreadAttributeList}; - use crate::sys::c::{BOOL, CloseHandle, HANDLE}; - use crate::sys::cvt; - - #[repr(C)] - #[allow(non_snake_case)] - struct PROCESSENTRY32W { - dwSize: u32, - cntUsage: u32, - th32ProcessID: u32, - th32DefaultHeapID: usize, - th32ModuleID: u32, - cntThreads: u32, - th32ParentProcessID: u32, - pcPriClassBase: i32, - dwFlags: u32, - szExeFile: [u16; 260], - } - - extern "system" { - fn CreateToolhelp32Snapshot(dwflags: u32, th32processid: u32) -> HANDLE; - fn Process32First(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - fn Process32Next(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - } - - const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; - const TH32CS_SNAPPROCESS: u32 = 0x00000002; - - struct ProcessDropGuard(crate::process::Child); - - impl Drop for ProcessDropGuard { - fn drop(&mut self) { - let _ = self.0.kill(); - } - } - - let parent = ProcessDropGuard(Command::new("cmd").spawn().unwrap()); - - let mut child_cmd = Command::new("cmd"); - - let parent_process_handle = parent.0.as_raw_handle(); - - let mut attribute_list = ProcThreadAttributeList::build() - .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle) - .finish() - .unwrap(); - - let child = ProcessDropGuard(child_cmd.spawn_with_attributes(&mut attribute_list).unwrap()); - - let h_snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; - - let mut process_entry = PROCESSENTRY32W { - dwSize: mem::size_of::<PROCESSENTRY32W>() as u32, - cntUsage: 0, - th32ProcessID: 0, - th32DefaultHeapID: 0, - th32ModuleID: 0, - cntThreads: 0, - th32ParentProcessID: 0, - pcPriClassBase: 0, - dwFlags: 0, - szExeFile: [0; 260], - }; - - unsafe { cvt(Process32First(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - - loop { - if child.0.id() == process_entry.th32ProcessID { - break; - } - unsafe { cvt(Process32Next(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - } - - unsafe { cvt(CloseHandle(h_snapshot)) }.unwrap(); - - assert_eq!(parent.0.id(), process_entry.th32ParentProcessID); - - drop(child) -} - #[test] fn test_command_implements_send_sync() { fn take_send_sync_type<T: Send + Sync>(_: T) {} @@ -688,7 +549,7 @@ fn debug_print() { #[test] #[cfg(windows)] fn run_bat_script() { - let tempdir = crate::sys_common::io::test::tmpdir(); + let tempdir = crate::test_helpers::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); @@ -707,7 +568,7 @@ fn run_bat_script() { #[test] #[cfg(windows)] fn run_canonical_bat_script() { - let tempdir = crate::sys_common::io::test::tmpdir(); + let tempdir = crate::test_helpers::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 862753e4765..067ff66d9af 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -1,6 +1,3 @@ -#[cfg(test)] -mod tests; - use crate::fmt; // FIXME(nonpoison_mutex,nonpoison_condvar): switch to nonpoison versions once they are available use crate::sync::{Condvar, Mutex}; diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 98c83d8d326..78cf8841efe 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -350,6 +350,3 @@ unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {} impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {} #[stable(feature = "lazy_cell", since = "1.80.0")] impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {} - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 0cf4902d6d5..8caa2dcfad9 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -18,7 +18,7 @@ //! infinite buffer. //! //! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(SyncSender, Receiver)` tuple where the storage for pending +//! return a `(Sender, Receiver)` tuple where the storage for pending //! messages is a pre-allocated buffer of a fixed size. All sends will be //! **synchronous** by blocking until there is buffer space available. Note //! that a bound of 0 is allowed, causing the channel to become a "rendezvous" @@ -360,9 +360,17 @@ impl<T> Sender<T> { /// that a return value of [`Err`] means that the data will never be /// received, but a return value of [`Ok`] does *not* mean that the data /// will be received. It is possible for the corresponding receiver to - /// hang up immediately after this function returns [`Ok`]. + /// hang up immediately after this function returns [`Ok`]. However, if + /// the channel is zero-capacity, it acts as a rendezvous channel and a + /// return value of [`Ok`] means that the data has been received. /// - /// This method will never block the current thread. + /// If the channel is full and not disconnected, this call will block until + /// the send operation can proceed. If the channel becomes disconnected, + /// this call will wake up and return an error. The returned error contains + /// the original message. + /// + /// If called on a zero-capacity channel, this method will wait for a receive + /// operation to appear on the other side of the channel. /// /// # Examples /// @@ -608,9 +616,9 @@ impl<T> Sender<T> { #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn same_channel(&self, other: &Sender<T>) -> bool { match (&self.flavor, &other.flavor) { - (SenderFlavor::Array(ref a), SenderFlavor::Array(ref b)) => a == b, - (SenderFlavor::List(ref a), SenderFlavor::List(ref b)) => a == b, - (SenderFlavor::Zero(ref a), SenderFlavor::Zero(ref b)) => a == b, + (SenderFlavor::Array(a), SenderFlavor::Array(b)) => a == b, + (SenderFlavor::List(a), SenderFlavor::List(b)) => a == b, + (SenderFlavor::Zero(a), SenderFlavor::Zero(b)) => a == b, _ => false, } } @@ -650,7 +658,7 @@ impl<T> fmt::Debug for Sender<T> { } /// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// Different threads can share this [`Sender`] by cloning it. +/// Different threads can share this [`Receiver`] by cloning it. /// /// Messages sent to the channel can be retrieved using [`recv`]. /// diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc.rs index c86b546e011..f942937c14d 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc.rs @@ -137,12 +137,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod sync_tests; - // MPSC channels are built as a wrapper around MPMC channels, which // were ported from the `crossbeam-channel` crate. MPMC channels are // not exposed publicly, but if you are curious about the implementation, @@ -737,9 +731,10 @@ impl<T> SyncSender<T> { // Attempts to send for a value on this receiver, returning an error if the // corresponding channel has hung up, or if it waits more than `timeout`. // - // This method is currently private and only used for tests. - #[allow(unused)] - fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError<T>> { + // This method is currently only used for tests. + #[unstable(issue = "none", feature = "std_internals")] + #[doc(hidden)] + pub fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError<T>> { self.inner.send_timeout(t, timeout) } } diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 49f2dafd8fd..21e6b65a744 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -13,6 +13,9 @@ use crate::sync::Once; /// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock /// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`]. /// +/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes +/// initialized once written. +/// /// [`OnceCell`]: crate::cell::OnceCell /// [`LazyLock<T, F>`]: crate::sync::LazyLock /// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new @@ -126,7 +129,7 @@ pub struct OnceLock<T> { } impl<T> OnceLock<T> { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -141,8 +144,8 @@ impl<T> OnceLock<T> { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is empty, or being initialized. This - /// method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -156,7 +159,8 @@ impl<T> OnceLock<T> { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. This method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { @@ -174,8 +178,6 @@ impl<T> OnceLock<T> { /// /// Waiting for a computation on another thread to finish: /// ```rust - /// #![feature(once_wait)] - /// /// use std::thread; /// use std::sync::OnceLock; /// @@ -189,19 +191,20 @@ impl<T> OnceLock<T> { /// }) /// ``` #[inline] - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait(&self) -> &T { self.once.wait_force(); unsafe { self.get_unchecked() } } - /// Sets the contents of this cell to `value`. + /// Initializes the contents of the cell to `value`. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `set` returns, though not necessarily the one provided. /// - /// Returns `Ok(())` if the cell's value was set by this call. + /// Returns `Ok(())` if the cell was uninitialized and + /// `Err(value)` if the cell was already initialized. /// /// # Examples /// @@ -230,13 +233,15 @@ impl<T> OnceLock<T> { } } - /// Sets the contents of this cell to `value` if the cell was empty, then - /// returns a reference to it. + /// Initializes the contents of the cell to `value` if the cell was uninitialized, + /// then returns a reference to it. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `try_insert` returns, though not necessarily the + /// one provided. /// - /// Returns `Ok(&value)` if the cell was empty and `Err(¤t_value, value)` if it was full. + /// Returns `Ok(&value)` if the cell was uninitialized and + /// `Err((¤t_value, value))` if it was already initialized. /// /// # Examples /// @@ -269,8 +274,8 @@ impl<T> OnceLock<T> { } } - /// Gets the contents of the cell, initializing it with `f` if the cell - /// was empty. + /// Gets the contents of the cell, initializing it to `f()` if the cell + /// was uninitialized. /// /// Many threads may call `get_or_init` concurrently with different /// initializing functions, but it is guaranteed that only one function @@ -278,7 +283,7 @@ impl<T> OnceLock<T> { /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. The @@ -308,13 +313,13 @@ impl<T> OnceLock<T> { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. + /// it to `f()` if the cell was uninitialized. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -345,13 +350,13 @@ impl<T> OnceLock<T> { } } - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. + /// Gets the contents of the cell, initializing it to `f()` if + /// the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. @@ -397,14 +402,14 @@ impl<T> OnceLock<T> { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. If the cell was empty and `f` failed, - /// an error is returned. + /// it to `f()` if the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// # Examples @@ -416,7 +421,7 @@ impl<T> OnceLock<T> { /// /// let mut cell: OnceLock<u32> = OnceLock::new(); /// - /// // Failed initializers do not change the value + /// // Failed attempts to initialize the cell do not change its contents /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// @@ -440,7 +445,7 @@ impl<T> OnceLock<T> { } /// Consumes the `OnceLock`, returning the wrapped value. Returns - /// `None` if the cell was empty. + /// `None` if the cell was uninitialized. /// /// # Examples /// @@ -462,7 +467,7 @@ impl<T> OnceLock<T> { /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceLock` hasn't been initialized. + /// Has no effect and returns `None` if the `OnceLock` was uninitialized. /// /// Safety is guaranteed by requiring a mutable reference. /// @@ -528,7 +533,7 @@ impl<T> OnceLock<T> { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); @@ -537,7 +542,7 @@ impl<T> OnceLock<T> { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked_mut(&mut self) -> &mut T { debug_assert!(self.is_initialized()); @@ -562,7 +567,7 @@ impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {} #[stable(feature = "once_cell", since = "1.70.0")] impl<T> Default for OnceLock<T> { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. /// /// # Example /// @@ -676,6 +681,3 @@ unsafe impl<#[may_dangle] T> Drop for OnceLock<T> { } } } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sync/poison/condvar.rs b/library/std/src/sync/poison/condvar.rs index a6e2389c93b..7f0f3f652bc 100644 --- a/library/std/src/sync/poison/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -1,6 +1,3 @@ -#[cfg(test)] -mod tests; - use crate::fmt; use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex}; use crate::sys::sync as sys; diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 01ef71a187f..9362c764173 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; @@ -181,10 +178,29 @@ pub struct Mutex<T: ?Sized> { data: UnsafeCell<T>, } -// these are the only places where `T: Send` matters; all other -// functionality works fine on a single thread. +/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire +/// the owned `T` from the `Mutex` via [`into_inner`]. +/// +/// [`into_inner`]: Mutex::into_inner #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: ?Sized + Send> Send for Mutex<T> {} + +/// `T` must be `Send` for [`Mutex`] to be `Sync`. +/// This ensures that the protected data can be accessed safely from multiple threads +/// without causing data races or other unsafe behavior. +/// +/// [`Mutex<T>`] provides mutable access to `T` to one thread at a time. However, it's essential +/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in +/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, +/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap +/// allocation with a non-atomic reference count. If we were to use `Mutex<Rc<_>>`, it would +/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable +/// to potential data races. +/// +/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available +/// to one thread at a time if `T` is not `Sync`. +/// +/// [`Rc`]: crate::rc::Rc #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {} @@ -211,8 +227,17 @@ pub struct MutexGuard<'a, T: ?Sized + 'a> { poison: poison::Guard, } +/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// +/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to +/// release mutex locks on the same thread they were acquired. +/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from +/// another thread. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> !Send for MutexGuard<'_, T> {} + +/// `T` must be `Sync` for a [`MutexGuard<T>`] to be `Sync` +/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). #[stable(feature = "mutexguard", since = "1.19.0")] unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {} diff --git a/library/std/src/sync/poison/once.rs b/library/std/src/sync/poison/once.rs index 27db4b634fb..d2938b7a0c1 100644 --- a/library/std/src/sync/poison/once.rs +++ b/library/std/src/sync/poison/once.rs @@ -3,9 +3,6 @@ //! This primitive is meant to be used to run one-time initialization. An //! example use case would be for initializing an FFI library. -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sys::sync as sys; @@ -269,8 +266,6 @@ impl Once { /// # Example /// /// ```rust - /// #![feature(once_wait)] - /// /// use std::sync::Once; /// use std::thread; /// @@ -289,7 +284,7 @@ impl Once { /// If this [`Once`] has been poisoned because an initialization closure has /// panicked, this method will also panic. Use [`wait_force`](Self::wait_force) /// if this behavior is not desired. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait(&self) { if !self.inner.is_completed() { self.inner.wait(false); @@ -298,7 +293,7 @@ impl Once { /// Blocks the current thread until initialization has completed, ignoring /// poisoning. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait_force(&self) { if !self.inner.is_completed() { self.inner.wait(true); diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 1519baf99a8..f9d9321f5f2 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 0140e0d2129..e009eb410ef 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use cfg_if::cfg_if; use crate::cell::UnsafeCell; @@ -324,7 +321,10 @@ impl<T: ?Sized> ReentrantLock<T> { /// Otherwise, an RAII guard is returned. /// /// This function does not block. - pub(crate) fn try_lock(&self) -> Option<ReentrantLockGuard<'_, T>> { + // FIXME maybe make it a public part of the API? + #[unstable(issue = "none", feature = "std_internals")] + #[doc(hidden)] + pub fn try_lock(&self) -> Option<ReentrantLockGuard<'_, T>> { let this_thread = current_id(); // Safety: We only touch lock_count when we own the inner mutex. // Additionally, we only call `self.owner.set()` while holding diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index a308fafc68b..53fbc9529e5 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -1,6 +1,6 @@ //! This is an implementation of a global allocator on wasm targets when -//! emscripten is not in use. In that situation there's no actual runtime for us -//! to lean on for allocation, so instead we provide our own! +//! emscripten or wasi is not in use. In that situation there's no actual runtime +//! for us to lean on for allocation, so instead we provide our own! //! //! The wasm instruction set has two instructions for getting the current //! amount of memory and growing the amount of memory. These instructions are the diff --git a/library/std/src/sys/alloc/xous.rs b/library/std/src/sys/alloc/xous.rs index 321d30e0b11..9d22e16fdf2 100644 --- a/library/std/src/sys/alloc/xous.rs +++ b/library/std/src/sys/alloc/xous.rs @@ -8,7 +8,7 @@ use crate::alloc::{GlobalAlloc, Layout, System}; static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new(); #[cfg(test)] -extern "Rust" { +unsafe extern "Rust" { #[link_name = "_ZN16__rust_internals3std3sys4xous5alloc8DLMALLOCE"] static mut DLMALLOC: dlmalloc::Dlmalloc; } diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index ee36127cfdf..e3f41f4fdbb 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -2,7 +2,7 @@ // These symbols are all defined by `libm`, // or by `compiler-builtins` on unsupported platforms. -extern "C" { +unsafe extern "C" { pub fn acos(n: f64) -> f64; pub fn asin(n: f64) -> f64; pub fn atan(n: f64) -> f64; diff --git a/library/std/src/sys/pal/unix/io.rs b/library/std/src/sys/io/io_slice/iovec.rs index 0d5a152dc0d..072191315f7 100644 --- a/library/std/src/sys/pal/unix/io.rs +++ b/library/std/src/sys/io/io_slice/iovec.rs @@ -1,8 +1,13 @@ -use libc::{c_void, iovec}; +#[cfg(target_os = "hermit")] +use hermit_abi::iovec; +#[cfg(target_family = "unix")] +use libc::iovec; +use crate::ffi::c_void; use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; use crate::slice; +#[cfg(target_os = "solid_asp3")] +use crate::sys::pal::abi::sockets::iovec; #[derive(Copy, Clone)] #[repr(transparent)] @@ -80,8 +85,3 @@ impl<'a> IoSliceMut<'a> { unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/pal/unsupported/io.rs b/library/std/src/sys/io/io_slice/unsupported.rs index 604735d32d5..1572cac6cd7 100644 --- a/library/std/src/sys/pal/unsupported/io.rs +++ b/library/std/src/sys/io/io_slice/unsupported.rs @@ -50,7 +50,3 @@ impl<'a> IoSliceMut<'a> { self.0 } } - -pub fn is_terminal<T>(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/pal/wasi/io.rs b/library/std/src/sys/io/io_slice/wasi.rs index 57f81bc6257..87acbbd924e 100644 --- a/library/std/src/sys/pal/wasi/io.rs +++ b/library/std/src/sys/io/io_slice/wasi.rs @@ -1,7 +1,4 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; use crate::slice; #[derive(Copy, Clone)] @@ -77,8 +74,3 @@ impl<'a> IoSliceMut<'a> { unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } } } - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/pal/solid/io.rs b/library/std/src/sys/io/io_slice/windows.rs index 9ef4b7049b6..c3d8ec87c19 100644 --- a/library/std/src/sys/pal/solid/io.rs +++ b/library/std/src/sys/io/io_slice/windows.rs @@ -1,86 +1,82 @@ -use libc::c_void; - -use super::abi::sockets::iovec; use crate::marker::PhantomData; use crate::slice; +use crate::sys::c; #[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { - vec: iovec, + vec: c::WSABUF, _p: PhantomData<&'a [u8]>, } impl<'a> IoSlice<'a> { #[inline] pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + assert!(buf.len() <= u32::MAX as usize); IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, _p: PhantomData, } } #[inline] pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { + if (self.vec.len as usize) < n { panic!("advancing IoSlice beyond its length"); } unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); } } #[inline] pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } } } #[repr(transparent)] pub struct IoSliceMut<'a> { - vec: iovec, + vec: c::WSABUF, _p: PhantomData<&'a mut [u8]>, } impl<'a> IoSliceMut<'a> { #[inline] pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + assert!(buf.len() <= u32::MAX as usize); IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, _p: PhantomData, } } #[inline] pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { + if (self.vec.len as usize) < n { panic!("advancing IoSliceMut beyond its length"); } unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); } } #[inline] pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } } #[inline] pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } } #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } } } - -pub fn is_terminal<T>(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/io/is_terminal/hermit.rs b/library/std/src/sys/io/is_terminal/hermit.rs new file mode 100644 index 00000000000..61bdb6f0a54 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/hermit.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + hermit_abi::isatty(fd.as_raw_fd()) +} diff --git a/library/std/src/sys/io/is_terminal/isatty.rs b/library/std/src/sys/io/is_terminal/isatty.rs new file mode 100644 index 00000000000..6e0b46211b9 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/isatty.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + unsafe { libc::isatty(fd.as_raw_fd()) != 0 } +} diff --git a/library/std/src/sys/io/is_terminal/unsupported.rs b/library/std/src/sys/io/is_terminal/unsupported.rs new file mode 100644 index 00000000000..cee4add32fb --- /dev/null +++ b/library/std/src/sys/io/is_terminal/unsupported.rs @@ -0,0 +1,3 @@ +pub fn is_terminal<T>(_: &T) -> bool { + false +} diff --git a/library/std/src/sys/pal/windows/io.rs b/library/std/src/sys/io/is_terminal/windows.rs index f2865d2ffc1..3ec18fb47b9 100644 --- a/library/std/src/sys/pal/windows/io.rs +++ b/library/std/src/sys/io/is_terminal/windows.rs @@ -1,90 +1,8 @@ -use core::ffi::c_void; - -use crate::marker::PhantomData; +use crate::ffi::c_void; use crate::mem::size_of; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; -use crate::slice; use crate::sys::c; -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSlice { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSliceMut { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } -} - pub fn is_terminal(h: &impl AsHandle) -> bool { handle_is_console(h.as_handle()) } diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs new file mode 100644 index 00000000000..e00b479109f --- /dev/null +++ b/library/std/src/sys/io/mod.rs @@ -0,0 +1,44 @@ +#![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"))] { + mod iovec; + pub use iovec::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else { + mod unsupported; + pub use unsupported::*; + } + } +} + +mod is_terminal { + cfg_if::cfg_if! { + if #[cfg(any(target_family = "unix", target_os = "wasi"))] { + mod isatty; + pub use isatty::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use io_slice::{IoSlice, IoSliceMut}; +pub use is_terminal::is_terminal; + +// Bare metal platforms usually have very small amounts of RAM +// (in the order of hundreds of KB) +pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index f17dd47dece..1032fcba5e2 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -12,6 +12,8 @@ pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; +pub mod io; +pub mod net; pub mod os_str; pub mod path; pub mod random; diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/net/connection/sgx.rs index c966886d163..b390a5eac5f 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -1,7 +1,7 @@ -use super::abi::usercalls; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; +use crate::sys::abi::usercalls; use crate::sys::fd::FileDesc; use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; use crate::time::Duration; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys/net/connection/socket.rs index 74306978d22..6fe3430b53f 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys/net/connection/socket.rs @@ -5,12 +5,32 @@ use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::common::small_c_string::run_with_cstr; -use crate::sys::net::{Socket, cvt, cvt_gai, cvt_r, init, netc as c, wrlen_t}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; cfg_if::cfg_if! { + if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { + mod wasip2; + pub use wasip2::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } +} + +use netc as c; + +cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", @@ -24,11 +44,11 @@ cfg_if::cfg_if! { target_os = "nuttx", target_vendor = "apple", ))] { - use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; } else { - use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP; + use c::IPV6_ADD_MEMBERSHIP; + use c::IPV6_DROP_MEMBERSHIP; } } diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/net/connection/socket/hermit.rs index 4e12374203e..42179dcc915 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -2,21 +2,20 @@ use core::ffi::c_int; -use super::fd::FileDesc; +pub(crate) use hermit_abi as netc; + use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::time::Instant; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +pub use crate::sys::{cvt, cvt_r}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem}; -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as netc; - -pub use crate::sys::{cvt, cvt_r}; - +#[expect(non_camel_case_types)] pub type wrlen_t = usize; pub fn cvt_gai(err: i32) -> io::Result<()> { diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/net/connection/socket/solid.rs index 5f6436807e2..f85ecbb883e 100644 --- a/library/std/src/sys/pal/solid/net.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -1,24 +1,23 @@ use libc::{c_int, c_void, size_t}; use self::netc::{MSG_PEEK, sockaddr, socklen_t}; -use super::abi; use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::abi; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, str}; pub mod netc { - pub use super::super::abi::sockets::*; + pub use crate::sys::abi::sockets::*; } +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; -const READ_LIMIT: usize = libc::ssize_t::MAX as usize; - const fn max_iov() -> usize { // Judging by the source code, it's unlimited, but specify a lower // value just in case. @@ -78,7 +77,7 @@ fn last_error() -> io::Error { io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) } -pub(super) fn error_name(er: abi::ER) -> Option<&'static str> { +pub fn error_name(er: abi::ER) -> Option<&'static str> { unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() } @@ -87,7 +86,7 @@ pub fn is_interrupted(er: abi::ER) -> bool { er == netc::SOLID_NET_ERR_BASE - libc::EINTR } -pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind { +pub fn decode_error_kind(er: abi::ER) -> ErrorKind { let errno = netc::SOLID_NET_ERR_BASE - er; match errno as libc::c_int { libc::ECONNREFUSED => ErrorKind::ConnectionRefused, @@ -268,17 +267,6 @@ impl Socket { self.recv_from_with_flags(buf, MSG_PEEK) } - pub fn write(&self, buf: &[u8]) -> io::Result<usize> { - let ret = cvt(unsafe { - netc::write( - self.as_raw_fd(), - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let ret = cvt(unsafe { netc::writev( diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs index fc236b8027b..fc236b8027b 100644 --- a/library/std/src/sys_common/net/tests.rs +++ b/library/std/src/sys/net/connection/socket/tests.rs diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/net/connection/socket/unix.rs index d73b9fd5eb8..da631605527 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -5,8 +5,8 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::pal::unix::IsMinusOne; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::pal::IsMinusOne; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem}; @@ -19,11 +19,11 @@ cfg_if::cfg_if! { } } -pub use crate::sys::{cvt, cvt_r}; +pub(crate) use libc as netc; -#[allow(unused_extern_crates)] -pub extern crate libc as netc; +pub use crate::sys::{cvt, cvt_r}; +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; pub struct Socket(FileDesc); diff --git a/library/std/src/sys/pal/wasip2/net.rs b/library/std/src/sys/net/connection/socket/wasip2.rs index f009a51821f..9d1c05a473e 100644 --- a/library/std/src/sys/pal/wasip2/net.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -6,8 +6,8 @@ use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::unsupported; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem, str}; diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/net/connection/socket/windows.rs index a92853c642c..80cf37eaf05 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -9,7 +9,7 @@ use crate::os::windows::io::{ }; use crate::sync::OnceLock; use crate::sys::c; -use crate::sys_common::{AsInner, FromInner, IntoInner, net}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -110,6 +110,7 @@ pub mod netc { } } +#[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); static WSA_CLEANUP: OnceLock<unsafe extern "system" fn() -> i32> = OnceLock::new(); @@ -400,12 +401,12 @@ impl Socket { let error = unsafe { c::WSAGetLastError() }; if error == c::WSAESHUTDOWN { - Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?)) + Ok((0, super::sockaddr_to_addr(&storage, addrlen as usize)?)) } else { Err(io::Error::from_raw_os_error(error)) } } - _ => Ok((result as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)), + _ => Ok((result as usize, super::sockaddr_to_addr(&storage, addrlen as usize)?)), } } @@ -450,11 +451,11 @@ impl Socket { } None => 0, }; - net::setsockopt(self, c::SOL_SOCKET, kind, timeout) + super::setsockopt(self, c::SOL_SOCKET, kind, timeout) } pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> { - let raw: u32 = net::getsockopt(self, c::SOL_SOCKET, kind)?; + let raw: u32 = super::getsockopt(self, c::SOL_SOCKET, kind)?; if raw == 0 { Ok(None) } else { @@ -487,26 +488,26 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; - net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + super::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) } pub fn linger(&self) -> io::Result<Option<Duration>> { - let val: c::LINGER = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = super::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + super::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } pub fn nodelay(&self) -> io::Result<bool> { - let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + let raw: c::BOOL = super::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; Ok(raw != 0) } pub fn take_error(&self) -> io::Result<Option<io::Error>> { - let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + let raw: c_int = super::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 87e6106468f..87e6106468f 100644 --- a/library/std/src/sys/pal/unsupported/net.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs diff --git a/library/std/src/sys/pal/teeos/net.rs b/library/std/src/sys/net/connection/unsupported.rs index fed95205027..87e6106468f 100644 --- a/library/std/src/sys/pal/teeos/net.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -346,6 +346,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -358,6 +359,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, @@ -365,5 +367,3 @@ pub mod netc { pub sin6_scope_id: u32, } } - -pub type Socket = UdpSocket; diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/net/connection/wasip1.rs index a6486799828..27e3a528af4 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -1,12 +1,11 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use super::err2io; -use super::fd::WasiFd; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::unsupported; +use crate::sys::fd::WasiFd; +use crate::sys::{err2io, unsupported}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs index ff6e49ed2d4..ff6e49ed2d4 100644 --- a/library/std/src/sys/pal/xous/net/dns.rs +++ b/library/std/src/sys/net/connection/xous/dns.rs diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs index 3e18ed24208..3e18ed24208 100644 --- a/library/std/src/sys/pal/xous/net/mod.rs +++ b/library/std/src/sys/net/connection/xous/mod.rs diff --git a/library/std/src/sys/pal/xous/net/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs index 640a02a64f5..640a02a64f5 100644 --- a/library/std/src/sys/pal/xous/net/tcplistener.rs +++ b/library/std/src/sys/net/connection/xous/tcplistener.rs diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs index 572dd6b3b63..572dd6b3b63 100644 --- a/library/std/src/sys/pal/xous/net/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs index 1b7ecac6d3a..1b7ecac6d3a 100644 --- a/library/std/src/sys/pal/xous/net/udp.rs +++ b/library/std/src/sys/net/connection/xous/udp.rs diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs new file mode 100644 index 00000000000..646679a1cc8 --- /dev/null +++ b/library/std/src/sys/net/mod.rs @@ -0,0 +1,41 @@ +cfg_if::cfg_if! { + if #[cfg(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"))] { + mod connection { + mod sgx; + pub use sgx::*; + } + } else if #[cfg(all(target_os = "wasi", target_env = "p1"))] { + mod connection { + mod wasip1; + pub use wasip1::*; + } + } else if #[cfg(target_os = "xous")] { + mod connection { + mod xous; + pub use xous::*; + } + } else if #[cfg(target_os = "uefi")] { + mod connection { + mod uefi; + pub use uefi::*; + } + } else { + mod connection { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use connection::*; diff --git a/library/std/src/sys/pal/hermit/io.rs b/library/std/src/sys/pal/hermit/io.rs deleted file mode 100644 index 0424a1ac55a..00000000000 --- a/library/std/src/sys/pal/hermit/io.rs +++ /dev/null @@ -1,87 +0,0 @@ -use hermit_abi::{c_void, iovec}; - -use crate::marker::PhantomData; -use crate::os::hermit::io::{AsFd, AsRawFd}; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: iovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - hermit_abi::isatty(fd.as_raw_fd()) -} diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index d833c9d632c..a746b853cbd 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -23,8 +23,6 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -80,7 +78,7 @@ pub unsafe extern "C" fn runtime_entry( argv: *const *const c_char, env: *const *const c_char, ) -> ! { - extern "C" { + unsafe extern "C" { fn main(argc: isize, argv: *const *const c_char) -> i32; } diff --git a/library/std/src/sys/pal/itron/abi.rs b/library/std/src/sys/pal/itron/abi.rs index 5eb14bb7e53..49b5251fc0e 100644 --- a/library/std/src/sys/pal/itron/abi.rs +++ b/library/std/src/sys/pal/itron/abi.rs @@ -132,7 +132,7 @@ pub struct T_CTSK { pub stk: *mut u8, } -extern "C" { +unsafe extern "C" { #[link_name = "__asp3_acre_tsk"] pub fn acre_tsk(pk_ctsk: *const T_CTSK) -> ER_ID; #[link_name = "__asp3_get_tid"] diff --git a/library/std/src/sys/pal/itron/time/tests.rs b/library/std/src/sys/pal/itron/time/tests.rs index 28db4f8b679..d14035d9da4 100644 --- a/library/std/src/sys/pal/itron/time/tests.rs +++ b/library/std/src/sys/pal/itron/time/tests.rs @@ -8,24 +8,26 @@ fn reltim2dur(t: u64) -> Duration { fn test_dur2reltims() { assert_eq!(dur2reltims(reltim2dur(0)).collect::<Vec<_>>(), vec![]); assert_eq!(dur2reltims(reltim2dur(42)).collect::<Vec<_>>(), vec![42]); - assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), vec![ - abi::TMAX_RELTIM - ]); - assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), vec![ - abi::TMAX_RELTIM, - 10000 - ]); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), + vec![abi::TMAX_RELTIM, 10000] + ); } #[test] fn test_dur2tmos() { assert_eq!(dur2tmos(reltim2dur(0)).collect::<Vec<_>>(), vec![0]); assert_eq!(dur2tmos(reltim2dur(42)).collect::<Vec<_>>(), vec![42]); - assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), vec![ - abi::TMAX_RELTIM - ]); - assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), vec![ - abi::TMAX_RELTIM, - 10000 - ]); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), + vec![abi::TMAX_RELTIM, 10000] + ); } diff --git a/library/std/src/sys/pal/sgx/abi/mem.rs b/library/std/src/sys/pal/sgx/abi/mem.rs index 18e6d5b3fa2..e6ce15bed3c 100644 --- a/library/std/src/sys/pal/sgx/abi/mem.rs +++ b/library/std/src/sys/pal/sgx/abi/mem.rs @@ -12,7 +12,7 @@ pub(crate) unsafe fn rel_ptr_mut<T>(offset: u64) -> *mut T { (image_base() + offset) as *mut T } -extern "C" { +unsafe extern "C" { static ENCLAVE_SIZE: usize; static HEAP_BASE: u64; static HEAP_SIZE: usize; diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index d8836452e75..f52acc41509 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -73,7 +73,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64 EntryReturn(0, 0) } else { - extern "C" { + unsafe extern "C" { fn main(argc: isize, argv: *const *const u8) -> isize; } diff --git a/library/std/src/sys/pal/sgx/abi/panic.rs b/library/std/src/sys/pal/sgx/abi/panic.rs index c06b97ee367..67af062b0ff 100644 --- a/library/std/src/sys/pal/sgx/abi/panic.rs +++ b/library/std/src/sys/pal/sgx/abi/panic.rs @@ -2,7 +2,7 @@ use super::usercalls::alloc::UserRef; use crate::io::{self, Write}; use crate::{cmp, mem}; -extern "C" { +unsafe extern "C" { fn take_debug_panic_buf_ptr() -> *mut u8; static DEBUG: u8; } diff --git a/library/std/src/sys/pal/sgx/abi/reloc.rs b/library/std/src/sys/pal/sgx/abi/reloc.rs index 02dff0ad29f..a4f5e4a0936 100644 --- a/library/std/src/sys/pal/sgx/abi/reloc.rs +++ b/library/std/src/sys/pal/sgx/abi/reloc.rs @@ -11,7 +11,7 @@ struct Rela<T> { } pub fn relocate_elf_rela() { - extern "C" { + unsafe extern "C" { static RELA: u64; static RELACOUNT: usize; } diff --git a/library/std/src/sys/pal/sgx/abi/thread.rs b/library/std/src/sys/pal/sgx/abi/thread.rs index 2b23e368cc3..9b37e2baf36 100644 --- a/library/std/src/sys/pal/sgx/abi/thread.rs +++ b/library/std/src/sys/pal/sgx/abi/thread.rs @@ -6,7 +6,7 @@ use fortanix_sgx_abi::Tcs; /// is a one-to-one correspondence of the ID to the address of the TCS. #[unstable(feature = "sgx_platform", issue = "56975")] pub fn current() -> Tcs { - extern "C" { + unsafe extern "C" { fn get_tcs_addr() -> *mut u8; } let addr = unsafe { get_tcs_addr() }; diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs index 34fc2f20d22..7131797003b 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -22,7 +22,7 @@ macro_rules! dup { #[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE"] static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0))); -extern "C" { +unsafe extern "C" { fn get_tls_ptr() -> *const u8; fn set_tls_ptr(tls: *const u8); } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs b/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs index 943b771498f..28fbbc3c518 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs @@ -9,7 +9,7 @@ use crate::ptr::NonNull; #[repr(C)] struct UsercallReturn(u64, u64); -extern "C" { +unsafe extern "C" { fn usercall(nr: NonZero<u64>, p1: u64, p2: u64, abort: u64, p3: u64, p4: u64) -> UsercallReturn; } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index ce8a2fed4bc..9a04fa4b97e 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -14,10 +14,7 @@ pub mod env; pub mod fd; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; mod libunwind_integration; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/sgx/stdio.rs b/library/std/src/sys/pal/sgx/stdio.rs index 2e680e740fd..e79a3d971c6 100644 --- a/library/std/src/sys/pal/sgx/stdio.rs +++ b/library/std/src/sys/pal/sgx/stdio.rs @@ -62,7 +62,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { // FIXME: Rust normally maps Unix EBADF to `Uncategorized` diff --git a/library/std/src/sys/pal/solid/abi/fs.rs b/library/std/src/sys/pal/solid/abi/fs.rs index 6864a3e7745..7f2b1f83e85 100644 --- a/library/std/src/sys/pal/solid/abi/fs.rs +++ b/library/std/src/sys/pal/solid/abi/fs.rs @@ -31,7 +31,7 @@ pub const DT_WHT: c_uchar = 14; pub type S_DIR = c_int; -extern "C" { +unsafe extern "C" { pub fn SOLID_FS_Open(fd: *mut c_int, path: *const c_char, mode: c_int) -> c_int; pub fn SOLID_FS_Close(fd: c_int) -> c_int; pub fn SOLID_FS_Read(fd: c_int, buf: *mut u8, size: usize, result: *mut usize) -> c_int; diff --git a/library/std/src/sys/pal/solid/abi/mod.rs b/library/std/src/sys/pal/solid/abi/mod.rs index 4d097057217..819f93f4074 100644 --- a/library/std/src/sys/pal/solid/abi/mod.rs +++ b/library/std/src/sys/pal/solid/abi/mod.rs @@ -33,27 +33,27 @@ pub struct SOLID_RTC_TIME { pub tm_wday: c_int, } -extern "C" { +unsafe extern "C" { pub fn SOLID_RTC_ReadTime(time: *mut SOLID_RTC_TIME) -> c_int; } // `solid_log.h` -extern "C" { +unsafe extern "C" { pub fn SOLID_LOG_write(s: *const u8, l: usize); } // `solid_mem.h` -extern "C" { +unsafe extern "C" { pub fn SOLID_TLS_AddDestructor(id: i32, dtor: unsafe extern "C" fn(*mut u8)); } // `solid_rng.h` -extern "C" { +unsafe extern "C" { pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> c_int; } // `rwlock.h` -extern "C" { +unsafe extern "C" { pub fn rwl_loc_rdl(id: ID) -> ER; pub fn rwl_loc_wrl(id: ID) -> ER; pub fn rwl_ploc_rdl(id: ID) -> ER; diff --git a/library/std/src/sys/pal/solid/abi/sockets.rs b/library/std/src/sys/pal/solid/abi/sockets.rs index 3c9e3f9ffb9..80802dd42e2 100644 --- a/library/std/src/sys/pal/solid/abi/sockets.rs +++ b/library/std/src/sys/pal/solid/abi/sockets.rs @@ -158,7 +158,7 @@ pub struct fd_set { pub fds: [c_int; SOLID_NET_FD_SETSIZE], } -extern "C" { +unsafe extern "C" { #[link_name = "SOLID_NET_StrError"] pub fn strerror(errnum: c_int) -> *const c_char; diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index e092497856d..b399463c0c2 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,6 +1,7 @@ pub use self::itron::error::{ItronError as SolidError, expect_success}; -use super::{abi, itron, net}; +use super::{abi, itron}; use crate::io::ErrorKind; +use crate::sys::net; /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index d41042be518..06af7bfade0 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -23,8 +23,6 @@ pub mod env; // `crate::sys::error` pub(crate) mod error; pub mod fs; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -51,7 +49,7 @@ pub fn unsupported_err() -> crate::io::Error { #[inline] pub fn is_interrupted(code: i32) -> bool { - net::is_interrupted(code) + crate::sys::net::is_interrupted(code) } pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index 57c28aed3b2..e3b2e0aa50f 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -129,7 +129,7 @@ impl Iterator for Env { /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { - extern "C" { + unsafe extern "C" { static mut environ: *const *const c_char; } diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index a9900f55b19..f850fefc8f2 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -13,9 +13,6 @@ pub mod env; //pub mod fd; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index 15c65240ddd..c779c5f3ed8 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -16,7 +16,7 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -extern "C" { +unsafe extern "C" { pub fn TEE_Wait(timeout: u32) -> u32; } diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 7504a0f7ad7..dccc137d6f5 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -14,10 +14,12 @@ use r_efi::protocols::{device_path, device_path_to_text, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_error}; +use crate::marker::PhantomData; use crate::mem::{MaybeUninit, size_of}; use crate::os::uefi::env::boot_services; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::os::uefi::{self}; +use crate::path::Path; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{AtomicPtr, Ordering}; @@ -278,6 +280,10 @@ impl OwnedDevicePath { pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { self.0.as_ptr() } + + pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> { + BorrowedDevicePath::new(self.0) + } } impl Drop for OwnedDevicePath { @@ -293,13 +299,37 @@ impl Drop for OwnedDevicePath { impl crate::fmt::Debug for OwnedDevicePath { fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { - match device_path_to_text(self.0) { + match self.borrow().to_text() { Ok(p) => p.fmt(f), Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(), } } } +pub(crate) struct BorrowedDevicePath<'a> { + protocol: NonNull<r_efi::protocols::device_path::Protocol>, + phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, +} + +impl<'a> BorrowedDevicePath<'a> { + pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self { + Self { protocol, phantom: PhantomData } + } + + pub(crate) fn to_text(&self) -> io::Result<OsString> { + device_path_to_text(self.protocol) + } +} + +impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + match self.to_text() { + Ok(p) => p.fmt(f), + Err(_) => f.debug_struct("BorrowedDevicePath").finish_non_exhaustive(), + } + } +} + pub(crate) struct OwnedProtocol<T> { guid: r_efi::efi::Guid, handle: NonNull<crate::ffi::c_void>, @@ -452,3 +482,21 @@ pub(crate) fn open_shell() -> Option<NonNull<shell::Protocol>> { None } + +/// Get device path protocol associated with shell mapping. +/// +/// returns None in case no such mapping is exists +pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result<BorrowedDevicePath<'static>> { + let shell = + open_shell().ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?; + let mut path = os_string_to_raw(map.as_os_str()) + .ok_or(io::const_error!(io::ErrorKind::InvalidFilename, "Invalid UEFI shell mapping"))?; + + // The Device Path Protocol pointer returned by UEFI shell is owned by the shell and is not + // freed throughout it's lifetime. So it has a 'static lifetime. + let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) }; + let protocol = NonNull::new(protocol) + .ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?; + + Ok(BorrowedDevicePath::new(protocol)) +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 111bed7a7eb..4766e2ef0a9 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -17,10 +17,6 @@ pub mod args; pub mod env; pub mod fs; pub mod helpers; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 6d23c72ef22..e305b8610c9 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -17,111 +17,50 @@ pub fn errno() -> RawOsError { pub fn error_string(errno: RawOsError) -> String { // Keep the List in Alphabetical Order // The Messages are taken from UEFI Specification Appendix D - Status Codes - match r_efi::efi::Status::from_usize(errno) { - Status::ABORTED => "The operation was aborted.".to_owned(), - Status::ACCESS_DENIED => "Access was denied.".to_owned(), - Status::ALREADY_STARTED => "The protocol has already been started.".to_owned(), - Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.".to_owned(), - Status::BUFFER_TOO_SMALL => { - "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.".to_owned() - } - Status::COMPROMISED_DATA => { - "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.".to_owned() - } - Status::CONNECTION_FIN => { - "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.".to_owned() - } - Status::CONNECTION_REFUSED => { - "The receiving or transmission operation fails because this connection is refused.".to_owned() - } - Status::CONNECTION_RESET => { - "The connect fails because the connection is reset either by instance itself or the communication peer.".to_owned() - } - Status::CRC_ERROR => "A CRC error was detected.".to_owned(), - Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.".to_owned() - , - Status::END_OF_FILE => { - "The end of the file was reached.".to_owned() - } - Status::END_OF_MEDIA => { - "Beginning or end of media was reached".to_owned() - } - Status::HOST_UNREACHABLE => { - "The remote host is not reachable.".to_owned() - } - Status::HTTP_ERROR => { - "A HTTP error occurred during the network operation.".to_owned() - } - Status::ICMP_ERROR => { - "An ICMP error occurred during the network operation.".to_owned() - } - Status::INCOMPATIBLE_VERSION => { - "The function encountered an internal version that was incompatible with a version requested by the caller.".to_owned() - } - Status::INVALID_LANGUAGE => { - "The language specified was invalid.".to_owned() - } - Status::INVALID_PARAMETER => { - "A parameter was incorrect.".to_owned() - } - Status::IP_ADDRESS_CONFLICT => { - "There is an address conflict address allocation".to_owned() - } - Status::LOAD_ERROR => { - "The image failed to load.".to_owned() - } - Status::MEDIA_CHANGED => { - "The medium in the device has changed since the last access.".to_owned() - } - Status::NETWORK_UNREACHABLE => { - "The network containing the remote host is not reachable.".to_owned() - } - Status::NO_MAPPING => { - "A mapping to a device does not exist.".to_owned() - } - Status::NO_MEDIA => { - "The device does not contain any medium to perform the operation.".to_owned() - } - Status::NO_RESPONSE => { - "The server was not found or did not respond to the request.".to_owned() - } - Status::NOT_FOUND => "The item was not found.".to_owned(), - Status::NOT_READY => { - "There is no data pending upon return.".to_owned() - } - Status::NOT_STARTED => { - "The protocol has not been started.".to_owned() - } - Status::OUT_OF_RESOURCES => { - "A resource has run out.".to_owned() - } - Status::PROTOCOL_ERROR => { - "A protocol error occurred during the network operation.".to_owned() - } - Status::PROTOCOL_UNREACHABLE => { - "An ICMP protocol unreachable error is received.".to_owned() - } - Status::SECURITY_VIOLATION => { - "The function was not performed due to a security violation.".to_owned() - } - Status::TFTP_ERROR => { - "A TFTP error occurred during the network operation.".to_owned() - } - Status::TIMEOUT => "The timeout time expired.".to_owned(), - Status::UNSUPPORTED => { - "The operation is not supported.".to_owned() - } - Status::VOLUME_FULL => { - "There is no more space on the file system.".to_owned() - } - Status::VOLUME_CORRUPTED => { - "An inconstancy was detected on the file system causing the operating to fail.".to_owned() - } - Status::WRITE_PROTECTED => { - "The device cannot be written to.".to_owned() - } - _ => format!("Status: {}", errno), - } + #[rustfmt::skip] + let msg = match r_efi::efi::Status::from_usize(errno) { + Status::ABORTED => "The operation was aborted.", + Status::ACCESS_DENIED => "Access was denied.", + Status::ALREADY_STARTED => "The protocol has already been started.", + Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.", + Status::BUFFER_TOO_SMALL => "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.", + Status::COMPROMISED_DATA => "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.", + Status::CONNECTION_FIN => "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.", + Status::CONNECTION_REFUSED => "The receiving or transmission operation fails because this connection is refused.", + Status::CONNECTION_RESET => "The connect fails because the connection is reset either by instance itself or the communication peer.", + Status::CRC_ERROR => "A CRC error was detected.", + Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.", + Status::END_OF_FILE => "The end of the file was reached.", + Status::END_OF_MEDIA => "Beginning or end of media was reached", + Status::HOST_UNREACHABLE => "The remote host is not reachable.", + Status::HTTP_ERROR => "A HTTP error occurred during the network operation.", + Status::ICMP_ERROR => "An ICMP error occurred during the network operation.", + Status::INCOMPATIBLE_VERSION => "The function encountered an internal version that was incompatible with a version requested by the caller.", + Status::INVALID_LANGUAGE => "The language specified was invalid.", + Status::INVALID_PARAMETER => "A parameter was incorrect.", + Status::IP_ADDRESS_CONFLICT => "There is an address conflict address allocation", + Status::LOAD_ERROR => "The image failed to load.", + Status::MEDIA_CHANGED => "The medium in the device has changed since the last access.", + Status::NETWORK_UNREACHABLE => "The network containing the remote host is not reachable.", + Status::NO_MAPPING => "A mapping to a device does not exist.", + Status::NO_MEDIA => "The device does not contain any medium to perform the operation.", + Status::NO_RESPONSE => "The server was not found or did not respond to the request.", + Status::NOT_FOUND => "The item was not found.", + Status::NOT_READY => "There is no data pending upon return.", + Status::NOT_STARTED => "The protocol has not been started.", + Status::OUT_OF_RESOURCES => "A resource has run out.", + Status::PROTOCOL_ERROR => "A protocol error occurred during the network operation.", + Status::PROTOCOL_UNREACHABLE => "An ICMP protocol unreachable error is received.", + Status::SECURITY_VIOLATION => "The function was not performed due to a security violation.", + Status::TFTP_ERROR => "A TFTP error occurred during the network operation.", + Status::TIMEOUT => "The timeout time expired.", + Status::UNSUPPORTED => "The operation is not supported.", + Status::VOLUME_FULL => "There is no more space on the file system.", + Status::VOLUME_CORRUPTED => "An inconstancy was detected on the file system causing the operating to fail.", + Status::WRITE_PROTECTED => "The device cannot be written to.", + _ => return format!("Status: {errno}"), + }; + msg.to_owned() } pub fn getcwd() -> io::Result<PathBuf> { @@ -314,7 +253,7 @@ mod uefi_env { let mut start = 0; - // UEFI Shell returns all keys seperated by NULL. + // UEFI Shell returns all keys separated by NULL. // End of string is denoted by two NULLs for i in 0.. { if unsafe { *val.add(i) } == 0 { diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 1a0754134df..0757f1cb490 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -1,6 +1,7 @@ use r_efi::protocols::simple_text_output; use super::helpers; +use crate::collections::BTreeMap; pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::{NonZero, NonZeroI32}; @@ -21,6 +22,7 @@ pub struct Command { args: Vec<OsString>, stdout: Option<Stdio>, stderr: Option<Stdio>, + env: CommandEnv, } // passed back to std::process with the pipes connected to the child, if any @@ -40,7 +42,13 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } + Command { + prog: program.to_os_string(), + args: Vec::new(), + stdout: None, + stderr: None, + env: Default::default(), + } } pub fn arg(&mut self, arg: &OsStr) { @@ -48,7 +56,7 @@ impl Command { } pub fn env_mut(&mut self) -> &mut CommandEnv { - panic!("unsupported") + &mut self.env } pub fn cwd(&mut self, _dir: &OsStr) { @@ -76,7 +84,7 @@ impl Command { } pub fn get_envs(&self) -> CommandEnvs<'_> { - panic!("unsupported") + self.env.iter() } pub fn get_current_dir(&self) -> Option<&Path> { @@ -140,8 +148,30 @@ impl Command { cmd.stderr_inherit() }; + let env = env_changes(&self.env); + + // Set any new vars + if let Some(e) = &env { + for (k, (_, v)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stat = cmd.start_image()?; + // Rollback any env changes + if let Some(e) = env { + for (k, (v, _)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stdout = cmd.stdout()?; let stderr = cmd.stderr()?; @@ -460,7 +490,7 @@ mod uefi_command_internal { helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); let len = args.len(); - let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap(); + let args_size: u32 = (len * crate::mem::size_of::<u16>()).try_into().unwrap(); let ptr = Box::into_raw(args).as_mut_ptr(); unsafe { @@ -706,9 +736,10 @@ mod uefi_command_internal { res.push(QUOTE); res.extend(prog.encode_wide()); res.push(QUOTE); - res.push(SPACE); for arg in args { + res.push(SPACE); + // Wrap the argument in quotes to be treat as single arg res.push(QUOTE); for c in arg.encode_wide() { @@ -719,10 +750,37 @@ mod uefi_command_internal { res.push(c); } res.push(QUOTE); - - res.push(SPACE); } res.into_boxed_slice() } } + +/// Create a map of environment variable changes. Allows efficient setting and rolling back of +/// enviroment variable changes. +/// +/// Entry: (Old Value, New Value) +fn env_changes(env: &CommandEnv) -> Option<BTreeMap<EnvKey, (Option<OsString>, Option<OsString>)>> { + if env.is_unchanged() { + return None; + } + + let mut result = BTreeMap::<EnvKey, (Option<OsString>, Option<OsString>)>::new(); + + // Check if we want to clear all prior variables + if env.does_clear() { + for (k, v) in crate::env::vars_os() { + result.insert(k.into(), (Some(v), None)); + } + } + + for (k, v) in env.iter() { + let v: Option<OsString> = v.map(Into::into); + result + .entry(k.into()) + .and_modify(|cur| *cur = (cur.0.clone(), v.clone())) + .or_insert((crate::env::var_os(k), v)); + } + + Some(result) +} diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index 8438a61e90f..1c87a79803c 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -147,7 +147,7 @@ mod imp { /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows. #[cfg(all(target_os = "linux", target_env = "gnu"))] #[used] - #[link_section = ".init_array.00099"] + #[unsafe(link_section = ".init_array.00099")] static ARGV_INIT_ARRAY: extern "C" fn( crate::os::raw::c_int, *const *const u8, @@ -204,7 +204,7 @@ mod imp { } pub fn argc_argv() -> (isize, *const *const c_char) { - extern "C" { + unsafe extern "C" { // These functions are in crt_externs.h. fn _NSGetArgc() -> *mut c_int; fn _NSGetArgv() -> *mut *mut *mut c_char; diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index fdf011c1948..00cfa7a7fcf 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -9,9 +9,12 @@ use libc::c_char; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), target_os = "android", + target_os = "fuchsia", target_os = "hurd" ))] use libc::dirfd; +#[cfg(target_os = "fuchsia")] +use libc::fstatat as fstatat64; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::fstatat64; #[cfg(any( @@ -740,29 +743,27 @@ impl Iterator for ReadDir { // to `byte_offset` and thus does not require the full extent of `*entry_ptr` // to be in bounds of the same allocation, only the offset of the field // being referenced. - macro_rules! entry_field_ptr { - ($field:ident) => { - &raw const (*entry_ptr).$field - }; - } // d_name is guaranteed to be null-terminated. - let name = CStr::from_ptr(entry_field_ptr!(d_name).cast()); + let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast()); let name_bytes = name.to_bytes(); if name_bytes == b"." || name_bytes == b".." { continue; } + // When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as + // a value expression will do the right thing: `byte_offset` to the field and then + // only access those bytes. #[cfg(not(target_os = "vita"))] let entry = dirent64_min { - d_ino: *entry_field_ptr!(d_ino) as u64, + d_ino: (*entry_ptr).d_ino as u64, #[cfg(not(any( target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "nto", )))] - d_type: *entry_field_ptr!(d_type) as u8, + d_type: (*entry_ptr).d_type as u8, }; #[cfg(target_os = "vita")] @@ -850,7 +851,6 @@ impl Drop for Dir { target_os = "vita", target_os = "hurd", target_os = "espidf", - target_os = "fuchsia", target_os = "horizon", target_os = "vxworks", target_os = "rtems", @@ -882,6 +882,7 @@ impl DirEntry { any( all(target_os = "linux", not(target_env = "musl")), target_os = "android", + target_os = "fuchsia", target_os = "hurd" ), not(miri) // no dirfd on Miri @@ -910,6 +911,7 @@ impl DirEntry { not(any( all(target_os = "linux", not(target_env = "musl")), target_os = "android", + target_os = "fuchsia", target_os = "hurd", )), miri @@ -1213,6 +1215,7 @@ impl File { } #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "android", target_os = "netbsd", @@ -1225,6 +1228,7 @@ impl File { } #[cfg(not(any( target_os = "android", + target_os = "fuchsia", target_os = "freebsd", target_os = "linux", target_os = "netbsd", @@ -1240,6 +1244,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1251,6 +1256,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1261,6 +1267,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1272,6 +1279,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1282,6 +1290,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1299,6 +1308,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1309,6 +1319,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1326,6 +1337,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1336,6 +1348,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1347,6 +1360,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index 0fc765dc87a..d4551dd6a38 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -219,7 +219,7 @@ pub fn futex_wake_all(futex: &AtomicU32) { } #[cfg(target_os = "emscripten")] -extern "C" { +unsafe extern "C" { fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int; fn emscripten_futex_wait( addr: *const AtomicU32, @@ -267,7 +267,7 @@ pub mod zircon { pub const ZX_ERR_BAD_STATE: zx_status_t = -20; pub const ZX_ERR_TIMED_OUT: zx_status_t = -21; - extern "C" { + unsafe extern "C" { pub fn zx_clock_get_monotonic() -> zx_time_t; pub fn zx_futex_wait( value_ptr: *const zx_futex_t, diff --git a/library/std/src/sys/pal/unix/kernel_copy/tests.rs b/library/std/src/sys/pal/unix/kernel_copy/tests.rs index 1350d743ff6..54d8f8ed2ed 100644 --- a/library/std/src/sys/pal/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/pal/unix/kernel_copy/tests.rs @@ -2,7 +2,7 @@ use crate::fs::OpenOptions; use crate::io; use crate::io::{BufRead, Read, Result, Seek, SeekFrom, Write}; use crate::os::unix::io::AsRawFd; -use crate::sys_common::io::test::tmpdir; +use crate::test_helpers::tmpdir; #[test] fn copy_specialization() -> Result<()> { diff --git a/library/std/src/sys/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs deleted file mode 100644 index 37dd370c514..00000000000 --- a/library/std/src/sys/pal/unix/l4re.rs +++ /dev/null @@ -1,564 +0,0 @@ -macro_rules! unimpl { - () => { - return Err(io::const_error!( - io::ErrorKind::Unsupported, - "No networking available on L4Re.", - )); - }; -} - -pub mod net { - #![allow(warnings)] - use crate::fmt; - use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; - use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; - use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; - use crate::sys::fd::FileDesc; - use crate::sys_common::{AsInner, FromInner, IntoInner}; - use crate::time::Duration; - - #[allow(unused_extern_crates)] - pub extern crate libc as netc; - - pub struct Socket(FileDesc); - impl Socket { - pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result<Socket> { - unimpl!(); - } - - pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result<Socket> { - unimpl!(); - } - - pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> { - unimpl!(); - } - - pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> { - unimpl!(); - } - - pub fn accept( - &self, - _: *mut libc::sockaddr, - _: *mut libc::socklen_t, - ) -> io::Result<Socket> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result<Socket> { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn write(&self, _: &[u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, _: Option<Duration>, _: libc::c_int) -> io::Result<()> { - unimpl!(); - } - - pub fn timeout(&self, _: libc::c_int) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unimpl!(); - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } - } - - impl AsInner<FileDesc> for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } - } - - impl FromInner<FileDesc> for Socket { - fn from_inner(file_desc: FileDesc) -> Socket { - Socket(file_desc) - } - } - - impl IntoInner<FileDesc> for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } - } - - impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } - } - - impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } - } - - impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } - } - - impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } - } - - pub struct TcpStream { - inner: Socket, - } - - impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { - unimpl!(); - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, _: &[u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn peer_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result<TcpStream> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result<u32> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner<Socket> for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } - } - - impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re") - } - } - - pub struct TcpListener { - inner: Socket, - } - - impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result<TcpListener> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result<u32> { - unimpl!(); - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn only_v6(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner<Socket> for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } - } - - impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re.") - } - } - - pub struct UdpSocket { - inner: Socket, - } - - impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result<UdpSocket> { - unimpl!(); - } - - pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn broadcast(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v4(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_ttl_v4(&self) -> io::Result<u32> { - unimpl!(); - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v6(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result<u32> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn send(&self, _: &[u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner<Socket> for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } - } - - impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support on L4Re available.") - } - } - - pub struct LookupHost { - original: *mut libc::addrinfo, - cur: *mut libc::addrinfo, - } - - impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option<SocketAddr> { - None - } - } - - impl LookupHost { - pub fn port(&self) -> u16 { - 0 // unimplemented - } - } - - unsafe impl Sync for LookupHost {} - unsafe impl Send for LookupHost {} - - impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result<LookupHost> { - unimpl!(); - } - } - - impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { - unimpl!(); - } - } -} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 3cc1cae8d00..c0b56d8d2b2 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -11,17 +11,10 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -pub mod io; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod kernel_copy; -#[cfg(target_os = "l4re")] -mod l4re; #[cfg(target_os = "linux")] pub mod linux; -#[cfg(not(target_os = "l4re"))] -pub mod net; -#[cfg(target_os = "l4re")] -pub use self::l4re::net; pub mod os; pub mod pipe; pub mod process; @@ -380,24 +373,24 @@ cfg_if::cfg_if! { cfg(target_feature = "crt-static"))] #[link(name = "dl", cfg(not(target_feature = "crt-static")))] #[link(name = "log", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "freebsd")] { #[link(name = "execinfo")] #[link(name = "pthread")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "netbsd")] { #[link(name = "pthread")] #[link(name = "rt")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] { #[link(name = "pthread")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "solaris")] { #[link(name = "socket")] #[link(name = "posix4")] #[link(name = "pthread")] #[link(name = "resolv")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "illumos")] { #[link(name = "socket")] #[link(name = "posix4")] @@ -406,24 +399,24 @@ cfg_if::cfg_if! { #[link(name = "nsl")] // Use libumem for the (malloc-compatible) allocator #[link(name = "umem")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(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")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "fuchsia")] { #[link(name = "zircon")] #[link(name = "fdio")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(all(target_os = "linux", target_env = "uclibc"))] { #[link(name = "dl")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "vita")] { #[link(name = "pthread", kind = "static", modifiers = "-bundle")] - extern "C" {} + unsafe extern "C" {} } } diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index b83772e34c1..04199c56330 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -30,7 +30,7 @@ cfg_if::cfg_if! { } } -extern "C" { +unsafe extern "C" { #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] #[cfg_attr( any( @@ -82,7 +82,7 @@ pub fn errno() -> i32 { #[cfg(target_os = "rtems")] pub fn errno() -> i32 { - extern "C" { + unsafe extern "C" { #[thread_local] static _tls_errno: c_int; } @@ -92,7 +92,7 @@ pub fn errno() -> i32 { #[cfg(target_os = "dragonfly")] pub fn errno() -> i32 { - extern "C" { + unsafe extern "C" { #[thread_local] static errno: c_int; } @@ -103,7 +103,7 @@ pub fn errno() -> i32 { #[cfg(target_os = "dragonfly")] #[allow(dead_code)] pub fn set_errno(e: i32) { - extern "C" { + unsafe extern "C" { #[thread_local] static mut errno: c_int; } @@ -115,7 +115,7 @@ pub fn set_errno(e: i32) { /// Gets a detailed string description for the given error number. pub fn error_string(errno: i32) -> String { - extern "C" { + unsafe extern "C" { #[cfg_attr( all( any(target_os = "linux", target_os = "hurd", target_env = "newlib"), @@ -610,7 +610,7 @@ pub unsafe fn environ() -> *mut *const *const c_char { // Use the `environ` static which is part of POSIX. #[cfg(not(target_vendor = "apple"))] pub unsafe fn environ() -> *mut *const *const c_char { - extern "C" { + unsafe extern "C" { static mut environ: *const *const c_char; } &raw mut environ @@ -847,7 +847,7 @@ pub fn getppid() -> u32 { #[cfg(all(target_os = "linux", target_env = "gnu"))] pub fn glibc_version() -> Option<(usize, usize)> { - extern "C" { + unsafe extern "C" { fn gnu_get_libc_version() -> *const libc::c_char; } let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) }; diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index ec4965c1d71..2bff192a5bd 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -19,8 +19,7 @@ use crate::sys::process::process_common::*; use crate::{fmt, mem, sys}; cfg_if::cfg_if! { - // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 - if #[cfg(any(target_env = "nto70", target_env = "nto71"))] { + if #[cfg(target_os = "nto")] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; @@ -187,12 +186,7 @@ impl Command { // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(any( - target_os = "watchos", - target_os = "tvos", - target_env = "nto70", - target_env = "nto71" - )))] + #[cfg(not(any(target_os = "watchos", target_os = "tvos", target_os = "nto")))] unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { cvt(libc::fork()) } @@ -201,8 +195,7 @@ impl Command { // or closed a file descriptor while the fork() was occurring". // Documentation says "... or try calling fork() again". This is what we do here. // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html - // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 - #[cfg(any(target_env = "nto70", target_env = "nto71"))] + #[cfg(target_os = "nto")] unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { use crate::sys::os::errno; diff --git a/library/std/src/sys/pal/unix/process/zircon.rs b/library/std/src/sys/pal/unix/process/zircon.rs index 4035e2370a3..7932bd26d76 100644 --- a/library/std/src/sys/pal/unix/process/zircon.rs +++ b/library/std/src/sys/pal/unix/process/zircon.rs @@ -75,7 +75,7 @@ pub struct zx_info_process_t { pub reserved1: u32, } -extern "C" { +unsafe extern "C" { pub fn zx_job_default() -> zx_handle_t; pub fn zx_task_kill(handle: zx_handle_t) -> zx_status_t; @@ -115,7 +115,7 @@ pub struct fdio_spawn_action_t { pub reserved1: u64, } -extern "C" { +unsafe extern "C" { pub fn fdio_spawn_etc( job: zx_handle_t, flags: u32, diff --git a/library/std/src/sys/pal/unix/stdio.rs b/library/std/src/sys/pal/unix/stdio.rs index 97e75f1b5b6..8c2f61a40de 100644 --- a/library/std/src/sys/pal/unix/stdio.rs +++ b/library/std/src/sys/pal/unix/stdio.rs @@ -92,7 +92,7 @@ pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(libc::EBADF as i32) } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option<impl io::Write> { Some(Stderr::new()) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index f657f82e6e3..479021af040 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -23,7 +23,7 @@ mod zircon { type zx_status_t = i32; pub const ZX_PROP_NAME: u32 = 3; - extern "C" { + unsafe extern "C" { pub fn zx_object_set_property( handle: zx_handle_t, property: u32, @@ -130,7 +130,12 @@ impl Thread { } } - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"))] + #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "nuttx" + ))] pub fn set_name(name: &CStr) { unsafe { cfg_if::cfg_if! { @@ -139,7 +144,7 @@ impl Thread { const TASK_COMM_LEN: usize = 16; let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); } else { - // FreeBSD and DragonFly BSD do not enforce length limits. + // FreeBSD, DragonFly, FreeBSD and NuttX do not enforce length limits. } }; // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, @@ -150,7 +155,7 @@ impl Thread { } } - #[cfg(any(target_os = "openbsd", target_os = "nuttx"))] + #[cfg(target_os = "openbsd")] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); @@ -225,7 +230,7 @@ impl Thread { #[cfg(target_os = "vxworks")] pub fn set_name(name: &CStr) { // FIXME(libc): adding real STATUS, ERROR type eventually. - extern "C" { + unsafe extern "C" { fn taskNameSet(task_id: libc::TASK_ID, task_name: *mut libc::c_char) -> libc::c_int; } @@ -501,7 +506,7 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { } else if #[cfg(target_os = "vxworks")] { // Note: there is also `vxCpuConfiguredGet`, closer to _SC_NPROCESSORS_CONF // expectations than the actual cores availability. - extern "C" { + unsafe extern "C" { fn vxCpuEnabledGet() -> libc::cpuset_t; } diff --git a/library/std/src/sys/pal/unix/thread_parking.rs b/library/std/src/sys/pal/unix/thread_parking.rs index 72dd2031479..bef8b4fb363 100644 --- a/library/std/src/sys/pal/unix/thread_parking.rs +++ b/library/std/src/sys/pal/unix/thread_parking.rs @@ -8,7 +8,7 @@ use crate::ffi::{c_int, c_void}; use crate::ptr; use crate::time::Duration; -extern "C" { +unsafe extern "C" { fn ___lwp_park60( clock_id: clockid_t, flags: c_int, diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index 35762f5a53b..5a37598f438 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -31,7 +31,7 @@ use crate::{mem, ptr}; pub(crate) macro weak { (fn $name:ident($($t:ty),*) -> $ret:ty) => ( let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = { - extern "C" { + unsafe extern "C" { #[linkage = "extern_weak"] static $name: Option<unsafe extern "C" fn($($t),*) -> $ret>; } diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 01d516f7568..b1aaeb1b4c8 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -3,8 +3,6 @@ pub mod args; pub mod env; pub mod fs; -pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index 7779d2b97d7..7705e7f6b88 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -787,7 +787,7 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> { } } - extern "C" { + unsafe extern "C" { pub fn __wasilibc_find_relpath( path: *const libc::c_char, abs_prefix: *mut *const libc::c_char, diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 5d54c790306..f4588a60ea9 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -1,8 +1,7 @@ //! System bindings for the wasm/web platform //! //! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for wasm. Note that this wasm is *not* the emscripten -//! wasm, so we have no runtime here. +//! OS level functionality for wasm. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -21,9 +20,7 @@ pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -46,5 +43,4 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; +pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index f7701360f5a..ba2b65a1f40 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -16,7 +16,7 @@ use crate::{fmt, io, str, vec}; mod libc { pub use libc::*; - extern "C" { + unsafe extern "C" { pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; pub fn chdir(dir: *const c_char) -> c_int; pub fn __wasilibc_get_environ() -> *mut *mut c_char; @@ -46,7 +46,7 @@ cfg_if::cfg_if! { } pub fn errno() -> i32 { - extern "C" { + unsafe extern "C" { #[thread_local] static errno: libc::c_int; } diff --git a/library/std/src/sys/pal/wasi/stdio.rs b/library/std/src/sys/pal/wasi/stdio.rs index ca49f871e19..d08b772e5fc 100644 --- a/library/std/src/sys/pal/wasi/stdio.rs +++ b/library/std/src/sys/pal/wasi/stdio.rs @@ -101,7 +101,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into()) diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index f5e19f26bfe..0ae02369410 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -35,7 +35,7 @@ cfg_if::cfg_if! { pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; - extern "C" { + unsafe extern "C" { pub fn pthread_create( native: *mut pthread_t, attr: *const pthread_attr_t, diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 320712fdcc9..72c9742b2e5 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -17,10 +17,7 @@ pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; -#[path = "../wasi/io.rs"] -pub mod io; -pub mod net; #[path = "../wasi/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 8141bfac49a..32d59c4d0f7 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -2,7 +2,7 @@ //! //! This module contains the facade (aka platform-specific) implementations of //! OS level functionality for wasm. Note that this wasm is *not* the emscripten -//! wasm, so we have no runtime here. +//! or wasi wasm, so we have no runtime here. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -21,10 +21,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; #[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/windows/args/tests.rs b/library/std/src/sys/pal/windows/args/tests.rs index 6d5c953cbd5..484a90ab056 100644 --- a/library/std/src/sys/pal/windows/args/tests.rs +++ b/library/std/src/sys/pal/windows/args/tests.rs @@ -47,10 +47,10 @@ fn whitespace_behavior() { fn genius_quotes() { chk(r#"EXE "" """#, &["EXE", "", ""]); chk(r#"EXE "" """"#, &["EXE", "", r#"""#]); - chk(r#"EXE "this is """all""" in the same argument""#, &[ - "EXE", - r#"this is "all" in the same argument"#, - ]); + chk( + r#"EXE "this is """all""" in the same argument""#, + &["EXE", r#"this is "all" in the same argument"#], + ); chk(r#"EXE "a"""#, &["EXE", r#"a""#]); chk(r#"EXE "a"" a"#, &["EXE", r#"a" a"#]); // quotes cannot be escaped in command names diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 9ce3e912caf..8dc61edb603 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -115,7 +115,7 @@ if #[cfg(not(target_vendor = "uwp"))] { link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated") )] #[cfg_attr(not(target_arch = "x86"), link(name = "bcryptprimitives", kind = "raw-dylib"))] -extern "system" { +unsafe extern "system" { pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; } @@ -164,7 +164,7 @@ compat_fn_with_fallback! { not(target_arch = "x86"), link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib") )] -extern "system" { +unsafe extern "system" { pub fn WaitOnAddress( address: *const c_void, compareaddress: *const c_void, diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index 42999da1664..2b9838437e9 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -39,7 +39,7 @@ use crate::sys::c; // See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170 #[cfg(target_vendor = "win7")] #[used] -#[link_section = ".CRT$XCT"] +#[unsafe(link_section = ".CRT$XCT")] static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init; /// Preload some imported functions. diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index f8493c21ad4..b786c79004f 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -44,7 +44,7 @@ pub struct FileType { } pub struct ReadDir { - handle: FindNextFileHandle, + handle: Option<FindNextFileHandle>, root: Arc<PathBuf>, first: Option<c::WIN32_FIND_DATAW>, } @@ -113,13 +113,13 @@ impl fmt::Debug for ReadDir { impl Iterator for ReadDir { type Item = io::Result<DirEntry>; fn next(&mut self) -> Option<io::Result<DirEntry>> { - if self.handle.0 == c::INVALID_HANDLE_VALUE { + let Some(handle) = self.handle.as_ref() else { // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle. // Simply return `None` because this is only the case when `FindFirstFileExW` in // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means // no matchhing files can be found. return None; - } + }; if let Some(first) = self.first.take() { if let Some(e) = DirEntry::new(&self.root, &first) { return Some(Ok(e)); @@ -128,7 +128,7 @@ impl Iterator for ReadDir { unsafe { let mut wfd = mem::zeroed(); loop { - if c::FindNextFileW(self.handle.0, &mut wfd) == 0 { + if c::FindNextFileW(handle.0, &mut wfd) == 0 { match api::get_last_error() { WinError::NO_MORE_FILES => return None, WinError { code } => { @@ -296,6 +296,10 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { let path = maybe_verbatim(path)?; + Self::open_native(&path, opts) + } + + fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result<File> { let creation = opts.get_creation_mode()?; let handle = unsafe { c::CreateFileW( @@ -1190,7 +1194,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> { if find_handle != c::INVALID_HANDLE_VALUE { Ok(ReadDir { - handle: FindNextFileHandle(find_handle), + handle: Some(FindNextFileHandle(find_handle)), root: Arc::new(root), first: Some(wfd), }) @@ -1208,11 +1212,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> { // See issue #120040: https://github.com/rust-lang/rust/issues/120040. let last_error = api::get_last_error(); if last_error == WinError::FILE_NOT_FOUND { - return Ok(ReadDir { - handle: FindNextFileHandle(find_handle), - root: Arc::new(root), - first: None, - }); + return Ok(ReadDir { handle: None, root: Arc::new(root), first: None }); } // Just return the error constructed from the raw OS error if the above is not the case. @@ -1226,8 +1226,26 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> { pub fn unlink(p: &Path) -> io::Result<()> { let p_u16s = maybe_verbatim(p)?; - cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?; - Ok(()) + if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 { + let err = api::get_last_error(); + // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove + // the file while ignoring the readonly attribute. + // This is accomplished by calling the `posix_delete` function on an open file handle. + if err == WinError::ACCESS_DENIED { + let mut opts = OpenOptions::new(); + opts.access_mode(c::DELETE); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT); + if let Ok(f) = File::open_native(&p_u16s, &opts) { + if f.posix_delete().is_ok() { + return Ok(()); + } + } + } + // return the original error if any of the above fails. + Err(io::Error::from_raw_os_error(err.code as i32)) + } else { + Ok(()) + } } pub fn rename(old: &Path, new: &Path) -> io::Result<()> { diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 4282dbb5493..1eca346b76c 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -21,8 +21,6 @@ pub mod fs; #[cfg(not(target_vendor = "win7"))] pub mod futex; pub mod handle; -pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; @@ -63,7 +61,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { - net::cleanup(); + crate::sys::net::cleanup(); } #[inline] diff --git a/library/std/src/sys/pal/windows/process/tests.rs b/library/std/src/sys/pal/windows/process/tests.rs index 1bcc5fa6b20..90f1157d7c4 100644 --- a/library/std/src/sys/pal/windows/process/tests.rs +++ b/library/std/src/sys/pal/windows/process/tests.rs @@ -33,7 +33,7 @@ fn test_thread_handle() { assert!(p.is_ok()); let mut p = p.unwrap(); - extern "system" { + unsafe extern "system" { fn ResumeThread(_: BorrowedHandle<'_>) -> u32; } unsafe { @@ -158,7 +158,7 @@ fn windows_exe_resolver() { use super::resolve_exe; use crate::io; use crate::sys::fs::symlink; - use crate::sys_common::io::test::tmpdir; + use crate::test_helpers::tmpdir; let env_paths = || env::var_os("PATH"); diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index a64cd068560..1bd0e67f371 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -5,9 +5,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index b0ab01a6383..307623c0425 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -37,7 +37,7 @@ mod eh_unwinding { #[cfg(not(test))] mod c_compat { use crate::os::xous::ffi::exit; - extern "C" { + unsafe extern "C" { fn main() -> u32; } diff --git a/library/std/src/sys/pal/zkvm/abi.rs b/library/std/src/sys/pal/zkvm/abi.rs index 53332d90e02..d000574f684 100644 --- a/library/std/src/sys/pal/zkvm/abi.rs +++ b/library/std/src/sys/pal/zkvm/abi.rs @@ -18,7 +18,7 @@ pub mod fileno { pub const JOURNAL: u32 = 3; } -extern "C" { +unsafe extern "C" { // Wrappers around syscalls provided by risc0-zkvm-platform: pub fn sys_halt(); pub fn sys_output(output_id: u32, output_value: u32); diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 6ea05772029..054c867f90d 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -16,10 +16,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/zkvm/stdio.rs b/library/std/src/sys/pal/zkvm/stdio.rs index dd218c8894c..5f1d06dd1d7 100644 --- a/library/std/src/sys/pal/zkvm/stdio.rs +++ b/library/std/src/sys/pal/zkvm/stdio.rs @@ -54,7 +54,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(_err: &io::Error) -> bool { true diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs index 24a94ec7828..1fa4e80d678 100644 --- a/library/std/src/sys/path/mod.rs +++ b/library/std/src/sys/path/mod.rs @@ -5,12 +5,12 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; pub use sgx::*; - } else if #[cfg(any( - target_os = "uefi", - target_os = "solid_asp3", - ))] { + } else if #[cfg(target_os = "solid_asp3")] { mod unsupported_backslash; pub use unsupported_backslash::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; } else { mod unix; pub use unix::*; diff --git a/library/std/src/sys/path/uefi.rs b/library/std/src/sys/path/uefi.rs new file mode 100644 index 00000000000..a3f4a3bfe1b --- /dev/null +++ b/library/std/src/sys/path/uefi.rs @@ -0,0 +1,105 @@ +#![forbid(unsafe_op_in_unsafe_fn)] +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; +use crate::sys::{helpers, unsupported_err}; + +const FORWARD_SLASH: u8 = b'/'; +const COLON: u8 = b':'; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> { + None +} + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +/// UEFI paths can be of 4 types: +/// +/// 1. Absolute Shell Path: Uses shell mappings (eg: `FS0:`). Does not exist if UEFI shell not present. +/// It can be identified with `:`. +/// Eg: FS0:\abc\run.efi +/// +/// 2. Absolute Device Path: this is what we want +/// It can be identified with `/`. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +/// +/// 3: Relative root: path relative to the current volume. +/// It will start with `\`. +/// Eg: \abc\run.efi +/// +/// 4: Relative +/// Eg: run.efi +/// +/// The algorithm is mostly taken from edk2 UEFI shell implementation and is +/// somewhat simple. Check for the path type in order. +/// +/// The volume mapping in Absolute Shell Path (not the rest of the path) can be converted to Device +/// Path Protocol using `EFI_SHELL->GetDevicePathFromMap`. The rest of the path (Relative root +/// path), can just be appended to the remaining path. +/// +/// For Relative root, we get the current volume (either in Shell Mapping, or Device Path Protocol +/// form) and join it with the relative root path. We then recurse the function to resolve the Shell +/// Mapping if present. +/// +/// For Relative paths, we use the current working directory to construct +/// the new path and recurse the function to resolve the Shell mapping if present. +/// +/// Finally, at the end, we get the 2nd form, i.e. Absolute Device Path, which can be used in the +/// normal UEFI APIs such as file, process, etc. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> { + // Absolute Shell Path + if path.as_os_str().as_encoded_bytes().contains(&COLON) { + let mut path_components = path.components(); + // Since path is not empty, it has at least one Component + let prefix = path_components.next().unwrap(); + + let dev_path = helpers::get_device_path_from_map(prefix.as_ref())?; + let mut dev_path_text = dev_path.to_text().map_err(|_| unsupported_err())?; + + // UEFI Shell does not seem to end device path with `/` + if *dev_path_text.as_encoded_bytes().last().unwrap() != FORWARD_SLASH { + dev_path_text.push("/"); + } + + let mut ans = PathBuf::from(dev_path_text); + ans.push(path_components); + + return Ok(ans); + } + + // Absolute Device Path + if path.as_os_str().as_encoded_bytes().contains(&FORWARD_SLASH) { + return Ok(path.to_path_buf()); + } + + // cur_dir() always returns something + let cur_dir = crate::env::current_dir().unwrap(); + let mut path_components = path.components(); + + // Relative Root + if path_components.next().unwrap() == crate::path::Component::RootDir { + let mut ans = PathBuf::new(); + ans.push(cur_dir.components().next().unwrap()); + ans.push(path_components); + return absolute(&ans); + } + + absolute(&cur_dir.join(path)) +} + +pub(crate) fn is_absolute(path: &Path) -> bool { + let temp = path.as_os_str().as_encoded_bytes(); + temp.contains(&COLON) || temp.contains(&FORWARD_SLASH) +} diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 88a25caeff0..cd2c7899f4b 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -194,7 +194,7 @@ cfg_if::cfg_if! { } } // defined in libgcc - extern "C" { + unsafe extern "C" { fn __gnu_unwind_frame( exception_object: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context, diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs index 32467e9ebaa..e1957bceb90 100644 --- a/library/std/src/sys/random/arc4random.rs +++ b/library/std/src/sys/random/arc4random.rs @@ -25,7 +25,7 @@ use libc::arc4random_buf; target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74 ))] #[cfg_attr(target_os = "haiku", link(name = "bsd"))] -extern "C" { +unsafe extern "C" { fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t); } diff --git a/library/std/src/sys/random/espidf.rs b/library/std/src/sys/random/espidf.rs index fd52cb5559c..6f48f7f1f29 100644 --- a/library/std/src/sys/random/espidf.rs +++ b/library/std/src/sys/random/espidf.rs @@ -1,6 +1,6 @@ use crate::ffi::c_void; -extern "C" { +unsafe extern "C" { fn esp_fill_random(buf: *mut c_void, len: usize); } diff --git a/library/std/src/sys/random/fuchsia.rs b/library/std/src/sys/random/fuchsia.rs index 77d72b3c5b7..269e0d9aeeb 100644 --- a/library/std/src/sys/random/fuchsia.rs +++ b/library/std/src/sys/random/fuchsia.rs @@ -4,7 +4,7 @@ //! <https://fuchsia.dev/reference/syscalls/cprng_draw>. #[link(name = "zircon")] -extern "C" { +unsafe extern "C" { fn zx_cprng_draw(buffer: *mut u8, len: usize); } diff --git a/library/std/src/sys/random/teeos.rs b/library/std/src/sys/random/teeos.rs index fd6b24e19e9..6ca59cc12c9 100644 --- a/library/std/src/sys/random/teeos.rs +++ b/library/std/src/sys/random/teeos.rs @@ -1,4 +1,4 @@ -extern "C" { +unsafe extern "C" { fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); } diff --git a/library/std/src/sys/sync/thread_parking/darwin.rs b/library/std/src/sys/sync/thread_parking/darwin.rs index 0553c5e19a9..a0d24a91e7c 100644 --- a/library/std/src/sys/sync/thread_parking/darwin.rs +++ b/library/std/src/sys/sync/thread_parking/darwin.rs @@ -24,7 +24,7 @@ const DISPATCH_TIME_NOW: dispatch_time_t = 0; const DISPATCH_TIME_FOREVER: dispatch_time_t = !0; // Contained in libSystem.dylib, which is linked by default. -extern "C" { +unsafe extern "C" { fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t; fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t; fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) -> isize; diff --git a/library/std/src/sys/thread_local/destructors/linux_like.rs b/library/std/src/sys/thread_local/destructors/linux_like.rs index f473dc4d79d..817941229ee 100644 --- a/library/std/src/sys/thread_local/destructors/linux_like.rs +++ b/library/std/src/sys/thread_local/destructors/linux_like.rs @@ -27,7 +27,7 @@ pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { #[allow(non_camel_case_types)] pub struct c_int(#[allow(dead_code)] pub core::ffi::c_int); - extern "C" { + unsafe extern "C" { #[linkage = "extern_weak"] static __dso_handle: *mut u8; #[linkage = "extern_weak"] diff --git a/library/std/src/sys/thread_local/guard/apple.rs b/library/std/src/sys/thread_local/guard/apple.rs index fa25b116622..edcedf21e9e 100644 --- a/library/std/src/sys/thread_local/guard/apple.rs +++ b/library/std/src/sys/thread_local/guard/apple.rs @@ -10,7 +10,7 @@ pub fn enable() { #[thread_local] static REGISTERED: Cell<bool> = Cell::new(false); - extern "C" { + unsafe extern "C" { fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); } diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index 1752b0e1208..b15a0d7c0bd 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -74,7 +74,7 @@ pub fn enable() { unsafe { ptr::from_ref(&CALLBACK).read_volatile() }; } -#[link_section = ".CRT$XLB"] +#[unsafe(link_section = ".CRT$XLB")] #[cfg_attr(miri, used)] // Miri only considers explicitly `#[used]` statics for `lookup_link_section` pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) = tls_callback; diff --git a/library/std/src/sys/thread_local/key/unix.rs b/library/std/src/sys/thread_local/key/unix.rs index b4b58b34706..93bd0d1f668 100644 --- a/library/std/src/sys/thread_local/key/unix.rs +++ b/library/std/src/sys/thread_local/key/unix.rs @@ -8,7 +8,7 @@ mod libc { #[allow(non_camel_case_types)] pub type pthread_key_t = ffi::c_uint; - extern "C" { + unsafe extern "C" { pub fn pthread_key_create( key: *mut pthread_key_t, destructor: unsafe extern "C" fn(*mut ffi::c_void), diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs index 2ab4bba7d8e..6c5e6447d19 100644 --- a/library/std/src/sys/thread_local/key/xous.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -59,7 +59,7 @@ static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1); static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut()); #[cfg(test)] -extern "Rust" { +unsafe extern "Rust" { #[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"] static TLS_KEY_INDEX: AtomicUsize; diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs deleted file mode 100644 index 6f6f282d432..00000000000 --- a/library/std/src/sys_common/io.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Bare metal platforms usually have very small amounts of RAM -// (in the order of hundreds of KB) -pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; - -#[cfg(test)] -#[allow(dead_code)] // not used on emscripten and wasi -pub mod test { - use rand::RngCore; - - use crate::path::{Path, PathBuf}; - use crate::{env, fs, thread}; - - pub struct TempDir(PathBuf); - - impl TempDir { - pub fn join(&self, path: &str) -> PathBuf { - let TempDir(ref p) = *self; - p.join(path) - } - - pub fn path(&self) -> &Path { - let TempDir(ref p) = *self; - p - } - } - - impl Drop for TempDir { - fn drop(&mut self) { - // Gee, seeing how we're testing the fs module I sure hope that we - // at least implement this correctly! - let TempDir(ref p) = *self; - let result = fs::remove_dir_all(p); - // Avoid panicking while panicking as this causes the process to - // immediately abort, without displaying test results. - if !thread::panicking() { - result.unwrap(); - } - } - } - - #[track_caller] // for `test_rng` - pub fn tmpdir() -> TempDir { - let p = env::temp_dir(); - let mut r = crate::test_helpers::test_rng(); - let ret = p.join(&format!("rust-{}", r.next_u32())); - fs::create_dir(&ret).unwrap(); - TempDir(ret) - } -} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 4f7a131f6bb..4dc67d26bd8 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -21,25 +21,10 @@ mod tests; pub mod fs; -pub mod io; pub mod process; pub mod wstr; pub mod wtf8; -cfg_if::cfg_if! { - if #[cfg(any( - all(unix, not(target_os = "l4re")), - windows, - target_os = "hermit", - target_os = "solid_asp3", - all(target_os = "wasi", target_env = "p2") - ))] { - pub mod net; - } else { - pub use crate::sys::net; - } -} - // common error constructors /// A trait for viewing representations from std types diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index bc06eaa2b8f..b57c99a8452 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -356,32 +356,32 @@ 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(&[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(&[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 }); } @@ -396,36 +396,36 @@ fn wtf8buf_extend() { 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(&[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 - }); + 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] @@ -556,9 +556,10 @@ 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 - ]); + assert_eq!( + string.encode_wide().collect::<Vec<_>>(), + vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9] + ); } #[test] diff --git a/library/std/src/test_helpers.rs b/library/std/src/test_helpers.rs new file mode 100644 index 00000000000..7c20f38c863 --- /dev/null +++ b/library/std/src/test_helpers.rs @@ -0,0 +1,65 @@ +use rand::{RngCore, SeedableRng}; + +use crate::hash::{BuildHasher, Hash, Hasher, RandomState}; +use crate::panic::Location; +use crate::path::{Path, PathBuf}; +use crate::{env, fs, thread}; + +/// Test-only replacement for `rand::thread_rng()`, which is unusable for +/// us, as we want to allow running stdlib tests on tier-3 targets which may +/// not have `getrandom` support. +/// +/// Does a bit of a song and dance to ensure that the seed is different on +/// each call (as some tests sadly rely on this), but doesn't try that hard. +/// +/// This is duplicated in the `core`, `alloc` test suites (as well as +/// `std`'s integration tests), but figuring out a mechanism to share these +/// seems far more painful than copy-pasting a 7 line function a couple +/// times, given that even under a perma-unstable feature, I don't think we +/// want to expose types from `rand` from `std`. +#[track_caller] +pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { + let mut hasher = RandomState::new().build_hasher(); + Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + SeedableRng::from_seed(seed) +} + +pub struct TempDir(PathBuf); + +impl TempDir { + pub fn join(&self, path: &str) -> PathBuf { + let TempDir(ref p) = *self; + p.join(path) + } + + pub fn path(&self) -> &Path { + let TempDir(ref p) = *self; + p + } +} + +impl Drop for TempDir { + fn drop(&mut self) { + // Gee, seeing how we're testing the fs module I sure hope that we + // at least implement this correctly! + let TempDir(ref p) = *self; + let result = fs::remove_dir_all(p); + // Avoid panicking while panicking as this causes the process to + // immediately abort, without displaying test results. + if !thread::panicking() { + result.unwrap(); + } + } +} + +#[track_caller] // for `test_rng` +pub fn tmpdir() -> TempDir { + let p = env::temp_dir(); + let mut r = test_rng(); + let ret = p.join(&format!("rust-{}", r.next_u32())); + fs::create_dir(&ret).unwrap(); + TempDir(ret) +} diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index c003503ca8b..ca04aa4ada4 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -2,12 +2,6 @@ #![unstable(feature = "thread_local_internals", issue = "none")] -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - -#[cfg(test)] -mod dynamic_tests; - use crate::cell::{Cell, RefCell}; use crate::error::Error; use crate::fmt; diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 9f4f8a0d088..88b3e9e0ceb 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -31,9 +31,6 @@ #![stable(feature = "time", since = "1.3.0")] -#[cfg(test)] -mod tests; - #[stable(feature = "time", since = "1.3.0")] pub use core::time::Duration; #[stable(feature = "duration_checked_float", since = "1.66.0")] diff --git a/library/std/tests/common/mod.rs b/library/std/tests/common/mod.rs index 7cf70c725e4..1e8e4cced6c 100644 --- a/library/std/tests/common/mod.rs +++ b/library/std/tests/common/mod.rs @@ -18,7 +18,7 @@ pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { rand::SeedableRng::from_seed(seed) } -// Copied from std::sys_common::io +// Copied from std::test_helpers pub(crate) struct TempDir(PathBuf); impl TempDir { diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index 44fe84c989f..e754cf8263b 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -1,163 +1,123 @@ use std::env::*; -use std::ffi::{OsStr, OsString}; - -use rand::distributions::{Alphanumeric, DistString}; +use std::path::Path; mod common; -use std::thread; - -use common::test_rng; - -#[track_caller] -fn make_rand_name() -> OsString { - let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10)); - let n = OsString::from(n); - assert!(var_os(&n).is_none()); - n -} - -fn eq(a: Option<OsString>, b: Option<&str>) { - assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); -} #[test] -fn test_set_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - eq(var_os(&n), Some("VALUE")); +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)] +fn test_self_exe_path() { + let path = current_exe(); + assert!(path.is_ok()); + let path = path.unwrap(); + + // Hard to test this function + assert!(path.is_absolute()); } #[test] -fn test_remove_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - remove_var(&n); - eq(var_os(&n), None); -} +fn test() { + assert!((!Path::new("test-path").is_absolute())); -#[test] -fn test_set_var_overwrite() { - let n = make_rand_name(); - set_var(&n, "1"); - set_var(&n, "2"); - eq(var_os(&n), Some("2")); - set_var(&n, ""); - eq(var_os(&n), Some("")); + #[cfg(not(target_env = "sgx"))] + current_dir().unwrap(); } #[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_var_big() { - let mut s = "".to_string(); - let mut i = 0; - while i < 100 { - s.push_str("aaaaaaaaaa"); - i += 1; +#[cfg(windows)] +fn split_paths_windows() { + use std::path::PathBuf; + + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::<Vec<_>>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>() } - let n = make_rand_name(); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); -} -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_env_set_get_huge() { - let n = make_rand_name(); - let s = "x".repeat(10000); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); - remove_var(&n); - eq(var_os(&n), None); + assert!(check_parse("", &mut [""])); + assert!(check_parse(r#""""#, &mut [""])); + assert!(check_parse(";;", &mut ["", "", ""])); + assert!(check_parse(r"c:\", &mut [r"c:\"])); + assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); + assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); + assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); + assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"])); } #[test] -fn test_env_set_var() { - let n = make_rand_name(); +#[cfg(unix)] +fn split_paths_unix() { + use std::path::PathBuf; - let mut e = vars_os(); - set_var(&n, "VALUE"); - assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::<Vec<_>>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>() + } - assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + assert!(check_parse("", &mut [""])); + assert!(check_parse("::", &mut ["", "", ""])); + assert!(check_parse("/", &mut ["/"])); + assert!(check_parse("/:", &mut ["/", ""])); + assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); } #[test] -#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))] -#[allow(deprecated)] -fn env_home_dir() { - use std::path::PathBuf; +#[cfg(unix)] +fn join_paths_unix() { + use std::ffi::OsStr; - fn var_to_os_string(var: Result<String, VarError>) -> Option<OsString> { - match var { - Ok(var) => Some(OsString::from(var)), - Err(VarError::NotUnicode(var)) => Some(var), - _ => None, - } + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) } - cfg_if::cfg_if! { - if #[cfg(unix)] { - let oldhome = var_to_os_string(var("HOME")); - - set_var("HOME", "/home/MountainView"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); - - remove_var("HOME"); - if cfg!(target_os = "android") { - assert!(home_dir().is_none()); - } else { - // When HOME is not set, some platforms return `None`, - // but others return `Some` with a default. - // Just check that it is not "/home/MountainView". - assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView"))); - } - - if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } - } else if #[cfg(windows)] { - let oldhome = var_to_os_string(var("HOME")); - let olduserprofile = var_to_os_string(var("USERPROFILE")); - - remove_var("HOME"); - remove_var("USERPROFILE"); - - assert!(home_dir().is_some()); - - set_var("HOME", "/home/PaloAlto"); - assert_ne!(home_dir(), Some(PathBuf::from("/home/PaloAlto")), "HOME must not be used"); + assert!(test_eq(&[], "")); + assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); + assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); + assert!(join_paths(["/te:st"].iter().cloned()).is_err()); +} - set_var("USERPROFILE", "/home/MountainView"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); +#[test] +#[cfg(windows)] +fn join_paths_windows() { + use std::ffi::OsStr; - remove_var("HOME"); + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) + } - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + assert!(test_eq(&[], "")); + assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); + assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); + assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); + assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); +} - set_var("USERPROFILE", ""); - assert_ne!(home_dir(), Some(PathBuf::from("")), "Empty USERPROFILE must be ignored"); +#[test] +fn args_debug() { + assert_eq!( + format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()), + format!("{:?}", args()) + ); +} - remove_var("USERPROFILE"); +#[test] +fn args_os_debug() { + assert_eq!( + format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()), + format!("{:?}", args_os()) + ); +} - if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } - if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); } - } - } +#[test] +fn vars_debug() { + assert_eq!( + format!("Vars {{ inner: {:?} }}", vars().collect::<Vec<_>>()), + format!("{:?}", vars()) + ); } -#[test] // miri shouldn't detect any data race in this fn -#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)] -fn test_env_get_set_multithreaded() { - let getter = thread::spawn(|| { - for _ in 0..100 { - let _ = var_os("foo"); - } - }); - - let setter = thread::spawn(|| { - for _ in 0..100 { - set_var("foo", "bar"); - } - }); - - let _ = getter.join(); - let _ = setter.join(); +#[test] +fn vars_os_debug() { + assert_eq!( + format!("VarsOs {{ inner: {:?} }}", vars_os().collect::<Vec<_>>()), + format!("{:?}", vars_os()) + ); } diff --git a/library/std/tests/env_modify.rs b/library/std/tests/env_modify.rs new file mode 100644 index 00000000000..60747447350 --- /dev/null +++ b/library/std/tests/env_modify.rs @@ -0,0 +1,166 @@ +// These tests are in a separate integration test as they modify the environment, +// and would otherwise cause some other tests to fail. + +use std::env::*; +use std::ffi::{OsStr, OsString}; + +use rand::distributions::{Alphanumeric, DistString}; + +mod common; +use std::thread; + +use common::test_rng; + +#[track_caller] +fn make_rand_name() -> OsString { + let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10)); + let n = OsString::from(n); + assert!(var_os(&n).is_none()); + n +} + +fn eq(a: Option<OsString>, b: Option<&str>) { + assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); +} + +#[test] +fn test_set_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + eq(var_os(&n), Some("VALUE")); +} + +#[test] +fn test_remove_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + remove_var(&n); + eq(var_os(&n), None); +} + +#[test] +fn test_set_var_overwrite() { + let n = make_rand_name(); + set_var(&n, "1"); + set_var(&n, "2"); + eq(var_os(&n), Some("2")); + set_var(&n, ""); + eq(var_os(&n), Some("")); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_var_big() { + let mut s = "".to_string(); + let mut i = 0; + while i < 100 { + s.push_str("aaaaaaaaaa"); + i += 1; + } + let n = make_rand_name(); + set_var(&n, &s); + eq(var_os(&n), Some(&s)); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_env_set_get_huge() { + let n = make_rand_name(); + let s = "x".repeat(10000); + set_var(&n, &s); + eq(var_os(&n), Some(&s)); + remove_var(&n); + eq(var_os(&n), None); +} + +#[test] +fn test_env_set_var() { + let n = make_rand_name(); + + let mut e = vars_os(); + set_var(&n, "VALUE"); + assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + + assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); +} + +#[test] +#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))] +#[allow(deprecated)] +fn env_home_dir() { + use std::path::PathBuf; + + fn var_to_os_string(var: Result<String, VarError>) -> Option<OsString> { + match var { + Ok(var) => Some(OsString::from(var)), + Err(VarError::NotUnicode(var)) => Some(var), + _ => None, + } + } + + cfg_if::cfg_if! { + if #[cfg(unix)] { + let oldhome = var_to_os_string(var("HOME")); + + set_var("HOME", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + if cfg!(target_os = "android") { + assert!(home_dir().is_none()); + } else { + // When HOME is not set, some platforms return `None`, + // but others return `Some` with a default. + // Just check that it is not "/home/MountainView". + assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + } + + if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } + } else if #[cfg(windows)] { + let oldhome = var_to_os_string(var("HOME")); + let olduserprofile = var_to_os_string(var("USERPROFILE")); + + remove_var("HOME"); + remove_var("USERPROFILE"); + + assert!(home_dir().is_some()); + + set_var("HOME", "/home/PaloAlto"); + assert_ne!(home_dir(), Some(PathBuf::from("/home/PaloAlto")), "HOME must not be used"); + + set_var("USERPROFILE", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + set_var("USERPROFILE", ""); + assert_ne!(home_dir(), Some(PathBuf::from("")), "Empty USERPROFILE must be ignored"); + + remove_var("USERPROFILE"); + + if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } + if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); } + } + } +} + +#[test] // miri shouldn't detect any data race in this fn +#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)] +fn test_env_get_set_multithreaded() { + let getter = thread::spawn(|| { + for _ in 0..100 { + let _ = var_os("foo"); + } + }); + + let setter = thread::spawn(|| { + for _ in 0..100 { + set_var("foo", "bar"); + } + }); + + let _ = getter.join(); + let _ = setter.join(); +} diff --git a/library/std/src/error/tests.rs b/library/std/tests/error.rs index 88a9f33c079..8fd6eb3c020 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/tests/error.rs @@ -1,7 +1,8 @@ -use core::error::Request; +#![feature(error_generic_member_access, error_reporter)] -use super::Error; -use crate::fmt; +use std::backtrace::Backtrace; +use std::error::{Error, Report, Request}; +use std::fmt; #[derive(Debug, PartialEq)] struct A; @@ -38,9 +39,6 @@ fn downcasting() { } } -use crate::backtrace::Backtrace; -use crate::error::Report; - #[derive(Debug)] struct SuperError { source: SuperErrorSideKick, diff --git a/library/std/src/f128/tests.rs b/library/std/tests/floats/f128.rs index cbcf9f96239..d0e8b157e6b 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/tests/floats/f128.rs @@ -1,11 +1,11 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f128)] -use crate::f128::consts; -use crate::num::FpCategory as Fp; +use std::f128::consts; +use std::num::FpCategory as Fp; #[cfg(reliable_f128_math)] -use crate::ops::Rem; -use crate::ops::{Add, Div, Mul, Sub}; +use std::ops::Rem; +use std::ops::{Add, Div, Mul, Sub}; // Note these tolerances make sense around zero, but not for more extreme exponents. @@ -762,8 +762,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { - use super::consts; - let pi: f128 = consts::PI; let frac_pi_2: f128 = consts::FRAC_PI_2; let frac_pi_3: f128 = consts::FRAC_PI_3; diff --git a/library/std/src/f16/tests.rs b/library/std/tests/floats/f16.rs index 684ee3f3855..5180f3d40f3 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/tests/floats/f16.rs @@ -1,8 +1,8 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f16)] -use crate::f16::consts; -use crate::num::{FpCategory as Fp, *}; +use std::f16::consts; +use std::num::FpCategory as Fp; /// Tolerance for results on the order of 10.0e-2 #[allow(unused)] @@ -54,7 +54,7 @@ macro_rules! assert_f16_biteq { #[test] fn test_num_f16() { - test_num(10f16, 2f16); + crate::test_num(10f16, 2f16); } #[test] @@ -734,7 +734,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { // FIXME(f16_f128): add math tests when available - use super::consts; let pi: f16 = consts::PI; let frac_pi_2: f16 = consts::FRAC_PI_2; diff --git a/library/std/src/f32/tests.rs b/library/std/tests/floats/f32.rs index 99cfcfb231d..bf7641986ad 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/tests/floats/f32.rs @@ -1,5 +1,5 @@ -use crate::f32::consts; -use crate::num::{FpCategory as Fp, *}; +use std::f32::consts; +use std::num::FpCategory as Fp; /// Smallest number const TINY_BITS: u32 = 0x1; @@ -35,7 +35,7 @@ macro_rules! assert_f32_biteq { #[test] fn test_num_f32() { - test_num(10f32, 2f32); + crate::test_num(10f32, 2f32); } #[test] @@ -700,8 +700,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { - use super::consts; - let pi: f32 = consts::PI; let frac_pi_2: f32 = consts::FRAC_PI_2; let frac_pi_3: f32 = consts::FRAC_PI_3; diff --git a/library/std/src/f64/tests.rs b/library/std/tests/floats/f64.rs index 3fac2efe0d7..cbbfcd15efd 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/tests/floats/f64.rs @@ -1,5 +1,5 @@ -use crate::f64::consts; -use crate::num::{FpCategory as Fp, *}; +use std::f64::consts; +use std::num::FpCategory as Fp; /// Smallest number const TINY_BITS: u64 = 0x1; @@ -35,7 +35,7 @@ macro_rules! assert_f64_biteq { #[test] fn test_num_f64() { - test_num(10f64, 2f64); + crate::test_num(10f64, 2f64); } #[test] @@ -112,7 +112,6 @@ fn test_neg_zero() { assert_eq!(Fp::Zero, neg_zero.classify()); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_one() { let one: f64 = 1.0f64; @@ -165,7 +164,6 @@ fn test_is_finite() { assert!((-109.2f64).is_finite()); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_is_normal() { let nan: f64 = f64::NAN; @@ -183,7 +181,6 @@ fn test_is_normal() { assert!(!1e-308f64.is_normal()); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_classify() { let nan: f64 = f64::NAN; @@ -683,7 +680,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { - use super::consts; let pi: f64 = consts::PI; let frac_pi_2: f64 = consts::FRAC_PI_2; let frac_pi_3: f64 = consts::FRAC_PI_3; diff --git a/library/std/tests/floats/lib.rs b/library/std/tests/floats/lib.rs new file mode 100644 index 00000000000..ad82f1a44e7 --- /dev/null +++ b/library/std/tests/floats/lib.rs @@ -0,0 +1,42 @@ +#![feature(f16, f128, float_gamma, float_minimum_maximum)] + +use std::fmt; +use std::ops::{Add, Div, Mul, Rem, Sub}; + +/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; + ($a:expr, $b:expr, $lim:expr) => {{ + let (a, b) = (&$a, &$b); + let diff = (*a - *b).abs(); + assert!( + diff < $lim, + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", + lim = $lim + ); + }}; +} + +/// Helper function for testing numeric operations +pub fn test_num<T>(ten: T, two: T) +where + T: PartialEq + + Add<Output = T> + + Sub<Output = T> + + Mul<Output = T> + + Div<Output = T> + + Rem<Output = T> + + fmt::Debug + + Copy, +{ + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); + assert_eq!(ten.rem(two), ten % two); +} + +mod f128; +mod f16; +mod f32; +mod f64; diff --git a/library/std/tests/istr.rs b/library/std/tests/istr.rs index 9a127ae803e..e481872977a 100644 --- a/library/std/tests/istr.rs +++ b/library/std/tests/istr.rs @@ -5,7 +5,7 @@ fn test_stack_assign() { let t: String = "a".to_string(); assert_eq!(s, t); let u: String = "b".to_string(); - assert!((s != u)); + assert!(s != u); } #[test] @@ -19,7 +19,7 @@ fn test_heap_assign() { let t: String = "a big ol' string".to_string(); assert_eq!(s, t); let u: String = "a bad ol' string".to_string(); - assert!((s != u)); + assert!(s != u); } #[test] diff --git a/library/std/src/num/tests.rs b/library/std/tests/num.rs index df0df3f23f7..a7400f1c02d 100644 --- a/library/std/src/num/tests.rs +++ b/library/std/tests/num.rs @@ -1,4 +1,4 @@ -use crate::ops::Mul; +use std::ops::Mul; #[test] fn test_saturating_add_uint() { @@ -190,8 +190,8 @@ fn test_uint_to_str_overflow() { assert_eq!(u64_val.to_string(), "0"); } -fn from_str<T: crate::str::FromStr>(t: &str) -> Option<T> { - crate::str::FromStr::from_str(t).ok() +fn from_str<T: std::str::FromStr>(t: &str) -> Option<T> { + std::str::FromStr::from_str(t).ok() } #[test] diff --git a/library/std/src/panic/tests.rs b/library/std/tests/panic.rs index b37d74011cc..f13b931dd22 100644 --- a/library/std/src/panic/tests.rs +++ b/library/std/tests/panic.rs @@ -1,9 +1,9 @@ #![allow(dead_code)] -use crate::cell::RefCell; -use crate::panic::{AssertUnwindSafe, UnwindSafe}; -use crate::rc::Rc; -use crate::sync::{Arc, Mutex, RwLock}; +use std::cell::RefCell; +use std::panic::{AssertUnwindSafe, UnwindSafe}; +use std::rc::Rc; +use std::sync::{Arc, Mutex, RwLock}; struct Foo { a: i32, diff --git a/library/std/src/path/tests.rs b/library/std/tests/path.rs index 3f96ac4672a..978402b6fda 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/tests/path.rs @@ -1,10 +1,19 @@ -use core::hint::black_box; - -use super::*; -use crate::collections::{BTreeSet, HashSet}; -use crate::hash::DefaultHasher; -use crate::mem::MaybeUninit; -use crate::ptr; +#![feature( + clone_to_uninit, + path_add_extension, + path_file_prefix, + maybe_uninit_slice, + os_string_pathbuf_leak +)] + +use std::clone::CloneToUninit; +use std::ffi::OsStr; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::mem::MaybeUninit; +use std::path::*; +use std::ptr; +use std::rc::Rc; +use std::sync::Arc; #[allow(unknown_lints, unused_macro_rules)] macro_rules! t ( @@ -110,7 +119,7 @@ macro_rules! t ( #[test] fn into() { - use crate::borrow::Cow; + use std::borrow::Cow; let static_path = Path::new("/home/foo"); let static_cow_path: Cow<'static, Path> = static_path.into(); @@ -1525,7 +1534,7 @@ pub fn test_with_added_extension() { #[test] fn test_eq_receivers() { - use crate::borrow::Cow; + use std::borrow::Cow; let borrowed: &Path = Path::new("foo/bar"); let mut owned: PathBuf = PathBuf::new(); @@ -1550,7 +1559,7 @@ fn test_eq_receivers() { #[test] pub fn test_compare() { - use crate::hash::{DefaultHasher, Hash, Hasher}; + use std::hash::{DefaultHasher, Hash, Hasher}; fn hash<T: Hash>(t: T) -> u64 { let mut s = DefaultHasher::new(); @@ -1867,12 +1876,12 @@ fn test_ord() { #[test] #[cfg(any(unix, target_os = "wasi"))] fn test_unix_absolute() { - use crate::path::absolute; + use std::path::absolute; assert!(absolute("").is_err()); let relative = "a/b"; - let mut expected = crate::env::current_dir().unwrap(); + let mut expected = std::env::current_dir().unwrap(); expected.push(relative); assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); @@ -1888,7 +1897,7 @@ fn test_unix_absolute() { ); // Test leading `.` and `..` components - let curdir = crate::env::current_dir().unwrap(); + let curdir = std::env::current_dir().unwrap(); assert_eq!(absolute("./a").unwrap().as_os_str(), curdir.join("a").as_os_str()); assert_eq!(absolute("../a").unwrap().as_os_str(), curdir.join("../a").as_os_str()); // return /pwd/../a } @@ -1896,12 +1905,12 @@ fn test_unix_absolute() { #[test] #[cfg(windows)] fn test_windows_absolute() { - use crate::path::absolute; + use std::path::absolute; // An empty path is an error. assert!(absolute("").is_err()); let relative = r"a\b"; - let mut expected = crate::env::current_dir().unwrap(); + let mut expected = std::env::current_dir().unwrap(); expected.push(relative); assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); @@ -1953,116 +1962,6 @@ fn test_extension_path_sep_alternate() { assert_eq!(path, Path::new("path/to/file.d\\test")); } -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { - let prefix = "my/home"; - let mut paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - paths.sort(); - - b.iter(|| { - black_box(paths.as_mut_slice()).sort_unstable(); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = BTreeSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(paths[500].as_path()); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { - let prefix = "my/home"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = BTreeSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(paths[500].as_path()); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_hashset(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = HashSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(black_box(paths[500].as_path())) - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_hashset_miss(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = HashSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - let probe = PathBuf::from(prefix).join("other"); - - b.iter(|| set.remove(black_box(probe.as_path()))); -} - -#[bench] -fn bench_hash_path_short(b: &mut test::Bencher) { - let mut hasher = DefaultHasher::new(); - let path = Path::new("explorer.exe"); - - b.iter(|| black_box(path).hash(&mut hasher)); - - black_box(hasher.finish()); -} - -#[bench] -fn bench_hash_path_long(b: &mut test::Bencher) { - let mut hasher = DefaultHasher::new(); - let path = - Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff"); - - b.iter(|| black_box(path).hash(&mut hasher)); - - black_box(hasher.finish()); -} - #[test] fn clone_to_uninit() { let a = Path::new("hello.txt"); diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index df946cdcf2b..00d99a578d5 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -1,7 +1,7 @@ #![feature(anonymous_pipe)] fn main() { - #[cfg(all(not(miri), any(unix, windows)))] + #[cfg(all(not(miri), any(unix, windows), not(target_os = "emscripten")))] { use std::io::{Read, pipe}; use std::{env, process}; diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 3e72e371ade..43b45cb2d2b 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -5,7 +5,8 @@ use std::{env, fs, process, str}; mod common; #[test] -#[cfg_attr(any(miri, target_os = "wasi"), ignore)] // Process spawning not supported by Miri and wasi +// Process spawning not supported by Miri, Emscripten and wasi +#[cfg_attr(any(miri, target_os = "emscripten", target_os = "wasi"), ignore)] fn issue_15149() { // If we're the parent, copy our own binary to a new directory. let my_path = env::current_exe().unwrap(); diff --git a/library/std/tests/seq-compare.rs b/library/std/tests/seq-compare.rs index 221f1c7cabd..ec39c5b603c 100644 --- a/library/std/tests/seq-compare.rs +++ b/library/std/tests/seq-compare.rs @@ -1,15 +1,15 @@ #[test] fn seq_compare() { - assert!(("hello".to_string() < "hellr".to_string())); - assert!(("hello ".to_string() > "hello".to_string())); - assert!(("hello".to_string() != "there".to_string())); - assert!((vec![1, 2, 3, 4] > vec![1, 2, 3])); - assert!((vec![1, 2, 3] < vec![1, 2, 3, 4])); - assert!((vec![1, 2, 4, 4] > vec![1, 2, 3, 4])); - assert!((vec![1, 2, 3, 4] < vec![1, 2, 4, 4])); - assert!((vec![1, 2, 3] <= vec![1, 2, 3])); - assert!((vec![1, 2, 3] <= vec![1, 2, 3, 3])); - assert!((vec![1, 2, 3, 4] > vec![1, 2, 3])); + assert!("hello".to_string() < "hellr".to_string()); + assert!("hello ".to_string() > "hello".to_string()); + assert!("hello".to_string() != "there".to_string()); + assert!(vec![1, 2, 3, 4] > vec![1, 2, 3]); + assert!(vec![1, 2, 3] < vec![1, 2, 3, 4]); + assert!(vec![1, 2, 4, 4] > vec![1, 2, 3, 4]); + assert!(vec![1, 2, 3, 4] < vec![1, 2, 4, 4]); + assert!(vec![1, 2, 3] <= vec![1, 2, 3]); + assert!(vec![1, 2, 3] <= vec![1, 2, 3, 3]); + assert!(vec![1, 2, 3, 4] > vec![1, 2, 3]); assert_eq!(vec![1, 2, 3], vec![1, 2, 3]); - assert!((vec![1, 2, 3] != vec![1, 1, 3])); + assert!(vec![1, 2, 3] != vec![1, 1, 3]); } diff --git a/library/std/tests/switch-stdout.rs b/library/std/tests/switch-stdout.rs index 42011a9b3da..91fe0200f6c 100644 --- a/library/std/tests/switch-stdout.rs +++ b/library/std/tests/switch-stdout.rs @@ -14,7 +14,7 @@ use std::os::windows::io::OwnedHandle; fn switch_stdout_to(file: OwnedFd) -> OwnedFd { use std::os::unix::prelude::*; - extern "C" { + unsafe extern "C" { fn dup(old: i32) -> i32; fn dup2(old: i32, new: i32) -> i32; } @@ -32,7 +32,7 @@ fn switch_stdout_to(file: OwnedFd) -> OwnedFd { fn switch_stdout_to(file: OwnedHandle) -> OwnedHandle { use std::os::windows::prelude::*; - extern "system" { + unsafe extern "system" { fn GetStdHandle(nStdHandle: u32) -> *mut u8; fn SetStdHandle(nStdHandle: u32, handle: *mut u8) -> i32; } diff --git a/library/std/src/sync/barrier/tests.rs b/library/std/tests/sync/barrier.rs index 0fbcd998812..8aefff9d507 100644 --- a/library/std/src/sync/barrier/tests.rs +++ b/library/std/tests/sync/barrier.rs @@ -1,6 +1,6 @@ -use crate::sync::mpsc::{TryRecvError, channel}; -use crate::sync::{Arc, Barrier}; -use crate::thread; +use std::sync::mpsc::{TryRecvError, channel}; +use std::sync::{Arc, Barrier}; +use std::thread; #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads diff --git a/library/std/src/sync/poison/condvar/tests.rs b/library/std/tests/sync/condvar.rs index f9e9066bc92..834de6bb1c2 100644 --- a/library/std/src/sync/poison/condvar/tests.rs +++ b/library/std/tests/sync/condvar.rs @@ -1,8 +1,8 @@ -use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread; -use crate::time::Duration; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread; +use std::time::Duration; #[test] fn smoke() { diff --git a/library/std/src/sync/lazy_lock/tests.rs b/library/std/tests/sync/lazy_lock.rs index 7d7dde54349..6c14b79f2ce 100644 --- a/library/std/src/sync/lazy_lock/tests.rs +++ b/library/std/tests/sync/lazy_lock.rs @@ -1,8 +1,8 @@ -use crate::cell::LazyCell; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::{LazyLock, Mutex, OnceLock}; -use crate::{panic, thread}; +use std::cell::LazyCell; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::{LazyLock, Mutex, OnceLock}; +use std::{panic, thread}; fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() @@ -149,7 +149,7 @@ fn is_sync_send() { #[should_panic = "has previously been poisoned"] fn lazy_force_mut_panic() { let mut lazy = LazyLock::<String>::new(|| panic!()); - crate::panic::catch_unwind(crate::panic::AssertUnwindSafe(|| { + panic::catch_unwind(panic::AssertUnwindSafe(|| { let _ = LazyLock::force_mut(&mut lazy); })) .unwrap_err(); diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs new file mode 100644 index 00000000000..51190f0894f --- /dev/null +++ b/library/std/tests/sync/lib.rs @@ -0,0 +1,31 @@ +#![feature(lazy_get)] +#![feature(mapped_lock_guards)] +#![feature(mpmc_channel)] +#![feature(once_cell_try)] +#![feature(lock_value_accessors)] +#![feature(reentrant_lock)] +#![feature(rwlock_downgrade)] +#![feature(std_internals)] +#![allow(internal_features)] + +mod barrier; +mod condvar; +mod lazy_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpmc; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpsc; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpsc_sync; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mutex; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod once; +mod once_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod reentrant_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod rwlock; + +#[path = "../common/mod.rs"] +mod common; diff --git a/library/std/src/sync/mpmc/tests.rs b/library/std/tests/sync/mpmc.rs index ab14050df6c..81b92297f76 100644 --- a/library/std/src/sync/mpmc/tests.rs +++ b/library/std/tests/sync/mpmc.rs @@ -1,5 +1,6 @@ -use super::*; -use crate::{env, thread}; +use std::sync::mpmc::*; +use std::time::{Duration, Instant}; +use std::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/tests/sync/mpsc.rs index 13892fa0d18..1d8edfde44b 100644 --- a/library/std/src/sync/mpsc/tests.rs +++ b/library/std/tests/sync/mpsc.rs @@ -1,5 +1,6 @@ -use super::*; -use crate::{env, thread}; +use std::sync::mpsc::*; +use std::time::{Duration, Instant}; +use std::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/tests/sync/mpsc_sync.rs index 49b65c8efe6..a7f326d201b 100644 --- a/library/std/src/sync/mpsc/sync_tests.rs +++ b/library/std/tests/sync/mpsc_sync.rs @@ -1,7 +1,8 @@ -use super::*; -use crate::rc::Rc; -use crate::sync::mpmc::SendTimeoutError; -use crate::{env, thread}; +use std::rc::Rc; +use std::sync::mpmc::SendTimeoutError; +use std::sync::mpsc::*; +use std::time::Duration; +use std::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/poison/mutex/tests.rs b/library/std/tests/sync/mutex.rs index 395c8aada08..74c62720107 100644 --- a/library/std/src/sync/poison/mutex/tests.rs +++ b/library/std/tests/sync/mutex.rs @@ -1,10 +1,10 @@ -use crate::fmt::Debug; -use crate::ops::FnMut; -use crate::panic::{self, AssertUnwindSafe}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; -use crate::{hint, mem, thread}; +use std::fmt::Debug; +use std::ops::FnMut; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; +use std::{hint, mem, thread}; struct Packet<T>(Arc<(Mutex<T>, Condvar)>); diff --git a/library/std/src/sync/poison/once/tests.rs b/library/std/tests/sync/once.rs index ce96468aeb6..a3ffc73fe06 100644 --- a/library/std/src/sync/poison/once/tests.rs +++ b/library/std/tests/sync/once.rs @@ -1,9 +1,9 @@ -use super::Once; -use crate::sync::atomic::AtomicBool; -use crate::sync::atomic::Ordering::Relaxed; -use crate::sync::mpsc::channel; -use crate::time::Duration; -use crate::{panic, thread}; +use std::sync::Once; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::mpsc::channel; +use std::time::Duration; +use std::{panic, thread}; #[test] fn smoke_once() { diff --git a/library/std/src/sync/once_lock/tests.rs b/library/std/tests/sync/once_lock.rs index 5113d436c3c..ac9aaa8892e 100644 --- a/library/std/src/sync/once_lock/tests.rs +++ b/library/std/tests/sync/once_lock.rs @@ -1,8 +1,8 @@ -use crate::sync::OnceLock; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::mpsc::channel; -use crate::{panic, thread}; +use std::sync::OnceLock; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::mpsc::channel; +use std::{panic, thread}; fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() @@ -34,15 +34,6 @@ fn sync_once_cell_get_mut() { } #[test] -fn sync_once_cell_get_unchecked() { - let c = OnceLock::new(); - c.set(92).unwrap(); - unsafe { - assert_eq!(c.get_unchecked(), &92); - } -} - -#[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn sync_once_cell_drop() { static DROP_CNT: AtomicUsize = AtomicUsize::new(0); @@ -88,7 +79,6 @@ fn get_or_try_init() { let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); assert!(res.is_err()); - assert!(!cell.is_initialized()); assert!(cell.get().is_none()); assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); @@ -174,7 +164,7 @@ fn sync_once_cell_does_not_leak_partially_constructed_boxes() { break; } #[cfg(target_env = "sgx")] - crate::thread::yield_now(); + std::thread::yield_now(); } }); } diff --git a/library/std/src/sync/reentrant_lock/tests.rs b/library/std/tests/sync/reentrant_lock.rs index aeef0289d28..2b7b87e3623 100644 --- a/library/std/src/sync/reentrant_lock/tests.rs +++ b/library/std/tests/sync/reentrant_lock.rs @@ -1,7 +1,6 @@ -use super::ReentrantLock; -use crate::cell::RefCell; -use crate::sync::Arc; -use crate::thread; +use std::cell::RefCell; +use std::sync::{Arc, ReentrantLock}; +use std::thread; #[test] fn smoke() { diff --git a/library/std/src/sync/poison/rwlock/tests.rs b/library/std/tests/sync/rwlock.rs index 057c2f1a5d7..bd4bc7a14bc 100644 --- a/library/std/src/sync/poison/rwlock/tests.rs +++ b/library/std/tests/sync/rwlock.rs @@ -1,15 +1,15 @@ -use rand::Rng; - -use crate::fmt::Debug; -use crate::ops::FnMut; -use crate::panic::{self, AssertUnwindSafe}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{ +use std::fmt::Debug; +use std::ops::FnMut; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{ Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError, }; -use crate::{hint, mem, thread}; +use std::{hint, mem, thread}; + +use rand::Rng; #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); @@ -57,7 +57,7 @@ fn frob() { let tx = tx.clone(); let r = r.clone(); thread::spawn(move || { - let mut rng = crate::test_helpers::test_rng(); + let mut rng = crate::common::test_rng(); for _ in 0..M { if rng.gen_bool(1.0 / (N as f64)) { drop(r.write().unwrap()); @@ -704,7 +704,7 @@ fn test_downgrade_atomic() { // Wait for a good amount of time so that evil threads go to sleep. // Note: this is not strictly necessary... - let eternity = crate::time::Duration::from_millis(42); + let eternity = std::time::Duration::from_millis(42); thread::sleep(eternity); // Once everyone is asleep, set the value to `NEW_VALUE`. diff --git a/library/std/src/thread/local/dynamic_tests.rs b/library/std/tests/thread_local/dynamic_tests.rs index dd180041648..454462b3925 100644 --- a/library/std/src/thread/local/dynamic_tests.rs +++ b/library/std/tests/thread_local/dynamic_tests.rs @@ -1,6 +1,6 @@ -use crate::cell::RefCell; -use crate::collections::HashMap; -use crate::thread_local; +use std::cell::RefCell; +use std::collections::HashMap; +use std::thread_local; #[test] fn smoke() { diff --git a/library/std/tests/thread_local/lib.rs b/library/std/tests/thread_local/lib.rs new file mode 100644 index 00000000000..c5291435425 --- /dev/null +++ b/library/std/tests/thread_local/lib.rs @@ -0,0 +1,4 @@ +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod tests; + +mod dynamic_tests; diff --git a/library/std/src/thread/local/tests.rs b/library/std/tests/thread_local/tests.rs index 9d4f52a0921..aa020c2559c 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/tests/thread_local/tests.rs @@ -1,8 +1,8 @@ -use crate::cell::{Cell, UnsafeCell}; -use crate::sync::atomic::{AtomicU8, Ordering}; -use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread::{self, Builder, LocalKey}; -use crate::thread_local; +use std::cell::{Cell, UnsafeCell}; +use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread::{self, Builder, LocalKey}; +use std::thread_local; #[derive(Clone, Default)] struct Signal(Arc<(Mutex<bool>, Condvar)>); diff --git a/library/std/src/time/tests.rs b/library/std/tests/time.rs index e88f2d5e806..40709eae37c 100644 --- a/library/std/src/time/tests.rs +++ b/library/std/tests/time.rs @@ -1,9 +1,7 @@ -use core::fmt::Debug; +#![feature(duration_constants)] -#[cfg(not(target_arch = "wasm32"))] -use test::{Bencher, black_box}; - -use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; +use std::fmt::Debug; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; macro_rules! assert_almost_eq { ($a:expr, $b:expr) => {{ @@ -29,10 +27,10 @@ fn instant_monotonic() { #[test] #[cfg(not(target_arch = "wasm32"))] -fn instant_monotonic_concurrent() -> crate::thread::Result<()> { +fn instant_monotonic_concurrent() -> std::thread::Result<()> { let threads: Vec<_> = (0..8) .map(|_| { - crate::thread::spawn(|| { + std::thread::spawn(|| { let mut old = Instant::now(); let count = if cfg!(miri) { 1_000 } else { 5_000_000 }; for _ in 0..count { @@ -229,46 +227,3 @@ fn big_math() { check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub); check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub); } - -macro_rules! bench_instant_threaded { - ($bench_name:ident, $thread_count:expr) => { - #[bench] - #[cfg(not(target_arch = "wasm32"))] - fn $bench_name(b: &mut Bencher) -> crate::thread::Result<()> { - use crate::sync::Arc; - use crate::sync::atomic::{AtomicBool, Ordering}; - - let running = Arc::new(AtomicBool::new(true)); - - let threads: Vec<_> = (0..$thread_count) - .map(|_| { - let flag = Arc::clone(&running); - crate::thread::spawn(move || { - while flag.load(Ordering::Relaxed) { - black_box(Instant::now()); - } - }) - }) - .collect(); - - b.iter(|| { - let a = Instant::now(); - let b = Instant::now(); - assert!(b >= a); - }); - - running.store(false, Ordering::Relaxed); - - for t in threads { - t.join()?; - } - Ok(()) - } - }; -} - -bench_instant_threaded!(instant_contention_01_threads, 0); -bench_instant_threaded!(instant_contention_02_threads, 1); -bench_instant_threaded!(instant_contention_04_threads, 3); -bench_instant_threaded!(instant_contention_08_threads, 7); -bench_instant_threaded!(instant_contention_16_threads, 15); diff --git a/library/std/tests/win_delete_self.rs b/library/std/tests/win_delete_self.rs new file mode 100644 index 00000000000..ce505de69a2 --- /dev/null +++ b/library/std/tests/win_delete_self.rs @@ -0,0 +1,9 @@ +#![cfg(windows)] + +/// Attempting to delete a running binary should return an error on Windows. +#[test] +#[cfg_attr(miri, ignore)] // `remove_file` does not work in Miri on Windows +fn win_delete_self() { + let path = std::env::current_exe().unwrap(); + assert!(std::fs::remove_file(path).is_err()); +} diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 4ccd825bf8d..ef6786f4316 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -1,7 +1,7 @@ //! Module converting command-line arguments into test configuration. use std::env; -use std::io::{self, IsTerminal}; +use std::io::{self, IsTerminal, Write}; use std::path::PathBuf; use super::options::{ColorConfig, Options, OutputFormat, RunIgnored}; @@ -44,7 +44,7 @@ impl TestOpts { } /// Result of parsing the options. -pub type OptRes = Result<TestOpts, String>; +pub(crate) type OptRes = Result<TestOpts, String>; /// Result of parsing the option part. type OptPartRes<T> = Result<T, String>; @@ -58,7 +58,7 @@ fn optgroups() -> getopts::Options { .optflag("", "bench", "Run benchmarks instead of tests") .optflag("", "list", "List all tests and benchmarks") .optflag("h", "help", "Display this message") - .optopt("", "logfile", "Write logs to the specified file", "PATH") + .optopt("", "logfile", "Write logs to the specified file (deprecated)", "PATH") .optflag( "", "nocapture", @@ -281,6 +281,10 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let options = Options::new().display_output(matches.opt_present("show-output")); + if logfile.is_some() { + let _ = write!(io::stderr(), "warning: `--logfile` is deprecated"); + } + let test_opts = TestOpts { list, filters, diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 4d4cdcf4d7b..8f29f1dada5 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -20,7 +20,7 @@ use super::types::{NamePadding, TestDesc, TestDescAndFn}; use super::{filter_tests, run_tests, term}; /// Generic wrapper over stdout. -pub enum OutputLocation<T> { +pub(crate) enum OutputLocation<T> { Pretty(Box<term::StdoutTerminal>), Raw(T), } @@ -41,7 +41,7 @@ impl<T: Write> Write for OutputLocation<T> { } } -pub struct ConsoleTestDiscoveryState { +pub(crate) struct ConsoleTestDiscoveryState { pub log_out: Option<File>, pub tests: usize, pub benchmarks: usize, @@ -49,7 +49,7 @@ pub struct ConsoleTestDiscoveryState { } impl ConsoleTestDiscoveryState { - pub fn new(opts: &TestOpts) -> io::Result<ConsoleTestDiscoveryState> { + pub(crate) fn new(opts: &TestOpts) -> io::Result<ConsoleTestDiscoveryState> { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -58,7 +58,7 @@ impl ConsoleTestDiscoveryState { Ok(ConsoleTestDiscoveryState { log_out, tests: 0, benchmarks: 0, ignored: 0 }) } - pub fn write_log<F, S>(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log<F, S>(&mut self, msg: F) -> io::Result<()> where S: AsRef<str>, F: FnOnce() -> S, @@ -74,7 +74,7 @@ impl ConsoleTestDiscoveryState { } } -pub struct ConsoleTestState { +pub(crate) struct ConsoleTestState { pub log_out: Option<File>, pub total: usize, pub passed: usize, @@ -92,7 +92,7 @@ pub struct ConsoleTestState { } impl ConsoleTestState { - pub fn new(opts: &TestOpts) -> io::Result<ConsoleTestState> { + pub(crate) fn new(opts: &TestOpts) -> io::Result<ConsoleTestState> { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -116,7 +116,7 @@ impl ConsoleTestState { }) } - pub fn write_log<F, S>(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log<F, S>(&mut self, msg: F) -> io::Result<()> where S: AsRef<str>, F: FnOnce() -> S, @@ -131,7 +131,7 @@ impl ConsoleTestState { } } - pub fn write_log_result( + pub(crate) fn write_log_result( &mut self, test: &TestDesc, result: &TestResult, @@ -170,7 +170,7 @@ impl ConsoleTestState { } // List the tests to console, and optionally to logfile. Filters are honored. -pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<()> { +pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<()> { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout().lock()), Some(t) => OutputLocation::Pretty(t), @@ -314,9 +314,10 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Resu let mut st = ConsoleTestState::new(opts)?; // Prevent the usage of `Instant` in some cases: - // - It's currently not supported for wasm targets. + // - It's currently not supported for wasm targets without Emscripten nor WASI. + // - It's currently not supported for zkvm targets. let is_instant_unsupported = - (cfg!(target_family = "wasm") && !cfg!(target_os = "wasi")) || cfg!(target_os = "zkvm"); + (cfg!(target_family = "wasm") && cfg!(target_os = "unknown")) || cfg!(target_os = "zkvm"); let start_time = (!is_instant_unsupported).then(Instant::now); run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?; diff --git a/library/test/src/formatters/json.rs b/library/test/src/formatters/json.rs index aa1c50641cb..92c1c0716f1 100644 --- a/library/test/src/formatters/json.rs +++ b/library/test/src/formatters/json.rs @@ -13,7 +13,7 @@ pub(crate) struct JsonFormatter<T> { } impl<T: Write> JsonFormatter<T> { - pub fn new(out: OutputLocation<T>) -> Self { + pub(crate) fn new(out: OutputLocation<T>) -> Self { Self { out } } diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 96b43200840..57b1b0fecee 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -8,13 +8,13 @@ use crate::test_result::TestResult; use crate::time; use crate::types::{TestDesc, TestType}; -pub struct JunitFormatter<T> { +pub(crate) struct JunitFormatter<T> { out: OutputLocation<T>, results: Vec<(TestDesc, TestResult, Duration, Vec<u8>)>, } impl<T: Write> JunitFormatter<T> { - pub fn new(out: OutputLocation<T>) -> Self { + pub(crate) fn new(out: OutputLocation<T>) -> Self { Self { out, results: Vec::new() } } diff --git a/library/test/src/formatters/pretty.rs b/library/test/src/formatters/pretty.rs index 7089eae4330..bf3fc40db41 100644 --- a/library/test/src/formatters/pretty.rs +++ b/library/test/src/formatters/pretty.rs @@ -20,7 +20,7 @@ pub(crate) struct PrettyFormatter<T> { } impl<T: Write> PrettyFormatter<T> { - pub fn new( + pub(crate) fn new( out: OutputLocation<T>, use_color: bool, max_name_len: usize, @@ -31,19 +31,19 @@ impl<T: Write> PrettyFormatter<T> { } #[cfg(test)] - pub fn output_location(&self) -> &OutputLocation<T> { + pub(crate) fn output_location(&self) -> &OutputLocation<T> { &self.out } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result("ok", term::color::GREEN) } - pub fn write_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED", term::color::RED) } - pub fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { if let Some(message) = message { self.write_short_result(&format!("ignored, {message}"), term::color::YELLOW) } else { @@ -51,15 +51,15 @@ impl<T: Write> PrettyFormatter<T> { } } - pub fn write_time_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_time_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED (time limit exceeded)", term::color::RED) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -67,7 +67,7 @@ impl<T: Write> PrettyFormatter<T> { self.write_pretty(result, color) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -86,7 +86,7 @@ impl<T: Write> PrettyFormatter<T> { } } - pub fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() @@ -154,15 +154,15 @@ impl<T: Write> PrettyFormatter<T> { Ok(()) } - pub fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.not_failures, "successes") } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.failures, "failures") } - pub fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.time_failures, "failures (time limit exceeded)") } diff --git a/library/test/src/formatters/terse.rs b/library/test/src/formatters/terse.rs index 534aa2f3311..b28120ab56e 100644 --- a/library/test/src/formatters/terse.rs +++ b/library/test/src/formatters/terse.rs @@ -25,7 +25,7 @@ pub(crate) struct TerseFormatter<T> { } impl<T: Write> TerseFormatter<T> { - pub fn new( + pub(crate) fn new( out: OutputLocation<T>, use_color: bool, max_name_len: usize, @@ -42,11 +42,11 @@ impl<T: Write> TerseFormatter<T> { } } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result(".", term::color::GREEN) } - pub fn write_failed(&mut self, name: &str) -> io::Result<()> { + pub(crate) fn write_failed(&mut self, name: &str) -> io::Result<()> { // Put failed tests on their own line and include the test name, so that it's faster // to see which test failed without having to wait for them all to run. @@ -62,15 +62,15 @@ impl<T: Write> TerseFormatter<T> { self.write_plain("\n") } - pub fn write_ignored(&mut self) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self) -> io::Result<()> { self.write_short_result("i", term::color::YELLOW) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -95,7 +95,7 @@ impl<T: Write> TerseFormatter<T> { Ok(()) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -114,13 +114,13 @@ impl<T: Write> TerseFormatter<T> { } } - pub fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() } - pub fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nsuccesses:\n")?; let mut successes = Vec::new(); let mut stdouts = String::new(); @@ -146,7 +146,7 @@ impl<T: Write> TerseFormatter<T> { Ok(()) } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nfailures:\n")?; let mut failures = Vec::new(); let mut fail_out = String::new(); diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs index b1545cbec43..6648b669125 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use std::{env, thread}; -pub fn get_concurrency() -> usize { +pub(crate) fn get_concurrency() -> usize { if let Ok(value) = env::var("RUST_TEST_THREADS") { match value.parse::<NonZero<usize>>().ok() { Some(n) => n.get(), diff --git a/library/test/src/helpers/mod.rs b/library/test/src/helpers/mod.rs index 3c79b90b167..2fb29b4c7be 100644 --- a/library/test/src/helpers/mod.rs +++ b/library/test/src/helpers/mod.rs @@ -1,6 +1,6 @@ //! Module with common helpers not directly related to tests //! but used in `libtest`. -pub mod concurrency; -pub mod metrics; -pub mod shuffle; +pub(crate) mod concurrency; +pub(crate) mod metrics; +pub(crate) mod shuffle; diff --git a/library/test/src/helpers/shuffle.rs b/library/test/src/helpers/shuffle.rs index 14389eb0e37..53d1d0e42d4 100644 --- a/library/test/src/helpers/shuffle.rs +++ b/library/test/src/helpers/shuffle.rs @@ -4,7 +4,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use crate::cli::TestOpts; use crate::types::{TestDescAndFn, TestId, TestName}; -pub fn get_shuffle_seed(opts: &TestOpts) -> Option<u64> { +pub(crate) fn get_shuffle_seed(opts: &TestOpts) -> Option<u64> { opts.shuffle_seed.or_else(|| { if opts.shuffle { Some( @@ -19,7 +19,7 @@ pub fn get_shuffle_seed(opts: &TestOpts) -> Option<u64> { }) } -pub fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { +pub(crate) fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { let test_names: Vec<&TestName> = tests.iter().map(|test| &test.1.desc.name).collect(); let test_names_hash = calculate_hash(&test_names); let mut rng = Rng::new(shuffle_seed, test_names_hash); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 47407df909b..e523d302866 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -27,6 +27,7 @@ #![feature(thread_spawn_hook)] #![allow(internal_features)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] pub use cli::TestOpts; @@ -183,12 +184,16 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { // If we're being run in SpawnedSecondary mode, run the test here. run_test // will then exit the process. if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) { - env::remove_var(SECONDARY_TEST_INVOKER_VAR); + unsafe { + env::remove_var(SECONDARY_TEST_INVOKER_VAR); + } // Convert benchmarks to tests if we're not benchmarking. let mut tests = tests.iter().map(make_owned_test).collect::<Vec<_>>(); if env::var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR).is_ok() { - env::remove_var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR); + unsafe { + env::remove_var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR); + } } else { tests = convert_benchmarks_to_tests(tests); }; diff --git a/library/test/src/options.rs b/library/test/src/options.rs index 3eaad59474a..7a5c55f4e24 100644 --- a/library/test/src/options.rs +++ b/library/test/src/options.rs @@ -2,7 +2,7 @@ /// Number of times to run a benchmarked function #[derive(Clone, PartialEq, Eq)] -pub enum BenchMode { +pub(crate) enum BenchMode { Auto, Single, } diff --git a/library/test/src/stats/tests.rs b/library/test/src/stats/tests.rs index 4b209dcf214..7804ddc9291 100644 --- a/library/test/src/stats/tests.rs +++ b/library/test/src/stats/tests.rs @@ -573,13 +573,13 @@ fn test_sum_f64_between_ints_that_sum_to_0() { } #[bench] -pub fn sum_three_items(b: &mut Bencher) { +fn sum_three_items(b: &mut Bencher) { b.iter(|| { [1e20f64, 1.5f64, -1e20f64].sum(); }) } #[bench] -pub fn sum_many_f64(b: &mut Bencher) { +fn sum_many_f64(b: &mut Bencher) { let nums = [-1e30f64, 1e60, 1e30, 1.0, -1e60]; let v = (0..500).map(|i| nums[i % 5]).collect::<Vec<_>>(); @@ -589,4 +589,4 @@ pub fn sum_many_f64(b: &mut Bencher) { } #[bench] -pub fn no_iter(_: &mut Bencher) {} +fn no_iter(_: &mut Bencher) {} diff --git a/library/test/src/term.rs b/library/test/src/term.rs index e736e85d469..d9880a77640 100644 --- a/library/test/src/term.rs +++ b/library/test/src/term.rs @@ -62,7 +62,7 @@ pub(crate) mod color { /// A terminal with similar capabilities to an ANSI Terminal /// (foreground/background colors etc). -pub trait Terminal: Write { +pub(crate) trait Terminal: Write { /// Sets the foreground color to the given color. /// /// If the color is a bright color, but the terminal only supports 8 colors, diff --git a/library/test/src/term/win.rs b/library/test/src/term/win.rs index c77e6aac478..62e5c43ea27 100644 --- a/library/test/src/term/win.rs +++ b/library/test/src/term/win.rs @@ -52,7 +52,7 @@ struct CONSOLE_SCREEN_BUFFER_INFO { #[allow(non_snake_case)] #[link(name = "kernel32")] -extern "system" { +unsafe extern "system" { fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL; fn GetStdHandle(which: DWORD) -> HANDLE; fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL; diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 79fe07bc1ac..73dcc2e2a0c 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -12,7 +12,7 @@ use super::types::TestDesc; // Return code for secondary process. // Start somewhere other than 0 so we know the return code means what we think // it means. -pub const TR_OK: i32 = 50; +pub(crate) const TR_OK: i32 = 50; // On Windows we use __fastfail to abort, which is documented to use this // exception code. @@ -39,7 +39,7 @@ pub enum TestResult { /// Creates a `TestResult` depending on the raw result of test execution /// and associated data. -pub fn calc_result<'a>( +pub(crate) fn calc_result<'a>( desc: &TestDesc, task_result: Result<(), &'a (dyn Any + 'static + Send)>, time_opts: Option<&time::TestTimeOptions>, @@ -93,7 +93,7 @@ pub fn calc_result<'a>( } /// Creates a `TestResult` depending on the exit code of test subprocess. -pub fn get_result_from_exit_code( +pub(crate) fn get_result_from_exit_code( desc: &TestDesc, status: ExitStatus, time_opts: Option<&time::TestTimeOptions>, diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index e85e61090a9..47f581fefae 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -78,7 +78,7 @@ fn one_ignored_one_unignored_test() -> Vec<TestDescAndFn> { } #[test] -pub fn do_not_run_ignored_tests() { +fn do_not_run_ignored_tests() { fn f() -> Result<(), String> { panic!(); } @@ -106,7 +106,7 @@ pub fn do_not_run_ignored_tests() { } #[test] -pub fn ignored_tests_result_in_ignored() { +fn ignored_tests_result_in_ignored() { fn f() -> Result<(), String> { Ok(()) } @@ -133,9 +133,7 @@ pub fn ignored_tests_result_in_ignored() { assert_eq!(result, TrIgnored); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic() { fn f() -> Result<(), String> { @@ -164,9 +162,7 @@ fn test_should_panic() { assert_eq!(result, TrOk); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_good_message() { fn f() -> Result<(), String> { @@ -195,9 +191,7 @@ fn test_should_panic_good_message() { assert_eq!(result, TrOk); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_bad_message() { use crate::tests::TrFailedMsg; @@ -231,9 +225,7 @@ fn test_should_panic_bad_message() { assert_eq!(result, TrFailedMsg(failed_msg.to_string())); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_non_string_message_type() { use std::any::TypeId; @@ -272,9 +264,7 @@ fn test_should_panic_non_string_message_type() { assert_eq!(result, TrFailedMsg(failed_msg)); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_but_succeeds() { let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")]; @@ -479,7 +469,7 @@ fn parse_include_ignored_flag() { } #[test] -pub fn filter_for_ignored_option() { +fn filter_for_ignored_option() { // When we run ignored tests the test filter should filter out all the // unignored tests and flip the ignore flag on the rest to false @@ -496,7 +486,7 @@ pub fn filter_for_ignored_option() { } #[test] -pub fn run_include_ignored_option() { +fn run_include_ignored_option() { // When we "--include-ignored" tests, the ignore flag should be set to false on // all tests and no test filtered out @@ -513,7 +503,7 @@ pub fn run_include_ignored_option() { } #[test] -pub fn exclude_should_panic_option() { +fn exclude_should_panic_option() { let mut opts = TestOpts::new(); opts.run_tests = true; opts.exclude_should_panic = true; @@ -544,7 +534,7 @@ pub fn exclude_should_panic_option() { } #[test] -pub fn exact_filter_match() { +fn exact_filter_match() { fn tests() -> Vec<TestDescAndFn> { ["base", "base::test", "base::test1", "base::test2"] .into_iter() @@ -667,7 +657,7 @@ fn sample_tests() -> Vec<TestDescAndFn> { } #[test] -pub fn shuffle_tests() { +fn shuffle_tests() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -686,7 +676,7 @@ pub fn shuffle_tests() { } #[test] -pub fn shuffle_tests_with_seed() { +fn shuffle_tests_with_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -704,7 +694,7 @@ pub fn shuffle_tests_with_seed() { } #[test] -pub fn order_depends_on_more_than_seed() { +fn order_depends_on_more_than_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -732,7 +722,7 @@ pub fn order_depends_on_more_than_seed() { } #[test] -pub fn test_metricmap_compare() { +fn test_metricmap_compare() { let mut m1 = MetricMap::new(); let mut m2 = MetricMap::new(); m1.insert_metric("in-both-noise", 1000.0, 200.0); @@ -755,7 +745,7 @@ pub fn test_metricmap_compare() { } #[test] -pub fn test_bench_once_no_iter() { +fn test_bench_once_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -763,7 +753,7 @@ pub fn test_bench_once_no_iter() { } #[test] -pub fn test_bench_once_iter() { +fn test_bench_once_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) @@ -772,7 +762,7 @@ pub fn test_bench_once_iter() { } #[test] -pub fn test_bench_no_iter() { +fn test_bench_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -799,7 +789,7 @@ pub fn test_bench_no_iter() { } #[test] -pub fn test_bench_iter() { +fn test_bench_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) diff --git a/library/test/src/time.rs b/library/test/src/time.rs index 02ae050db55..f63b156b3dc 100644 --- a/library/test/src/time.rs +++ b/library/test/src/time.rs @@ -11,7 +11,7 @@ use std::{env, fmt}; use super::types::{TestDesc, TestType}; -pub const TEST_WARN_TIMEOUT_S: u64 = 60; +pub(crate) const TEST_WARN_TIMEOUT_S: u64 = 60; /// This small module contains constants used by `report-time` option. /// Those constants values will be used if corresponding environment variables are not set. @@ -22,42 +22,42 @@ pub const TEST_WARN_TIMEOUT_S: u64 = 60; /// /// Example of the expected format is `RUST_TEST_TIME_xxx=100,200`, where 100 means /// warn time, and 200 means critical time. -pub mod time_constants { +pub(crate) mod time_constants { use std::time::Duration; use super::TEST_WARN_TIMEOUT_S; /// Environment variable for overriding default threshold for unit-tests. - pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; + pub(crate) const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; // Unit tests are supposed to be really quick. - pub const UNIT_WARN: Duration = Duration::from_millis(50); - pub const UNIT_CRITICAL: Duration = Duration::from_millis(100); + pub(crate) const UNIT_WARN: Duration = Duration::from_millis(50); + pub(crate) const UNIT_CRITICAL: Duration = Duration::from_millis(100); /// Environment variable for overriding default threshold for unit-tests. - pub const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; + pub(crate) const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; // Integration tests may have a lot of work, so they can take longer to execute. - pub const INTEGRATION_WARN: Duration = Duration::from_millis(500); - pub const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); + pub(crate) const INTEGRATION_WARN: Duration = Duration::from_millis(500); + pub(crate) const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); /// Environment variable for overriding default threshold for unit-tests. - pub const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; + pub(crate) const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; // Doctests are similar to integration tests, because they can include a lot of // initialization code. - pub const DOCTEST_WARN: Duration = INTEGRATION_WARN; - pub const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; + pub(crate) const DOCTEST_WARN: Duration = INTEGRATION_WARN; + pub(crate) const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; // Do not suppose anything about unknown tests, base limits on the // `TEST_WARN_TIMEOUT_S` constant. - pub const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); - pub const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); + pub(crate) const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); + pub(crate) const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); } /// Returns an `Instance` object denoting when the test should be considered /// timed out. -pub fn get_default_test_timeout() -> Instant { +pub(crate) fn get_default_test_timeout() -> Instant { Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S) } @@ -73,7 +73,7 @@ impl fmt::Display for TestExecTime { /// The measured execution time of the whole test suite. #[derive(Debug, Clone, Default, PartialEq)] -pub struct TestSuiteExecTime(pub Duration); +pub(crate) struct TestSuiteExecTime(pub Duration); impl fmt::Display for TestSuiteExecTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index e4ba2bc1ed8..2650b273a4d 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -56,15 +56,15 @@ cfg_if::cfg_if! { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); } else if #[cfg(feature = "llvm-libunwind")] { #[link(name = "unwind", kind = "static", modifiers = "-bundle")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(feature = "system-llvm-libunwind")] { #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } else { #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } } @@ -76,11 +76,11 @@ cfg_if::cfg_if! { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); } else if #[cfg(feature = "llvm-libunwind")] { #[link(name = "unwind", kind = "static", modifiers = "-bundle")] - extern "C" {} + unsafe extern "C" {} } else { #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } } @@ -91,14 +91,14 @@ cfg_if::cfg_if! { } else { #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } } // Android's unwinding library depends on dl_iterate_phdr in `libdl`. #[cfg(target_os = "android")] #[link(name = "dl", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "dl", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} // When building with crt-static, we get `gcc_eh` from the `libc` crate, since // glibc needs it, and needs it listed later on the linker command line. We @@ -110,7 +110,7 @@ extern "C" {} not(feature = "system-llvm-libunwind") ))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} #[cfg(all( target_os = "linux", @@ -119,67 +119,67 @@ extern "C" {} feature = "system-llvm-libunwind" ))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "redox")] #[link(name = "gcc_eh", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] #[link(name = "unwind", kind = "static", modifiers = "-bundle")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "netbsd")] #[link(name = "gcc_s")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "freebsd")] #[link(name = "gcc", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_eh", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} #[cfg(all(target_os = "openbsd", target_arch = "sparc64"))] #[link(name = "gcc")] -extern "C" {} +unsafe extern "C" {} #[cfg(all(target_os = "openbsd", not(target_arch = "sparc64")))] #[link(name = "c++abi")] -extern "C" {} +unsafe extern "C" {} #[cfg(any(target_os = "solaris", target_os = "illumos"))] #[link(name = "gcc_s")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "dragonfly")] #[link(name = "gcc_pic")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "haiku")] #[link(name = "gcc_s")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "aix")] #[link(name = "unwind")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "nto")] cfg_if::cfg_if! { if #[cfg(target_env = "nto70")] { #[link(name = "gcc")] - extern "C" {} + unsafe extern "C" {} } else { #[link(name = "gcc_s")] - extern "C" {} + unsafe extern "C" {} } } #[cfg(target_os = "hurd")] #[link(name = "gcc_s")] -extern "C" {} +unsafe extern "C" {} #[cfg(all(target_os = "windows", target_env = "gnu", target_abi = "llvm"))] #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 1fa9e480166..62165f8a200 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -108,10 +108,10 @@ pub type _Unwind_Exception_Cleanup_Fn = ), link(name = "unwind", kind = "static", modifiers = "-bundle") )] -extern "C-unwind" { +unsafe extern "C-unwind" { pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; } -extern "C" { +unsafe extern "C" { pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void; pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr; @@ -140,7 +140,7 @@ if #[cfg(any(target_vendor = "apple", target_os = "netbsd", not(target_arch = "a all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")), link(name = "unwind", kind = "static", modifiers = "-bundle") )] - extern "C" { + unsafe extern "C" { pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word; pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word); pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> _Unwind_Word; @@ -198,7 +198,7 @@ if #[cfg(any(target_vendor = "apple", target_os = "netbsd", not(target_arch = "a all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")), link(name = "unwind", kind = "static", modifiers = "-bundle") )] - extern "C" { + unsafe extern "C" { fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, regclass: _Unwind_VRS_RegClass, regno: _Unwind_Word, @@ -261,7 +261,7 @@ cfg_if::cfg_if! { if #[cfg(all(target_vendor = "apple", not(target_os = "watchos"), target_arch = "arm"))] { // 32-bit ARM Apple (except for watchOS armv7k specifically) uses SjLj and // does not provide _Unwind_Backtrace() - extern "C-unwind" { + unsafe extern "C-unwind" { pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code; } @@ -271,14 +271,14 @@ if #[cfg(all(target_vendor = "apple", not(target_os = "watchos"), target_arch = all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")), link(name = "unwind", kind = "static", modifiers = "-bundle") )] - extern "C-unwind" { + unsafe extern "C-unwind" { pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code; } #[cfg_attr( all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")), link(name = "unwind", kind = "static", modifiers = "-bundle") )] - extern "C" { + unsafe extern "C" { pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, trace_argument: *mut c_void) -> _Unwind_Reason_Code; @@ -302,7 +302,7 @@ if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), targ context: *mut _Unwind_Context) -> _Unwind_Reason_Code; - extern "C" { + unsafe extern "C" { pub fn _GCC_specific_handler(exceptionRecord: *mut EXCEPTION_RECORD, establisherFrame: LPVOID, contextRecord: *mut CONTEXT, diff --git a/library/windows_targets/src/lib.rs b/library/windows_targets/src/lib.rs index 395cd6a4bab..e89bde8b1ab 100644 --- a/library/windows_targets/src/lib.rs +++ b/library/windows_targets/src/lib.rs @@ -39,4 +39,4 @@ pub macro link { #[link(name = "userenv")] #[link(name = "ws2_32")] #[link(name = "dbghelp")] // required for backtrace-rs symbolization -extern "C" {} +unsafe extern "C" {} |
