diff --git a/Cargo.lock b/Cargo.lock index 8b78908e6d730..55851daaf2a80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,21 +4,21 @@ version = 4 [[package]] name = "addr2line" -version = "0.24.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "compiler_builtins", - "gimli", + "gimli 0.29.0", "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] [[package]] -name = "adler2" -version = "2.0.0" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -36,9 +36,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.21" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "cc" @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.145" +version = "0.1.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0705f5abaaab7168ccc14f8f340ded61be2bd3ebea86b9834b6acbc8495de8" +checksum = "53f0ea7fff95b51f84371588f06062557e96bbe363d2b36218ddb806f3ca8611" dependencies = [ "cc", "rustc-std-workspace-core", @@ -72,10 +72,6 @@ dependencies = [ [[package]] name = "core" version = "0.0.0" - -[[package]] -name = "coretests" -version = "0.0.0" dependencies = [ "rand", "rand_xorshift", @@ -115,6 +111,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + [[package]] name = "gimli" version = "0.31.1" @@ -128,9 +135,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ "allocator-api2", "compiler_builtins", @@ -151,9 +158,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" dependencies = [ "rustc-std-workspace-core", ] @@ -170,11 +177,11 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ - "adler2", + "adler", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -182,9 +189,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.7" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "compiler_builtins", "memchr", @@ -228,6 +235,8 @@ name = "profiler_builtins" version = "0.0.0" dependencies = [ "cc", + "compiler_builtins", + "core", ] [[package]] @@ -396,12 +405,12 @@ dependencies = [ [[package]] name = "unwinding" -version = "0.2.5" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f06a05848f650946acef3bf525fe96612226b61f74ae23ffa4e98bfbb8ab3c" +checksum = "637d511437df708cee34bdec7ba2f1548d256b7acf3ff20e0a1c559f9bf3a987" dependencies = [ "compiler_builtins", - "gimli", + "gimli 0.31.1", "rustc-std-workspace-core", ] diff --git a/Cargo.toml b/Cargo.toml index 1205f7c9ed6b5..e744cfe5e0f57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ resolver = "1" members = [ "std", "sysroot", - "coretests", ] exclude = [ @@ -33,7 +32,7 @@ codegen-units = 10000 [profile.release.package] addr2line.debug = 0 addr2line.opt-level = "s" -adler2.debug = 0 +adler.debug = 0 gimli.debug = 0 gimli.opt-level = "s" miniz_oxide.debug = 0 diff --git a/alloc/Cargo.toml b/alloc/Cargo.toml index db7eaf52fb227..3464047d4ee9e 100644 --- a/alloc/Cargo.toml +++ b/alloc/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "=0.1.145", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/alloc/benches/btree/map.rs b/alloc/benches/btree/map.rs index 5b15aaeddbc40..b8119c9f0ebf4 100644 --- a/alloc/benches/btree/map.rs +++ b/alloc/benches/btree/map.rs @@ -353,7 +353,6 @@ 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/alloc/benches/lib.rs b/alloc/benches/lib.rs index 2633154318c13..c1907361f93e1 100644 --- a/alloc/benches/lib.rs +++ b/alloc/benches/lib.rs @@ -4,7 +4,8 @@ #![feature(iter_next_chunk)] #![feature(repr_simd)] #![feature(slice_partition_dedup)] -#![feature(strict_provenance_lints)] +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(test)] #![deny(fuzzy_provenance_casts)] diff --git a/alloc/benches/slice.rs b/alloc/benches/slice.rs index c45c372271297..48c74c4491dc8 100644 --- a/alloc/benches/slice.rs +++ b/alloc/benches/slice.rs @@ -366,25 +366,14 @@ 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/alloc/benches/vec.rs b/alloc/benches/vec.rs index a725ad6894b9c..d29ffae9d70b1 100644 --- a/alloc/benches/vec.rs +++ b/alloc/benches/vec.rs @@ -547,11 +547,6 @@ 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/alloc/src/alloc.rs b/alloc/src/alloc.rs index e9b7f9856677c..04b7315e650a2 100644 --- a/alloc/src/alloc.rs +++ b/alloc/src/alloc.rs @@ -10,6 +10,9 @@ use core::hint; #[cfg(not(test))] use core::ptr::{self, NonNull}; +#[cfg(test)] +mod tests; + 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 @@ -339,7 +342,7 @@ unsafe impl Allocator for Global { } } -/// The allocator for `Box`. +/// The allocator for unique pointers. #[cfg(all(not(no_global_oom_handling), not(test)))] #[lang = "exchange_malloc"] #[inline] diff --git a/alloc/tests/alloc.rs b/alloc/src/alloc/tests.rs similarity index 93% rename from alloc/tests/alloc.rs rename to alloc/src/alloc/tests.rs index 1e722d667955c..5d6077f057a2c 100644 --- a/alloc/tests/alloc.rs +++ b/alloc/src/alloc/tests.rs @@ -1,9 +1,10 @@ -use alloc::alloc::*; -use alloc::boxed::Box; +use super::*; extern crate test; use test::Bencher; +use crate::boxed::Box; + #[test] fn allocate_zeroed() { unsafe { diff --git a/alloc/src/boxed.rs b/alloc/src/boxed.rs index 8b38e6fc259af..ee60ec0fbacbe 100644 --- a/alloc/src/boxed.rs +++ b/alloc/src/boxed.rs @@ -191,7 +191,9 @@ use core::error::{self, Error}; use core::fmt; use core::future::Future; use core::hash::{Hash, Hasher}; -use core::marker::{PointerLike, Tuple, Unsize}; +#[cfg(not(bootstrap))] +use core::marker::PointerLike; +use core::marker::{Tuple, Unsize}; use core::mem::{self, SizedTypeProperties}; use core::ops::{ AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, @@ -225,7 +227,7 @@ pub use thin::ThinBox; #[fundamental] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] -#[doc(search_unbox)] +#[cfg_attr(not(bootstrap), doc(search_unbox))] // The declaration of the `Box` struct must be kept in sync with the // compiler or ICEs will happen. pub struct Box< @@ -233,27 +235,6 @@ pub struct Box< #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, >(Unique, A); -/// Constructs a `Box` by calling the `exchange_malloc` lang item and moving the argument into -/// the newly allocated memory. This is an intrinsic to avoid unnecessary copies. -/// -/// This is the surface syntax for `box ` expressions. -#[cfg(not(bootstrap))] -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[unstable(feature = "liballoc_internals", issue = "none")] -pub fn box_new(_x: T) -> Box { - unreachable!() -} - -/// Transition function for the next bootstrap bump. -#[cfg(bootstrap)] -#[unstable(feature = "liballoc_internals", issue = "none")] -#[inline(always)] -pub fn box_new(x: T) -> Box { - #[rustc_box] - Box::new(x) -} - impl Box { /// Allocates memory on the heap and then places `x` into it. /// @@ -271,7 +252,8 @@ impl Box { #[rustc_diagnostic_item = "box_new"] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn new(x: T) -> Self { - return box_new(x); + #[rustc_box] + Box::new(x) } /// Constructs a new box with uninitialized contents. @@ -781,26 +763,6 @@ impl Box<[T]> { }; unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, Global).into_box(len)) } } - - /// Converts the boxed slice into a boxed array. - /// - /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. - /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[unstable(feature = "slice_as_array", issue = "133508")] - #[inline] - #[must_use] - pub fn into_array(self) -> Option> { - if self.len() == N { - let ptr = Self::into_raw(self) as *mut [T; N]; - - // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. - let me = unsafe { Box::from_raw(ptr) }; - Some(me) - } else { - None - } - } } impl Box<[T], A> { @@ -1065,8 +1027,6 @@ impl Box { /// 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 the global allocator. - /// /// The safety conditions are described in the [memory layout] section. /// /// # Examples @@ -1115,8 +1075,6 @@ impl Box { /// 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 @@ -1172,7 +1130,6 @@ impl Box { /// 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`. /// /// # Examples /// @@ -1227,7 +1184,6 @@ impl Box { /// 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 /// @@ -1546,7 +1502,7 @@ impl Box { /// [`as_ptr`]: Self::as_ptr #[unstable(feature = "box_as_ptr", issue = "129090")] #[rustc_never_returns_null_ptr] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[inline] pub fn as_mut_ptr(b: &mut Self) -> *mut T { // This is a primitive deref, not going through `DerefMut`, and therefore not materializing @@ -1595,7 +1551,7 @@ impl Box { /// [`as_ptr`]: Self::as_ptr #[unstable(feature = "box_as_ptr", issue = "129090")] #[rustc_never_returns_null_ptr] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[inline] pub fn as_ptr(b: &Self) -> *const T { // This is a primitive deref, not going through `DerefMut`, and therefore not materializing @@ -2031,7 +1987,7 @@ impl + ?Sized, A: Allocator> Fn for Box { } } -#[stable(feature = "async_closure", since = "1.85.0")] +#[unstable(feature = "async_fn_traits", issue = "none")] impl + ?Sized, A: Allocator> AsyncFnOnce for Box { type Output = F::Output; type CallOnceFuture = F::CallOnceFuture; @@ -2041,7 +1997,7 @@ impl + ?Sized, A: Allocator> AsyncFnOnce } } -#[stable(feature = "async_closure", since = "1.85.0")] +#[unstable(feature = "async_fn_traits", issue = "none")] impl + ?Sized, A: Allocator> AsyncFnMut for Box { type CallRefFuture<'a> = F::CallRefFuture<'a> @@ -2053,7 +2009,7 @@ impl + ?Sized, A: Allocator> AsyncFnMut f } } -#[stable(feature = "async_closure", since = "1.85.0")] +#[unstable(feature = "async_fn_traits", issue = "none")] impl + ?Sized, A: Allocator> AsyncFn for Box { extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_> { F::async_call(self, args) @@ -2178,5 +2134,6 @@ impl Error for Box { } } +#[cfg(not(bootstrap))] #[unstable(feature = "pointer_like_trait", issue = "none")] impl PointerLike for Box {} diff --git a/alloc/src/boxed/convert.rs b/alloc/src/boxed/convert.rs index 255cefb1e78fb..4430fff66775c 100644 --- a/alloc/src/boxed/convert.rs +++ b/alloc/src/boxed/convert.rs @@ -110,7 +110,7 @@ impl From<&[T]> for Box<[T]> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "box_from_mut_slice", since = "1.84.0")] +#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut [T]> for Box<[T]> { /// Converts a `&mut [T]` into a `Box<[T]>` /// @@ -171,7 +171,7 @@ impl From<&str> for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "box_from_mut_slice", since = "1.84.0")] +#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut str> for Box { /// Converts a `&mut str` into a `Box` /// diff --git a/alloc/src/bstr.rs b/alloc/src/bstr.rs deleted file mode 100644 index 61e61019b508c..0000000000000 --- a/alloc/src/bstr.rs +++ /dev/null @@ -1,702 +0,0 @@ -//! 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` 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`, so all methods available on `&Vec` are -/// available on `ByteString`. Similarly, `ByteString` implements `DerefMut` to `&mut Vec`, -/// so you can modify a `ByteString` using any method available on `&mut Vec`. -/// -/// 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); - -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; - - #[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 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 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 for ByteString { - #[inline] - fn borrow(&self) -> &ByteStr { - self.as_bytestr() - } -} - -// `impl Borrow for Vec` omitted to avoid inference failures -// `impl Borrow 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 for ByteString { - #[inline] - fn borrow_mut(&mut self) -> &mut ByteStr { - self.as_mut_bytestr() - } -} - -// `impl BorrowMut for Vec` 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 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> for ByteString { -// #[inline] -// fn from(s: Vec) -> Self { -// ByteString(s) -// } -// } - -#[unstable(feature = "bstr", issue = "134915")] -impl From for Vec { - #[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 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 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 for ByteString { - #[inline] - fn from_iter>(iter: T) -> Self { - ByteString(iter.into_iter().collect::().into_bytes()) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl FromIterator for ByteString { - #[inline] - fn from_iter>(iter: T) -> Self { - ByteString(iter.into_iter().collect()) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl<'a> FromIterator<&'a str> for ByteString { - #[inline] - fn from_iter>(iter: T) -> Self { - ByteString(iter.into_iter().collect::().into_bytes()) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl<'a> FromIterator<&'a [u8]> for ByteString { - #[inline] - fn from_iter>(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>(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 for ByteString { - #[inline] - fn from_iter>(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 { - Ok(ByteString(s.as_bytes().to_vec())) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index for ByteString { - type Output = u8; - - #[inline] - fn index(&self, idx: usize) -> &u8 { - &self.0[idx] - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index for ByteString { - type Output = ByteStr; - - #[inline] - fn index(&self, _: RangeFull) -> &ByteStr { - self.as_bytestr() - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteString { - type Output = ByteStr; - - #[inline] - fn index(&self, r: Range) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteString { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeInclusive) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteString { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeFrom) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteString { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeTo) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteString { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeToInclusive) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut for ByteString { - #[inline] - fn index_mut(&mut self, idx: usize) -> &mut u8 { - &mut self.0[idx] - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut for ByteString { - #[inline] - fn index_mut(&mut self, _: RangeFull) -> &mut ByteStr { - self.as_mut_bytestr() - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteString { - #[inline] - fn index_mut(&mut self, r: Range) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteString { - #[inline] - fn index_mut(&mut self, r: RangeInclusive) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteString { - #[inline] - fn index_mut(&mut self, r: RangeFrom) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteString { - #[inline] - fn index_mut(&mut self, r: RangeTo) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteString { - #[inline] - fn index_mut(&mut self, r: RangeToInclusive) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl hash::Hash for ByteString { - #[inline] - fn hash(&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 { - 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 { - let this: &[u8] = (&**self).as_ref(); - PartialOrd::partial_cmp(this, other.as_bytes()) - } - } - }; -} - -// PartialOrd with `Vec` omitted to avoid inference failures -impl_partial_eq!(ByteString, Vec); -// 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 { - 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 for String { - type Error = crate::string::FromUtf8Error; - - #[inline] - fn try_from(s: ByteString) -> Result { - 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 { - 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 { - #[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> for Box { - #[inline] - fn from(s: Box<[u8]>) -> Box { - // 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> for Box<[u8]> { - #[inline] - fn from(s: Box) -> 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> for Rc { - #[inline] - fn from(s: Rc<[u8]>) -> Rc { - // 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> for Rc<[u8]> { - #[inline] - fn from(s: Rc) -> 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> for Arc { - #[inline] - fn from(s: Arc<[u8]>) -> Arc { - // 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> for Arc<[u8]> { - #[inline] - fn from(s: Arc) -> Arc<[u8]> { - // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. - unsafe { Arc::from_raw(Arc::into_raw(s) as _) } - } -} - -// PartialOrd with `Vec` omitted to avoid inference failures -impl_partial_eq!(ByteStr, Vec); -// 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 { - Ok(core::str::from_utf8(&s.0)?.into()) - } -} diff --git a/alloc/src/collections/binary_heap/mod.rs b/alloc/src/collections/binary_heap/mod.rs index 965fd63a52981..59f10b09c73fd 100644 --- a/alloc/src/collections/binary_heap/mod.rs +++ b/alloc/src/collections/binary_heap/mod.rs @@ -155,6 +155,9 @@ use crate::collections::TryReserveError; use crate::slice; use crate::vec::{self, AsVecIntoIter, Vec}; +#[cfg(test)] +mod tests; + /// A priority queue implemented with a binary heap. /// /// This will be a max-heap. @@ -449,7 +452,7 @@ impl BinaryHeap { /// /// The binary heap will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the binary heap will not allocate. + /// `capacity`. If `capacity` is 0, the binary heap will not allocate. /// /// # Examples /// @@ -483,6 +486,7 @@ impl BinaryHeap { /// heap.push(4); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] + #[rustc_const_unstable(feature = "const_binary_heap_new_in", issue = "125961")] #[must_use] pub const fn new_in(alloc: A) -> BinaryHeap { BinaryHeap { data: Vec::new_in(alloc) } @@ -492,7 +496,7 @@ impl BinaryHeap { /// /// The binary heap will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the binary heap will not allocate. + /// `capacity`. If `capacity` is 0, the binary heap will not allocate. /// /// # Examples /// @@ -531,7 +535,8 @@ impl BinaryHeap { /// heap.push(1); /// heap.push(5); /// heap.push(2); - /// if let Some(mut val) = heap.peek_mut() { + /// { + /// let mut val = heap.peek_mut().unwrap(); /// *val = 0; /// } /// assert_eq!(heap.peek(), Some(&2)); diff --git a/alloc/tests/collections/binary_heap.rs b/alloc/src/collections/binary_heap/tests.rs similarity index 98% rename from alloc/tests/collections/binary_heap.rs rename to alloc/src/collections/binary_heap/tests.rs index 95f4c3e614f5e..ad0a020a1a961 100644 --- a/alloc/tests/collections/binary_heap.rs +++ b/alloc/src/collections/binary_heap/tests.rs @@ -1,9 +1,7 @@ -use alloc::boxed::Box; -use alloc::collections::binary_heap::*; -use std::iter::TrustedLen; -use std::mem; use std::panic::{AssertUnwindSafe, catch_unwind}; +use super::*; +use crate::boxed::Box; use crate::testing::crash_test::{CrashTestDummy, Panic}; #[test] @@ -502,7 +500,9 @@ 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; @@ -531,7 +531,7 @@ fn panic_safe() { self.0.partial_cmp(&other.0) } } - let mut rng = crate::test_rng(); + let mut rng = crate::test_helpers::test_rng(); const DATASZ: usize = 32; // Miri is too slow let ntest = if cfg!(miri) { 1 } else { 10 }; diff --git a/alloc/src/collections/btree/append.rs b/alloc/src/collections/btree/append.rs index 091376d5d685b..d137d2721ee4f 100644 --- a/alloc/src/collections/btree/append.rs +++ b/alloc/src/collections/btree/append.rs @@ -16,7 +16,7 @@ impl Root { /// 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(super) fn append_from_sorted_iters( + pub fn append_from_sorted_iters( &mut self, left: I, right: I, @@ -36,12 +36,8 @@ impl Root { /// 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(super) fn bulk_push( - &mut self, - iter: I, - length: &mut usize, - alloc: A, - ) where + pub fn bulk_push(&mut self, iter: I, length: &mut usize, alloc: A) + where I: Iterator, { let mut cur_node = self.borrow_mut().last_leaf_edge().into_node(); diff --git a/alloc/src/collections/btree/borrow.rs b/alloc/src/collections/btree/borrow.rs index e848ac3f2d192..000b9bd0fab42 100644 --- a/alloc/src/collections/btree/borrow.rs +++ b/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(super) struct DormantMutRef<'a, T> { +pub struct DormantMutRef<'a, T> { ptr: NonNull, _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(super) fn new(t: &'a mut T) -> (&'a mut T, Self) { + pub 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(super) unsafe fn awaken(self) -> &'a mut T { + pub 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(super) unsafe fn reborrow(&mut self) -> &'a mut T { + pub 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(super) unsafe fn reborrow_shared(&self) -> &'a T { + pub 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/alloc/src/collections/btree/dedup_sorted_iter.rs b/alloc/src/collections/btree/dedup_sorted_iter.rs index 6bcf0bca519af..cd6a88f329125 100644 --- a/alloc/src/collections/btree/dedup_sorted_iter.rs +++ b/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(super) struct DedupSortedIter +pub struct DedupSortedIter where I: Iterator, { @@ -17,7 +17,7 @@ impl DedupSortedIter where I: Iterator, { - pub(super) fn new(iter: I) -> Self { + pub fn new(iter: I) -> Self { Self { iter: iter.peekable() } } } diff --git a/alloc/src/collections/btree/fix.rs b/alloc/src/collections/btree/fix.rs index b0c6759794691..09edea3555ad5 100644 --- a/alloc/src/collections/btree/fix.rs +++ b/alloc/src/collections/btree/fix.rs @@ -57,10 +57,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, 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(super) fn fix_node_and_affected_ancestors( - mut self, - alloc: A, - ) -> bool { + pub fn fix_node_and_affected_ancestors(mut self, alloc: A) -> bool { loop { match self.fix_node_through_parent(alloc.clone()) { Ok(Some(parent)) => self = parent.forget_type(), @@ -73,7 +70,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { impl Root { /// Removes empty levels on the top, but keeps an empty leaf if the entire tree is empty. - pub(super) fn fix_top(&mut self, alloc: A) { + pub fn fix_top(&mut self, alloc: A) { while self.height() > 0 && self.len() == 0 { self.pop_internal_level(alloc.clone()); } @@ -82,7 +79,7 @@ impl Root { /// 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(super) fn fix_right_border(&mut self, alloc: A) { + pub fn fix_right_border(&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()); @@ -91,7 +88,7 @@ impl Root { } /// The symmetric clone of `fix_right_border`. - pub(super) fn fix_left_border(&mut self, alloc: A) { + pub fn fix_left_border(&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()); @@ -102,7 +99,7 @@ impl Root { /// 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(super) fn fix_right_border_of_plentiful(&mut self) { + pub 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/alloc/src/collections/btree/map.rs b/alloc/src/collections/btree/map.rs index 6d305386dbfa0..213924d1d0203 100644 --- a/alloc/src/collections/btree/map.rs +++ b/alloc/src/collections/btree/map.rs @@ -308,38 +308,11 @@ impl BTreeMap { alloc: (*map.alloc).clone(), _marker: PhantomData, } - .insert(SetValZST); + .insert(SetValZST::default()); None } } } - - pub(super) fn get_or_insert_with(&mut self, q: &Q, f: F) -> &K - where - K: Borrow + Ord, - Q: Ord, - F: FnOnce(&Q) -> K, - { - let (map, dormant_map) = DormantMutRef::new(self); - let root_node = - map.root.get_or_insert_with(|| Root::new((*map.alloc).clone())).borrow_mut(); - match root_node.search_tree(q) { - Found(handle) => handle.into_kv_mut().0, - GoDown(handle) => { - let key = f(q); - assert!(*key.borrow() == *q, "new value is not equal"); - VacantEntry { - key, - handle: Some(handle), - dormant_map, - alloc: (*map.alloc).clone(), - _marker: PhantomData, - } - .insert_entry(SetValZST) - .into_key() - } - } - } } /// An iterator over the entries of a `BTreeMap`. @@ -2289,10 +2262,6 @@ impl FusedIterator for RangeMut<'_, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] impl FromIterator<(K, V)> for BTreeMap { - /// Constructs a `BTreeMap` from an iterator of key-value pairs. - /// - /// If the iterator produces any pairs with equal keys, - /// all but one of the corresponding values will be dropped. fn from_iter>(iter: T) -> BTreeMap { let mut inputs: Vec<_> = iter.into_iter().collect(); @@ -2407,10 +2376,7 @@ where #[stable(feature = "std_collections_from_array", since = "1.56.0")] impl From<[(K, V); N]> for BTreeMap { - /// Converts a `[(K, V); N]` into a `BTreeMap`. - /// - /// If any entries in the array have equal keys, - /// all but one of the corresponding values will be dropped. + /// Converts a `[(K, V); N]` into a `BTreeMap<(K, V)>`. /// /// ``` /// use std::collections::BTreeMap; diff --git a/alloc/src/collections/btree/map/entry.rs b/alloc/src/collections/btree/map/entry.rs index ea8fa363c3805..75bb86916a887 100644 --- a/alloc/src/collections/btree/map/entry.rs +++ b/alloc/src/collections/btree/map/entry.rs @@ -269,31 +269,6 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { Vacant(entry) => Vacant(entry), } } - - /// Sets the value of the entry, and returns an `OccupiedEntry`. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_entry_insert)] - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); - /// let entry = map.entry("poneyland").insert_entry("hoho".to_string()); - /// - /// assert_eq!(entry.key(), &"poneyland"); - /// ``` - #[inline] - #[unstable(feature = "btree_entry_insert", issue = "65225")] - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { - match self { - Occupied(mut entry) => { - entry.insert(value); - entry - } - Vacant(entry) => entry.insert_entry(value), - } - } } impl<'a, K: Ord, V: Default, A: Allocator + Clone> Entry<'a, K, V, A> { @@ -373,61 +348,41 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push", "put")] - pub fn insert(self, value: V) -> &'a mut V { - self.insert_entry(value).into_mut() - } - - /// Sets the value of the entry with the `VacantEntry`'s key, - /// and returns an `OccupiedEntry`. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_entry_insert)] - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, u32> = BTreeMap::new(); - /// - /// if let Entry::Vacant(o) = map.entry("poneyland") { - /// let entry = o.insert_entry(37); - /// assert_eq!(entry.get(), &37); - /// } - /// assert_eq!(map["poneyland"], 37); - /// ``` - #[unstable(feature = "btree_entry_insert", issue = "65225")] - pub fn insert_entry(mut self, value: V) -> OccupiedEntry<'a, K, V, A> { - let handle = match self.handle { + pub fn insert(mut self, value: V) -> &'a mut V { + let out_ptr = match self.handle { None => { // SAFETY: There is no tree yet so no reference to it exists. - let map = unsafe { self.dormant_map.reborrow() }; - let root = map.root.insert(NodeRef::new_leaf(self.alloc.clone()).forget_type()); - // SAFETY: We *just* created the root as a leaf, and we're - // stacking the new handle on the original borrow lifetime. - unsafe { - let mut leaf = root.borrow_mut().cast_to_leaf_unchecked(); - leaf.push_with_handle(self.key, value) - } + let map = unsafe { self.dormant_map.awaken() }; + let mut root = NodeRef::new_leaf(self.alloc.clone()); + let val_ptr = root.borrow_mut().push(self.key, value); + map.root = Some(root.forget_type()); + map.length = 1; + val_ptr + } + Some(handle) => { + let new_handle = + handle.insert_recursing(self.key, value, self.alloc.clone(), |ins| { + drop(ins.left); + // SAFETY: Pushing a new root node doesn't invalidate + // handles to existing nodes. + let map = unsafe { self.dormant_map.reborrow() }; + let root = map.root.as_mut().unwrap(); // same as ins.left + root.push_internal_level(self.alloc).push(ins.kv.0, ins.kv.1, ins.right) + }); + + // Get the pointer to the value + let val_ptr = new_handle.into_val_mut(); + + // SAFETY: We have consumed self.handle. + let map = unsafe { self.dormant_map.awaken() }; + map.length += 1; + val_ptr } - Some(handle) => handle.insert_recursing(self.key, value, self.alloc.clone(), |ins| { - drop(ins.left); - // SAFETY: Pushing a new root node doesn't invalidate - // handles to existing nodes. - let map = unsafe { self.dormant_map.reborrow() }; - let root = map.root.as_mut().unwrap(); // same as ins.left - root.push_internal_level(self.alloc.clone()).push(ins.kv.0, ins.kv.1, ins.right) - }), }; - // SAFETY: modifying the length doesn't invalidate handles to existing nodes. - unsafe { self.dormant_map.reborrow().length += 1 }; - - OccupiedEntry { - handle: handle.forget_node_type(), - dormant_map: self.dormant_map, - alloc: self.alloc, - _marker: PhantomData, - } + // Now that we have finished growing the tree using borrowed references, + // dereference the pointer to a part of it, that we picked up along the way. + unsafe { &mut *out_ptr } } } @@ -449,11 +404,6 @@ impl<'a, K: Ord, V, A: Allocator + Clone> OccupiedEntry<'a, K, V, A> { self.handle.reborrow().into_kv().0 } - /// Converts the entry into a reference to its key. - pub(crate) fn into_key(self) -> &'a K { - self.handle.into_kv_mut().0 - } - /// Take ownership of the key and value from the map. /// /// # Examples diff --git a/alloc/src/collections/btree/mem.rs b/alloc/src/collections/btree/mem.rs index 4643c4133d55d..d738c5c47b4cc 100644 --- a/alloc/src/collections/btree/mem.rs +++ b/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(super) fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { +pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { replace(v, |value| (change(value), ())) } @@ -15,7 +15,7 @@ pub(super) fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { /// /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] -pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { +pub fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { struct PanicGuard; impl Drop for PanicGuard { fn drop(&mut self) { diff --git a/alloc/src/collections/btree/merge_iter.rs b/alloc/src/collections/btree/merge_iter.rs index c5b93d30a1185..7f23d93b990f5 100644 --- a/alloc/src/collections/btree/merge_iter.rs +++ b/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(super) struct MergeIterInner { +pub struct MergeIterInner { a: I, b: I, peeked: Option>, @@ -40,7 +40,7 @@ where impl MergeIterInner { /// Creates a new core for an iterator merging a pair of sources. - pub(super) fn new(a: I, b: I) -> Self { + pub fn new(a: I, b: I) -> Self { MergeIterInner { a, b, peeked: None } } @@ -51,7 +51,7 @@ impl MergeIterInner { /// 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(super) fn nexts Ordering>( + pub fn nexts Ordering>( &mut self, cmp: Cmp, ) -> (Option, Option) @@ -85,7 +85,7 @@ impl MergeIterInner { } /// Returns a pair of upper bounds for the `size_hint` of the final iterator. - pub(super) fn lens(&self) -> (usize, usize) + pub fn lens(&self) -> (usize, usize) where I: ExactSizeIterator, { diff --git a/alloc/src/collections/btree/mod.rs b/alloc/src/collections/btree/mod.rs index 6651480667391..b8667d09c33b3 100644 --- a/alloc/src/collections/btree/mod.rs +++ b/alloc/src/collections/btree/mod.rs @@ -2,13 +2,13 @@ mod append; mod borrow; mod dedup_sorted_iter; mod fix; -pub(super) mod map; +pub mod map; mod mem; mod merge_iter; mod navigate; mod node; mod remove; mod search; -pub(super) mod set; +pub mod set; mod set_val; mod split; diff --git a/alloc/src/collections/btree/navigate.rs b/alloc/src/collections/btree/navigate.rs index b2a7de74875d9..14b7d4ad71f86 100644 --- a/alloc/src/collections/btree/navigate.rs +++ b/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(super) struct LeafRange { +pub struct LeafRange { front: Option, marker::Edge>>, back: Option, marker::Edge>>, } @@ -25,7 +25,7 @@ impl Default for LeafRange { } impl LeafRange { - pub(super) fn none() -> Self { + pub fn none() -> Self { LeafRange { front: None, back: None } } @@ -34,7 +34,7 @@ impl LeafRange { } /// Temporarily takes out another, immutable equivalent of the same range. - pub(super) fn reborrow(&self) -> LeafRange, K, V> { + pub fn reborrow(&self) -> LeafRange, 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 LeafRange { impl<'a, K, V> LeafRange, K, V> { #[inline] - pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_checked(|kv| kv.into_kv()) } #[inline] - pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub 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, K, V> { #[inline] - pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub 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(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub 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 LazyLeafHandle { } // `front` and `back` are always both `None` or both `Some`. -pub(super) struct LazyLeafRange { +pub struct LazyLeafRange { front: Option>, back: Option>, } @@ -142,12 +142,12 @@ impl<'a, K: 'a, V: 'a> Clone for LazyLeafRange, K, V> { } impl LazyLeafRange { - pub(super) fn none() -> Self { + pub fn none() -> Self { LazyLeafRange { front: None, back: None } } /// Temporarily takes out another, immutable equivalent of the same range. - pub(super) fn reborrow(&self) -> LazyLeafRange, K, V> { + pub fn reborrow(&self) -> LazyLeafRange, 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 LazyLeafRange { impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub 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 LazyLeafRange { } #[inline] - pub(super) unsafe fn deallocating_next_unchecked( + pub unsafe fn deallocating_next_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -200,7 +200,7 @@ impl LazyLeafRange { } #[inline] - pub(super) unsafe fn deallocating_next_back_unchecked( + pub unsafe fn deallocating_next_back_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -210,7 +210,7 @@ impl LazyLeafRange { } #[inline] - pub(super) fn deallocating_end(&mut self, alloc: A) { + pub fn deallocating_end(&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, K, V, marker::LeafOrInternal> /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub(super) fn range_search(self, range: R) -> LeafRange, K, V> + pub fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -324,7 +324,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Finds the pair of leaf edges delimiting an entire tree. - pub(super) fn full_range(self) -> LazyLeafRange, K, V> { + pub fn full_range(self) -> LazyLeafRange, K, V> { full_range(self, self) } } @@ -339,7 +339,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// /// # Safety /// Do not use the duplicate handles to visit the same KV twice. - pub(super) fn range_search(self, range: R) -> LeafRange, K, V> + pub fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -351,7 +351,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, 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(super) fn full_range(self) -> LazyLeafRange, K, V> { + pub fn full_range(self) -> LazyLeafRange, 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 NodeRef { /// 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(super) fn full_range(self) -> LazyLeafRange { + pub fn full_range(self) -> LazyLeafRange { // 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 /// 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(super) fn next_kv( + pub fn next_kv( self, ) -> Result< Handle, marker::KV>, @@ -398,7 +398,7 @@ impl /// 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(super) fn next_back_kv( + pub fn next_back_kv( self, ) -> Result< Handle, marker::KV>, @@ -627,9 +627,7 @@ impl NodeRef Handle, marker::Edge> { + pub fn first_leaf_edge(self) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -642,9 +640,7 @@ impl NodeRef Handle, marker::Edge> { + pub fn last_leaf_edge(self) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -655,7 +651,7 @@ impl NodeRef { +pub enum Position { Leaf(NodeRef), Internal(NodeRef), InternalKV, @@ -665,7 +661,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, 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(super) fn visit_nodes_in_order(self, mut visit: F) + pub fn visit_nodes_in_order(self, mut visit: F) where F: FnMut(Position, K, V>), { @@ -697,7 +693,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Calculates the number of elements in a (sub)tree. - pub(super) fn calc_length(self) -> usize { + pub fn calc_length(self) -> usize { let mut result = 0; self.visit_nodes_in_order(|pos| match pos { Position::Leaf(node) => result += node.len(), @@ -712,9 +708,7 @@ impl Handle, marker::KV> { /// Returns the leaf edge closest to a KV for forward navigation. - pub(super) fn next_leaf_edge( - self, - ) -> Handle, marker::Edge> { + pub fn next_leaf_edge(self) -> Handle, marker::Edge> { match self.force() { Leaf(leaf_kv) => leaf_kv.right_edge(), Internal(internal_kv) => { @@ -725,7 +719,7 @@ impl } /// Returns the leaf edge closest to a KV for backward navigation. - pub(super) fn next_back_leaf_edge( + pub fn next_back_leaf_edge( self, ) -> Handle, marker::Edge> { match self.force() { @@ -741,7 +735,7 @@ impl impl NodeRef { /// Returns the leaf edge corresponding to the first point at which the /// given bound is true. - pub(super) fn lower_bound( + pub fn lower_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> @@ -764,7 +758,7 @@ impl NodeRef( + pub fn upper_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> diff --git a/alloc/src/collections/btree/node.rs b/alloc/src/collections/btree/node.rs index 37f784a322cad..64a13bb6a0b3a 100644 --- a/alloc/src/collections/btree/node.rs +++ b/alloc/src/collections/btree/node.rs @@ -40,8 +40,8 @@ use crate::alloc::{Allocator, Layout}; use crate::boxed::Box; const B: usize = 6; -pub(super) const CAPACITY: usize = 2 * B - 1; -pub(super) const MIN_LEN_AFTER_SPLIT: usize = B - 1; +pub const CAPACITY: usize = 2 * B - 1; +pub 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 = NonNull>; /// 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(super) struct NodeRef { +pub struct NodeRef { /// 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(super) struct NodeRef { /// The root node of an owned tree. /// /// Note that this does not have a destructor, and must be cleaned up manually. -pub(super) type Root = NodeRef; +pub type Root = NodeRef; impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> {} impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { @@ -213,7 +213,7 @@ unsafe impl Send for NodeRef unsafe impl Send for NodeRef {} impl NodeRef { - pub(super) fn new_leaf(alloc: A) -> Self { + pub fn new_leaf(alloc: A) -> Self { Self::from_new_leaf(LeafNode::new(alloc)) } @@ -274,7 +274,7 @@ impl NodeRef { /// 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(super) fn len(&self) -> usize { + pub 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 NodeRef { /// 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(super) fn height(&self) -> usize { + pub fn height(&self) -> usize { self.height } /// Temporarily takes out another, immutable reference to the same node. - pub(super) fn reborrow(&self) -> NodeRef, K, V, Type> { + pub fn reborrow(&self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -315,7 +315,7 @@ impl NodeRef /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub(super) fn ascend( + pub fn ascend( self, ) -> Result, marker::Edge>, Self> { const { @@ -335,24 +335,24 @@ impl NodeRef .ok_or(self) } - pub(super) fn first_edge(self) -> Handle { + pub fn first_edge(self) -> Handle { unsafe { Handle::new_edge(self, 0) } } - pub(super) fn last_edge(self) -> Handle { + pub fn last_edge(self) -> Handle { let len = self.len(); unsafe { Handle::new_edge(self, len) } } /// Note that `self` must be nonempty. - pub(super) fn first_kv(self) -> Handle { + pub fn first_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, 0) } } /// Note that `self` must be nonempty. - pub(super) fn last_kv(self) -> Handle { + pub fn last_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, len - 1) } @@ -381,9 +381,11 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { } /// Borrows a view into the keys stored in the node. - pub(super) fn keys(&self) -> &[K] { + pub fn keys(&self) -> &[K] { let leaf = self.into_leaf(); - unsafe { leaf.keys.get_unchecked(..usize::from(leaf.len)).assume_init_ref() } + unsafe { + MaybeUninit::slice_assume_init_ref(leaf.keys.get_unchecked(..usize::from(leaf.len))) + } } } @@ -391,7 +393,7 @@ impl NodeRef { /// 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(super) unsafe fn deallocate_and_ascend( + pub unsafe fn deallocate_and_ascend( self, alloc: A, ) -> Option, marker::Edge>> { @@ -443,7 +445,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { /// Returns a dormant copy of this node with its lifetime erased which can /// be reawakened later. - pub(super) fn dormant(&self) -> NodeRef { + pub fn dormant(&self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -455,7 +457,7 @@ impl NodeRef { /// /// 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(super) unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { + pub unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -536,7 +538,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// Borrows exclusive access to the length of the node. - pub(super) fn len_mut(&mut self) -> &mut u16 { + pub fn len_mut(&mut self) -> &mut u16 { &mut self.as_leaf_mut().len } } @@ -578,14 +580,14 @@ impl NodeRef { impl NodeRef { /// Returns a new owned tree, with its own root node that is initially empty. - pub(super) fn new(alloc: A) -> Self { + pub fn new(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(super) fn push_internal_level( + pub fn push_internal_level( &mut self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -600,11 +602,11 @@ impl NodeRef { /// 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`. /// - /// Does not invalidate any handles or references pointing into the subtree - /// rooted at the first child of `self`. + /// 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. /// /// Panics if there is no internal level, i.e., if the root node is a leaf. - pub(super) fn pop_internal_level(&mut self, alloc: A) { + pub fn pop_internal_level(&mut self, alloc: A) { assert!(self.height > 0); let top = self.node; @@ -628,18 +630,18 @@ impl NodeRef { /// 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(super) fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { + pub fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Slightly mutably borrows the owned root node. - pub(super) fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { + pub fn borrow_valmut(&mut self) -> NodeRef, 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(super) fn into_dying(self) -> NodeRef { + pub fn into_dying(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -651,7 +653,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// # Safety /// /// The returned handle has an unbound lifetime. - pub(super) unsafe fn push_with_handle<'b>( + pub unsafe fn push_with_handle<'b>( &mut self, key: K, val: V, @@ -672,7 +674,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, 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(super) fn push(&mut self, key: K, val: V) -> *mut V { + pub 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 +683,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { impl<'a, K: 'a, V: 'a> NodeRef, 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(super) fn push(&mut self, key: K, val: V, edge: Root) { + pub fn push(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); let len = self.len_mut(); @@ -699,21 +701,21 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { impl NodeRef { /// Removes any static information asserting that this node is a `Leaf` node. - pub(super) fn forget_type(self) -> NodeRef { + pub fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Removes any static information asserting that this node is an `Internal` node. - pub(super) fn forget_type(self) -> NodeRef { + pub fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Checks whether a node is an `Internal` node or a `Leaf` node. - pub(super) fn force( + pub fn force( self, ) -> ForceResult< NodeRef, @@ -737,9 +739,7 @@ impl NodeRef { impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// Unsafely asserts to the compiler the static information that this node is a `Leaf`. - pub(super) unsafe fn cast_to_leaf_unchecked( - self, - ) -> NodeRef, K, V, marker::Leaf> { + unsafe fn cast_to_leaf_unchecked(self) -> NodeRef, K, V, marker::Leaf> { debug_assert!(self.height == 0); NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -759,7 +759,7 @@ impl<'a, K, V> NodeRef, 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(super) struct Handle { +pub struct Handle { node: Node, idx: usize, _marker: PhantomData, @@ -776,12 +776,12 @@ impl Clone for Handle { impl Handle { /// Retrieves the node that contains the edge or key-value pair this handle points to. - pub(super) fn into_node(self) -> Node { + pub fn into_node(self) -> Node { self.node } /// Returns the position of this handle in the node. - pub(super) fn idx(&self) -> usize { + pub fn idx(&self) -> usize { self.idx } } @@ -789,17 +789,17 @@ impl Handle { impl Handle, marker::KV> { /// Creates a new handle to a key-value pair in `node`. /// Unsafe because the caller must ensure that `idx < node.len()`. - pub(super) unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { + pub unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { debug_assert!(idx < node.len()); Handle { node, idx, _marker: PhantomData } } - pub(super) fn left_edge(self) -> Handle, marker::Edge> { + pub fn left_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx) } } - pub(super) fn right_edge(self) -> Handle, marker::Edge> { + pub fn right_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx + 1) } } } @@ -817,9 +817,7 @@ impl Handle, HandleType> { /// Temporarily takes out another immutable handle on the same location. - pub(super) fn reborrow( - &self, - ) -> Handle, K, V, NodeType>, HandleType> { + pub fn reborrow(&self) -> Handle, 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 } } @@ -831,7 +829,7 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// dangerous. /// /// For details, see `NodeRef::reborrow_mut`. - pub(super) unsafe fn reborrow_mut( + pub unsafe fn reborrow_mut( &mut self, ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type @@ -841,9 +839,7 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// Returns a dormant copy of this handle which can be reawakened later. /// /// See `DormantMutRef` for more details. - pub(super) fn dormant( - &self, - ) -> Handle, HandleType> { + pub fn dormant(&self) -> Handle, HandleType> { Handle { node: self.node.dormant(), idx: self.idx, _marker: PhantomData } } } @@ -855,9 +851,7 @@ impl Handle( - self, - ) -> Handle, K, V, NodeType>, HandleType> { + pub unsafe fn awaken<'a>(self) -> Handle, K, V, NodeType>, HandleType> { Handle { node: unsafe { self.node.awaken() }, idx: self.idx, _marker: PhantomData } } } @@ -865,15 +859,13 @@ impl Handle Handle, marker::Edge> { /// Creates a new handle to an edge in `node`. /// Unsafe because the caller must ensure that `idx <= node.len()`. - pub(super) unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { + pub unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { debug_assert!(idx <= node.len()); Handle { node, idx, _marker: PhantomData } } - pub(super) fn left_kv( - self, - ) -> Result, marker::KV>, Self> { + pub fn left_kv(self) -> Result, marker::KV>, Self> { if self.idx > 0 { Ok(unsafe { Handle::new_kv(self.node, self.idx - 1) }) } else { @@ -881,9 +873,7 @@ impl Handle, mar } } - pub(super) fn right_kv( - self, - ) -> Result, marker::KV>, Self> { + pub fn right_kv(self) -> Result, marker::KV>, Self> { if self.idx < self.node.len() { Ok(unsafe { Handle::new_kv(self.node, self.idx) }) } else { @@ -892,7 +882,7 @@ impl Handle, mar } } -pub(super) enum LeftOrRight { +pub enum LeftOrRight { Left(T), Right(T), } @@ -1046,7 +1036,7 @@ impl<'a, K: 'a, V: 'a> Handle, 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(super) fn insert_recursing( + pub fn insert_recursing( self, key: K, value: V, @@ -1090,7 +1080,7 @@ impl /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub(super) fn descend(self) -> NodeRef { + pub fn descend(self) -> NodeRef { const { assert!(BorrowType::TRAVERSAL_PERMIT); } @@ -1109,7 +1099,7 @@ impl } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub(super) fn into_kv(self) -> (&'a K, &'a V) { + pub 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() }; @@ -1119,17 +1109,17 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeTyp } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub(super) fn key_mut(&mut self) -> &mut K { + pub fn key_mut(&mut self) -> &mut K { unsafe { self.node.key_area_mut(self.idx).assume_init_mut() } } - pub(super) fn into_val_mut(self) -> &'a mut V { + pub 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(super) fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { + pub 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() }; @@ -1139,13 +1129,13 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub(super) fn into_kv_valmut(self) -> (&'a K, &'a mut V) { + pub 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, K, V, NodeType>, marker::KV> { - pub(super) fn kv_mut(&mut self) -> (&mut K, &mut V) { + pub 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. @@ -1158,7 +1148,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } /// Replaces the key and value that the KV handle refers to. - pub(super) fn replace_kv(&mut self, k: K, v: V) -> (K, V) { + pub 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)) } @@ -1168,7 +1158,7 @@ impl Handle, 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(super) unsafe fn into_key_val(mut self) -> (K, V) { + pub unsafe fn into_key_val(mut self) -> (K, V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); unsafe { @@ -1182,7 +1172,7 @@ impl Handle, marker::KV> /// # Safety /// The node that the handle refers to must not yet have been deallocated. #[inline] - pub(super) unsafe fn drop_key_val(mut self) { + pub 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); impl Drop for Dropper<'_, T> { @@ -1241,10 +1231,7 @@ impl<'a, K: 'a, V: 'a> Handle, 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(super) fn split( - mut self, - alloc: A, - ) -> SplitResult<'a, K, V, marker::Leaf> { + pub fn split(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); @@ -1255,7 +1242,7 @@ impl<'a, K: 'a, V: 'a> Handle, 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(super) fn remove( + pub fn remove( mut self, ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { let old_len = self.node.len(); @@ -1276,7 +1263,7 @@ impl<'a, K: 'a, V: 'a> Handle, 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(super) fn split( + pub fn split( mut self, alloc: A, ) -> SplitResult<'a, K, V, marker::Internal> { @@ -1300,14 +1287,14 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// Represents a session for evaluating and performing a balancing operation /// around an internal key-value pair. -pub(super) struct BalancingContext<'a, K, V> { +pub struct BalancingContext<'a, K, V> { parent: Handle, K, V, marker::Internal>, marker::KV>, left_child: NodeRef, K, V, marker::LeafOrInternal>, right_child: NodeRef, K, V, marker::LeafOrInternal>, } impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { - pub(super) fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { + pub fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { let self1 = unsafe { ptr::read(&self) }; let self2 = unsafe { ptr::read(&self) }; BalancingContext { @@ -1333,7 +1320,7 @@ impl<'a, K, V> NodeRef, 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(super) fn choose_parent_kv(self) -> Result>, Self> { + pub fn choose_parent_kv(self) -> Result>, Self> { match unsafe { ptr::read(&self) }.ascend() { Ok(parent_edge) => match parent_edge.left_kv() { Ok(left_parent_kv) => Ok(LeftOrRight::Left(BalancingContext { @@ -1356,25 +1343,25 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { } impl<'a, K, V> BalancingContext<'a, K, V> { - pub(super) fn left_child_len(&self) -> usize { + pub fn left_child_len(&self) -> usize { self.left_child.len() } - pub(super) fn right_child_len(&self) -> usize { + pub fn right_child_len(&self) -> usize { self.right_child.len() } - pub(super) fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { self.left_child } - pub(super) fn into_right_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub fn into_right_child(self) -> NodeRef, 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(super) fn can_merge(&self) -> bool { + pub fn can_merge(&self) -> bool { self.left_child.len() + 1 + self.right_child.len() <= CAPACITY } } @@ -1448,7 +1435,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(super) fn merge_tracking_parent( + pub fn merge_tracking_parent( self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -1459,7 +1446,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(super) fn merge_tracking_child( + pub fn merge_tracking_child( self, alloc: A, ) -> NodeRef, K, V, marker::LeafOrInternal> { @@ -1471,7 +1458,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// where the tracked child edge ended up, /// /// Panics unless we `.can_merge()`. - pub(super) fn merge_tracking_child_edge( + pub fn merge_tracking_child_edge( self, track_edge_idx: LeftOrRight, alloc: A, @@ -1494,7 +1481,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(super) fn steal_left( + pub fn steal_left( mut self, track_right_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1506,7 +1493,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(super) fn steal_right( + pub fn steal_right( mut self, track_left_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1515,7 +1502,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(super) fn bulk_steal_left(&mut self, count: usize) { + pub fn bulk_steal_left(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1578,7 +1565,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// The symmetric clone of `bulk_steal_left`. - pub(super) fn bulk_steal_right(&mut self, count: usize) { + pub fn bulk_steal_right(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1643,7 +1630,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } impl Handle, marker::Edge> { - pub(super) fn forget_node_type( + pub fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1651,7 +1638,7 @@ impl Handle, marker::E } impl Handle, marker::Edge> { - pub(super) fn forget_node_type( + pub fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1659,7 +1646,7 @@ impl Handle, marke } impl Handle, marker::KV> { - pub(super) fn forget_node_type( + pub fn forget_node_type( self, ) -> Handle, marker::KV> { unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } @@ -1668,7 +1655,7 @@ impl Handle, marker::K impl Handle, Type> { /// Checks whether the underlying node is an `Internal` node or a `Leaf` node. - pub(super) fn force( + pub fn force( self, ) -> ForceResult< Handle, Type>, @@ -1687,7 +1674,7 @@ impl Handle Handle, K, V, marker::LeafOrInternal>, Type> { /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`. - pub(super) unsafe fn cast_to_leaf_unchecked( + pub unsafe fn cast_to_leaf_unchecked( self, ) -> Handle, K, V, marker::Leaf>, Type> { let node = unsafe { self.node.cast_to_leaf_unchecked() }; @@ -1698,7 +1685,7 @@ impl<'a, K, V, Type> Handle, K, V, marker::LeafOrInterna impl<'a, K, V> Handle, 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(super) fn move_suffix( + pub fn move_suffix( &mut self, right: &mut NodeRef, K, V, marker::LeafOrInternal>, ) { @@ -1741,13 +1728,13 @@ impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, ma } } -pub(super) enum ForceResult { +pub enum ForceResult { Leaf(Leaf), Internal(Internal), } /// Result of insertion, when a node needed to expand beyond its capacity. -pub(super) struct SplitResult<'a, K, V, NodeType> { +pub 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, K, V, NodeType>, // Some key and value that existed before and were split off, to be inserted elsewhere. @@ -1757,32 +1744,32 @@ pub(super) struct SplitResult<'a, K, V, NodeType> { } impl<'a, K, V> SplitResult<'a, K, V, marker::Leaf> { - pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub 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(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub 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(super) mod marker { +pub mod marker { use core::marker::PhantomData; - pub(crate) enum Leaf {} - pub(crate) enum Internal {} - pub(crate) enum LeafOrInternal {} + pub enum Leaf {} + pub enum Internal {} + pub enum LeafOrInternal {} - 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 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) trait BorrowType { + pub 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. @@ -1801,8 +1788,8 @@ pub(super) mod marker { impl<'a> BorrowType for ValMut<'a> {} impl BorrowType for DormantMut {} - pub(crate) enum KV {} - pub(crate) enum Edge {} + pub enum KV {} + pub enum Edge {} } /// Inserts a value into a slice of initialized elements followed by one uninitialized element. diff --git a/alloc/src/collections/btree/node/tests.rs b/alloc/src/collections/btree/node/tests.rs index ecd009f11c71a..4d2fa0f094171 100644 --- a/alloc/src/collections/btree/node/tests.rs +++ b/alloc/src/collections/btree/node/tests.rs @@ -6,7 +6,7 @@ use crate::string::String; impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { // Asserts that the back pointer in each reachable node points to its parent. - pub(crate) fn assert_back_pointers(self) { + pub 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, 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(crate) fn dump_keys(self) -> String + pub fn dump_keys(self) -> String where K: Debug, { diff --git a/alloc/src/collections/btree/remove.rs b/alloc/src/collections/btree/remove.rs index 9d870b86f34a0..56f2824b782bd 100644 --- a/alloc/src/collections/btree/remove.rs +++ b/alloc/src/collections/btree/remove.rs @@ -10,7 +10,7 @@ impl<'a, K: 'a, V: 'a> Handle, 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(super) fn remove_kv_tracking( + pub fn remove_kv_tracking( self, handle_emptied_internal_root: F, alloc: A, diff --git a/alloc/src/collections/btree/search.rs b/alloc/src/collections/btree/search.rs index 96e5bf108024b..22e015edac3d2 100644 --- a/alloc/src/collections/btree/search.rs +++ b/alloc/src/collections/btree/search.rs @@ -8,7 +8,7 @@ use SearchResult::*; use super::node::ForceResult::*; use super::node::{Handle, NodeRef, marker}; -pub(super) enum SearchBound { +pub enum SearchBound { /// 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(super) enum SearchBound { } impl SearchBound { - pub(super) fn from_range(range_bound: Bound) -> Self { + pub fn from_range(range_bound: Bound) -> Self { match range_bound { Bound::Included(t) => Included(t), Bound::Excluded(t) => Excluded(t), @@ -29,12 +29,12 @@ impl SearchBound { } } -pub(super) enum SearchResult { +pub enum SearchResult { Found(Handle, marker::KV>), GoDown(Handle, marker::Edge>), } -pub(super) enum IndexResult { +pub enum IndexResult { KV(usize), Edge(usize), } @@ -46,7 +46,7 @@ impl NodeRef( + pub fn search_tree( mut self, key: &Q, ) -> SearchResult @@ -80,7 +80,7 @@ impl NodeRef( + pub fn search_tree_for_bifurcation<'r, Q: ?Sized, R>( mut self, range: &'r R, ) -> Result< @@ -156,7 +156,7 @@ impl NodeRef( + pub fn find_lower_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -170,7 +170,7 @@ impl NodeRef( + pub fn find_upper_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -192,10 +192,7 @@ impl NodeRef { /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub(super) fn search_node( - self, - key: &Q, - ) -> SearchResult + pub fn search_node(self, key: &Q) -> SearchResult where Q: Ord, K: Borrow, diff --git a/alloc/src/collections/btree/set.rs b/alloc/src/collections/btree/set.rs index 041f80c1f2c52..8daee6030c270 100644 --- a/alloc/src/collections/btree/set.rs +++ b/alloc/src/collections/btree/set.rs @@ -7,17 +7,12 @@ use core::iter::{FusedIterator, Peekable}; use core::mem::ManuallyDrop; use core::ops::{BitAnd, BitOr, BitXor, Bound, RangeBounds, Sub}; -use super::map::{self, BTreeMap, Keys}; +use super::map::{BTreeMap, Keys}; use super::merge_iter::MergeIterInner; use super::set_val::SetValZST; use crate::alloc::{Allocator, Global}; use crate::vec::Vec; -mod entry; - -#[unstable(feature = "btree_set_entry", issue = "133549")] -pub use self::entry::{Entry, OccupiedEntry, VacantEntry}; - /// An ordered set based on a B-Tree. /// /// See [`BTreeMap`]'s documentation for a detailed discussion of this collection's performance @@ -933,109 +928,6 @@ impl BTreeSet { self.map.replace(value) } - /// Inserts the given `value` into the set if it is not present, then - /// returns a reference to the value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::from([1, 2, 3]); - /// assert_eq!(set.len(), 3); - /// assert_eq!(set.get_or_insert(2), &2); - /// assert_eq!(set.get_or_insert(100), &100); - /// assert_eq!(set.len(), 4); // 100 was inserted - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn get_or_insert(&mut self, value: T) -> &T - where - T: Ord, - { - self.map.entry(value).insert_entry(SetValZST).into_key() - } - - /// Inserts a value computed from `f` into the set if the given `value` is - /// not present, then returns a reference to the value in the set. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::BTreeSet; - /// - /// let mut set: BTreeSet = ["cat", "dog", "horse"] - /// .iter().map(|&pet| pet.to_owned()).collect(); - /// - /// assert_eq!(set.len(), 3); - /// for &pet in &["cat", "dog", "fish"] { - /// let value = set.get_or_insert_with(pet, str::to_owned); - /// assert_eq!(value, pet); - /// } - /// assert_eq!(set.len(), 4); // a new "fish" was inserted - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn get_or_insert_with(&mut self, value: &Q, f: F) -> &T - where - T: Borrow + Ord, - Q: Ord, - F: FnOnce(&Q) -> T, - { - self.map.get_or_insert_with(value, f) - } - - /// Gets the given value's corresponding entry in the set for in-place manipulation. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::BTreeSet; - /// use std::collections::btree_set::Entry::*; - /// - /// let mut singles = BTreeSet::new(); - /// let mut dupes = BTreeSet::new(); - /// - /// for ch in "a short treatise on fungi".chars() { - /// if let Vacant(dupe_entry) = dupes.entry(ch) { - /// // We haven't already seen a duplicate, so - /// // check if we've at least seen it once. - /// match singles.entry(ch) { - /// Vacant(single_entry) => { - /// // We found a new character for the first time. - /// single_entry.insert() - /// } - /// Occupied(single_entry) => { - /// // We've already seen this once, "move" it to dupes. - /// single_entry.remove(); - /// dupe_entry.insert(); - /// } - /// } - /// } - /// } - /// - /// assert!(!singles.contains(&'t') && dupes.contains(&'t')); - /// assert!(singles.contains(&'u') && !dupes.contains(&'u')); - /// assert!(!singles.contains(&'v') && !dupes.contains(&'v')); - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn entry(&mut self, value: T) -> Entry<'_, T, A> - where - T: Ord, - { - match self.map.entry(value) { - map::Entry::Occupied(entry) => Entry::Occupied(OccupiedEntry { inner: entry }), - map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { inner: entry }), - } - } - /// If the set contains an element equal to the value, removes it from the /// set and drops it. Returns whether such an element was present. /// @@ -1442,20 +1334,20 @@ impl BTreeSet { /// /// let mut set = BTreeSet::from([1, 2, 3, 4]); /// - /// let mut cursor = set.upper_bound_mut(Bound::Included(&3)); + /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Included(&3)) }; /// assert_eq!(cursor.peek_prev(), Some(&3)); /// assert_eq!(cursor.peek_next(), Some(&4)); /// - /// let mut cursor = set.upper_bound_mut(Bound::Excluded(&3)); + /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Excluded(&3)) }; /// assert_eq!(cursor.peek_prev(), Some(&2)); /// assert_eq!(cursor.peek_next(), Some(&3)); /// - /// let mut cursor = set.upper_bound_mut(Bound::Unbounded); + /// let mut cursor = unsafe { 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 fn upper_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> + pub unsafe fn upper_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> where T: Borrow + Ord, Q: Ord, @@ -1491,11 +1383,6 @@ impl BTreeSet { impl From<[T; N]> for BTreeSet { /// Converts a `[T; N]` into a `BTreeSet`. /// - /// If the array contains any equal values, - /// all but one will be dropped. - /// - /// # Examples - /// /// ``` /// use std::collections::BTreeSet; /// diff --git a/alloc/src/collections/btree/set/entry.rs b/alloc/src/collections/btree/set/entry.rs deleted file mode 100644 index a60d22f9ece71..0000000000000 --- a/alloc/src/collections/btree/set/entry.rs +++ /dev/null @@ -1,388 +0,0 @@ -use core::fmt::{self, Debug}; - -use Entry::*; - -use super::{SetValZST, map}; -use crate::alloc::{Allocator, Global}; - -/// A view into a single entry in a set, which may either be vacant or occupied. -/// -/// This `enum` is constructed from the [`entry`] method on [`BTreeSet`]. -/// -/// [`BTreeSet`]: super::BTreeSet -/// [`entry`]: super::BTreeSet::entry -/// -/// # Examples -/// -/// ``` -/// #![feature(btree_set_entry)] -/// -/// use std::collections::btree_set::BTreeSet; -/// -/// let mut set = BTreeSet::new(); -/// set.extend(["a", "b", "c"]); -/// assert_eq!(set.len(), 3); -/// -/// // Existing value (insert) -/// let entry = set.entry("a"); -/// let _raw_o = entry.insert(); -/// assert_eq!(set.len(), 3); -/// // Nonexistent value (insert) -/// set.entry("d").insert(); -/// -/// // Existing value (or_insert) -/// set.entry("b").or_insert(); -/// // Nonexistent value (or_insert) -/// set.entry("e").or_insert(); -/// -/// println!("Our BTreeSet: {:?}", set); -/// assert!(set.iter().eq(&["a", "b", "c", "d", "e"])); -/// ``` -#[unstable(feature = "btree_set_entry", issue = "133549")] -pub enum Entry< - 'a, - T, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, -> { - /// An occupied entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::btree_set::{Entry, BTreeSet}; - /// - /// let mut set = BTreeSet::from(["a", "b"]); - /// - /// match set.entry("a") { - /// Entry::Vacant(_) => unreachable!(), - /// Entry::Occupied(_) => { } - /// } - /// ``` - #[unstable(feature = "btree_set_entry", issue = "133549")] - Occupied(OccupiedEntry<'a, T, A>), - - /// A vacant entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::btree_set::{Entry, BTreeSet}; - /// - /// let mut set = BTreeSet::new(); - /// - /// match set.entry("a") { - /// Entry::Occupied(_) => unreachable!(), - /// Entry::Vacant(_) => { } - /// } - /// ``` - #[unstable(feature = "btree_set_entry", issue = "133549")] - Vacant(VacantEntry<'a, T, A>), -} - -#[unstable(feature = "btree_set_entry", issue = "133549")] -impl Debug for Entry<'_, T, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), - Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), - } - } -} - -/// A view into an occupied entry in a `BTreeSet`. -/// It is part of the [`Entry`] enum. -/// -/// # Examples -/// -/// ``` -/// #![feature(btree_set_entry)] -/// -/// use std::collections::btree_set::{Entry, BTreeSet}; -/// -/// let mut set = BTreeSet::new(); -/// set.extend(["a", "b", "c"]); -/// -/// let _entry_o = set.entry("a").insert(); -/// assert_eq!(set.len(), 3); -/// -/// // Existing key -/// match set.entry("a") { -/// Entry::Vacant(_) => unreachable!(), -/// Entry::Occupied(view) => { -/// assert_eq!(view.get(), &"a"); -/// } -/// } -/// -/// assert_eq!(set.len(), 3); -/// -/// // Existing key (take) -/// match set.entry("c") { -/// Entry::Vacant(_) => unreachable!(), -/// Entry::Occupied(view) => { -/// assert_eq!(view.remove(), "c"); -/// } -/// } -/// assert_eq!(set.get(&"c"), None); -/// assert_eq!(set.len(), 2); -/// ``` -#[unstable(feature = "btree_set_entry", issue = "133549")] -pub struct OccupiedEntry< - 'a, - T, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, -> { - pub(super) inner: map::OccupiedEntry<'a, T, SetValZST, A>, -} - -#[unstable(feature = "btree_set_entry", issue = "133549")] -impl Debug for OccupiedEntry<'_, T, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedEntry").field("value", self.get()).finish() - } -} - -/// A view into a vacant entry in a `BTreeSet`. -/// It is part of the [`Entry`] enum. -/// -/// # Examples -/// -/// ``` -/// #![feature(btree_set_entry)] -/// -/// use std::collections::btree_set::{Entry, BTreeSet}; -/// -/// let mut set = BTreeSet::<&str>::new(); -/// -/// let entry_v = match set.entry("a") { -/// Entry::Vacant(view) => view, -/// Entry::Occupied(_) => unreachable!(), -/// }; -/// entry_v.insert(); -/// assert!(set.contains("a") && set.len() == 1); -/// -/// // Nonexistent key (insert) -/// match set.entry("b") { -/// Entry::Vacant(view) => view.insert(), -/// Entry::Occupied(_) => unreachable!(), -/// } -/// assert!(set.contains("b") && set.len() == 2); -/// ``` -#[unstable(feature = "btree_set_entry", issue = "133549")] -pub struct VacantEntry< - 'a, - T, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, -> { - pub(super) inner: map::VacantEntry<'a, T, SetValZST, A>, -} - -#[unstable(feature = "btree_set_entry", issue = "133549")] -impl Debug for VacantEntry<'_, T, A> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("VacantEntry").field(self.get()).finish() - } -} - -impl<'a, T: Ord, A: Allocator + Clone> Entry<'a, T, A> { - /// Sets the value of the entry, and returns an `OccupiedEntry`. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// let entry = set.entry("horseyland").insert(); - /// - /// assert_eq!(entry.get(), &"horseyland"); - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn insert(self) -> OccupiedEntry<'a, T, A> { - match self { - Occupied(entry) => entry, - Vacant(entry) => entry.insert_entry(), - } - } - - /// Ensures a value is in the entry by inserting if it was vacant. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// - /// // nonexistent key - /// set.entry("poneyland").or_insert(); - /// assert!(set.contains("poneyland")); - /// - /// // existing key - /// set.entry("poneyland").or_insert(); - /// assert!(set.contains("poneyland")); - /// assert_eq!(set.len(), 1); - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn or_insert(self) { - if let Vacant(entry) = self { - entry.insert(); - } - } - - /// Returns a reference to this entry's value. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// set.entry("poneyland").or_insert(); - /// - /// // existing key - /// assert_eq!(set.entry("poneyland").get(), &"poneyland"); - /// // nonexistent key - /// assert_eq!(set.entry("horseland").get(), &"horseland"); - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn get(&self) -> &T { - match *self { - Occupied(ref entry) => entry.get(), - Vacant(ref entry) => entry.get(), - } - } -} - -impl<'a, T: Ord, A: Allocator + Clone> OccupiedEntry<'a, T, A> { - /// Gets a reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::btree_set::{Entry, BTreeSet}; - /// - /// let mut set = BTreeSet::new(); - /// set.entry("poneyland").or_insert(); - /// - /// match set.entry("poneyland") { - /// Entry::Vacant(_) => panic!(), - /// Entry::Occupied(entry) => assert_eq!(entry.get(), &"poneyland"), - /// } - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn get(&self) -> &T { - self.inner.key() - } - - /// Takes the value out of the entry, and returns it. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::BTreeSet; - /// use std::collections::btree_set::Entry; - /// - /// let mut set = BTreeSet::new(); - /// set.entry("poneyland").or_insert(); - /// - /// if let Entry::Occupied(o) = set.entry("poneyland") { - /// assert_eq!(o.remove(), "poneyland"); - /// } - /// - /// assert_eq!(set.contains("poneyland"), false); - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn remove(self) -> T { - self.inner.remove_entry().0 - } -} - -impl<'a, T: Ord, A: Allocator + Clone> VacantEntry<'a, T, A> { - /// Gets a reference to the value that would be used when inserting - /// through the `VacantEntry`. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// assert_eq!(set.entry("poneyland").get(), &"poneyland"); - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn get(&self) -> &T { - self.inner.key() - } - - /// Take ownership of the value. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::btree_set::{Entry, BTreeSet}; - /// - /// let mut set = BTreeSet::new(); - /// - /// match set.entry("poneyland") { - /// Entry::Occupied(_) => panic!(), - /// Entry::Vacant(v) => assert_eq!(v.into_value(), "poneyland"), - /// } - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn into_value(self) -> T { - self.inner.into_key() - } - - /// Sets the value of the entry with the VacantEntry's value. - /// - /// # Examples - /// - /// ``` - /// #![feature(btree_set_entry)] - /// - /// use std::collections::BTreeSet; - /// use std::collections::btree_set::Entry; - /// - /// let mut set = BTreeSet::new(); - /// - /// if let Entry::Vacant(o) = set.entry("poneyland") { - /// o.insert(); - /// } - /// assert!(set.contains("poneyland")); - /// ``` - #[inline] - #[unstable(feature = "btree_set_entry", issue = "133549")] - pub fn insert(self) { - self.inner.insert(SetValZST); - } - - #[inline] - fn insert_entry(self) -> OccupiedEntry<'a, T, A> { - OccupiedEntry { inner: self.inner.insert_entry(SetValZST) } - } -} diff --git a/alloc/src/collections/btree/set/tests.rs b/alloc/src/collections/btree/set/tests.rs index d538ef707eb93..990044e069f64 100644 --- a/alloc/src/collections/btree/set/tests.rs +++ b/alloc/src/collections/btree/set/tests.rs @@ -132,11 +132,9 @@ 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 @@ -252,11 +250,9 @@ 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/alloc/src/collections/btree/split.rs b/alloc/src/collections/btree/split.rs index 87a79e6cf3f93..c188ed1da6113 100644 --- a/alloc/src/collections/btree/split.rs +++ b/alloc/src/collections/btree/split.rs @@ -8,7 +8,7 @@ use super::search::SearchResult::*; impl Root { /// Calculates the length of both trees that result from splitting up /// a given number of distinct key-value pairs. - pub(super) fn calc_split_length( + pub fn calc_split_length( total_num: usize, root_a: &Root, root_b: &Root, @@ -31,11 +31,7 @@ impl Root { /// 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(super) fn split_off( - &mut self, - key: &Q, - alloc: A, - ) -> Self + pub fn split_off(&mut self, key: &Q, alloc: A) -> Self where K: Borrow, { diff --git a/alloc/src/collections/linked_list.rs b/alloc/src/collections/linked_list.rs index 13168b7a39fe4..ca0ea1ec8b2ba 100644 --- a/alloc/src/collections/linked_list.rs +++ b/alloc/src/collections/linked_list.rs @@ -1939,7 +1939,9 @@ pub struct ExtractIf< T: 'a, F: 'a, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { +> where + F: FnMut(&mut T) -> bool, +{ list: &'a mut LinkedList, it: Option>>, pred: F, @@ -1977,7 +1979,10 @@ where } #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] -impl fmt::Debug for ExtractIf<'_, T, F> { +impl fmt::Debug for ExtractIf<'_, T, F> +where + F: FnMut(&mut T) -> bool, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("ExtractIf").field(&self.list).finish() } diff --git a/alloc/src/collections/linked_list/tests.rs b/alloc/src/collections/linked_list/tests.rs index 812fe229a0fc5..b7d4f8512a0f2 100644 --- a/alloc/src/collections/linked_list/tests.rs +++ b/alloc/src/collections/linked_list/tests.rs @@ -58,7 +58,7 @@ fn list_from(v: &[T]) -> LinkedList { v.iter().cloned().collect() } -fn check_links(list: &LinkedList) { +pub fn check_links(list: &LinkedList) { unsafe { let mut len = 0; let mut last_ptr: Option<&Node> = None; @@ -696,10 +696,9 @@ fn test_cursor_mut_insert() { cursor.splice_after(p); cursor.splice_before(q); check_links(&m); - assert_eq!( - m.iter().cloned().collect::>(), - &[200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6] - ); + assert_eq!(m.iter().cloned().collect::>(), &[ + 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(); @@ -916,10 +915,9 @@ 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![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] - ); + assert_eq!(list.into_iter().collect::>(), vec![ + 1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39 + ]); } { @@ -934,10 +932,9 @@ 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![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] - ); + assert_eq!(list.into_iter().collect::>(), vec![ + 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39 + ]); } { @@ -952,10 +949,9 @@ 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![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35] - ); + assert_eq!(list.into_iter().collect::>(), vec![ + 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35 + ]); } { diff --git a/alloc/src/collections/vec_deque/mod.rs b/alloc/src/collections/vec_deque/mod.rs index 299c8b8679e3d..cf51a84bb6f24 100644 --- a/alloc/src/collections/vec_deque/mod.rs +++ b/alloc/src/collections/vec_deque/mod.rs @@ -823,7 +823,6 @@ impl VecDeque { /// 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"); @@ -1736,52 +1735,6 @@ impl VecDeque { } } - /// 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 = 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 { - 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 = 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 { - let first = self.back_mut()?; - if predicate(first) { self.pop_back() } else { None } - } - /// Prepends an element to the deque. /// /// # Examples @@ -1916,7 +1869,7 @@ impl VecDeque { /// /// # Panics /// - /// Panics if `index` is strictly greater than deque's length + /// Panics if `index` is greater than deque's length /// /// # Examples /// @@ -1931,9 +1884,6 @@ impl VecDeque { /// /// vec_deque.insert(1, 'd'); /// assert_eq!(vec_deque, &['a', 'd', 'b', 'c']); - /// - /// vec_deque.insert(4, 'e'); - /// assert_eq!(vec_deque, &['a', 'd', 'b', 'c', 'e']); /// ``` #[stable(feature = "deque_extras_15", since = "1.5.0")] #[track_caller] @@ -1978,13 +1928,13 @@ impl VecDeque { /// use std::collections::VecDeque; /// /// let mut buf = VecDeque::new(); - /// buf.push_back('a'); - /// buf.push_back('b'); - /// buf.push_back('c'); - /// assert_eq!(buf, ['a', 'b', 'c']); + /// buf.push_back(1); + /// buf.push_back(2); + /// buf.push_back(3); + /// assert_eq!(buf, [1, 2, 3]); /// - /// assert_eq!(buf.remove(1), Some('b')); - /// assert_eq!(buf, ['a', 'c']); + /// assert_eq!(buf.remove(1), Some(2)); + /// assert_eq!(buf, [1, 3]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("delete", "take")] @@ -2032,10 +1982,10 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let mut buf: VecDeque<_> = ['a', 'b', 'c'].into(); + /// let mut buf: VecDeque<_> = [1, 2, 3].into(); /// let buf2 = buf.split_off(1); - /// assert_eq!(buf, ['a']); - /// assert_eq!(buf2, ['b', 'c']); + /// assert_eq!(buf, [1]); + /// assert_eq!(buf2, [2, 3]); /// ``` #[inline] #[must_use = "use `.truncate()` if you don't need the other half"] diff --git a/alloc/src/collections/vec_deque/tests.rs b/alloc/src/collections/vec_deque/tests.rs index c90679f179775..6328b3b4db867 100644 --- a/alloc/src/collections/vec_deque/tests.rs +++ b/alloc/src/collections/vec_deque/tests.rs @@ -562,10 +562,9 @@ 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/alloc/src/ffi/c_str.rs b/alloc/src/ffi/c_str.rs index 07c75677d0532..d91682b796e4f 100644 --- a/alloc/src/ffi/c_str.rs +++ b/alloc/src/ffi/c_str.rs @@ -1,5 +1,8 @@ //! [`CString`] and its related types. +#[cfg(test)] +mod tests; + use core::borrow::Borrow; use core::ffi::{CStr, c_char}; use core::num::NonZero; @@ -381,7 +384,7 @@ impl CString { /// fn some_extern_function(s: *mut c_char); /// } /// - /// let c_string = CString::from(c"Hello!"); + /// let c_string = CString::new("Hello!").expect("CString::new failed"); /// let raw = c_string.into_raw(); /// unsafe { /// some_extern_function(raw); @@ -426,7 +429,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::from(c"foo"); + /// let c_string = CString::new("foo").expect("CString::new failed"); /// /// let ptr = c_string.into_raw(); /// @@ -484,7 +487,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::from(c"foo"); + /// let c_string = CString::new("foo").expect("CString::new failed"); /// let bytes = c_string.into_bytes(); /// assert_eq!(bytes, vec![b'f', b'o', b'o']); /// ``` @@ -505,7 +508,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::from(c"foo"); + /// let c_string = CString::new("foo").expect("CString::new failed"); /// let bytes = c_string.into_bytes_with_nul(); /// assert_eq!(bytes, vec![b'f', b'o', b'o', b'\0']); /// ``` @@ -527,7 +530,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::from(c"foo"); + /// let c_string = CString::new("foo").expect("CString::new failed"); /// let bytes = c_string.as_bytes(); /// assert_eq!(bytes, &[b'f', b'o', b'o']); /// ``` @@ -547,7 +550,7 @@ impl CString { /// ``` /// use std::ffi::CString; /// - /// let c_string = CString::from(c"foo"); + /// let c_string = CString::new("foo").expect("CString::new failed"); /// let bytes = c_string.as_bytes_with_nul(); /// assert_eq!(bytes, &[b'f', b'o', b'o', b'\0']); /// ``` @@ -565,7 +568,7 @@ impl CString { /// ``` /// use std::ffi::{CString, CStr}; /// - /// let c_string = CString::from(c"foo"); + /// let c_string = CString::new(b"foo".to_vec()).expect("CString::new failed"); /// let cstr = c_string.as_c_str(); /// assert_eq!(cstr, /// CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed")); @@ -583,9 +586,12 @@ impl CString { /// # Examples /// /// ``` - /// let c_string = c"foo".to_owned(); + /// use std::ffi::{CString, CStr}; + /// + /// let c_string = CString::new(b"foo".to_vec()).expect("CString::new failed"); /// let boxed = c_string.into_boxed_c_str(); - /// assert_eq!(boxed.to_bytes_with_nul(), b"foo\0"); + /// assert_eq!(&*boxed, + /// CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed")); /// ``` #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "into_boxed_c_str", since = "1.20.0")] @@ -652,7 +658,7 @@ impl CString { /// assert_eq!( /// CString::from_vec_with_nul(b"abc\0".to_vec()) /// .expect("CString::from_vec_with_nul failed"), - /// c"abc".to_owned() + /// CString::new(b"abc".to_vec()).expect("CString::new failed") /// ); /// ``` /// @@ -767,7 +773,7 @@ impl From<&CStr> for Box { } #[cfg(not(test))] -#[stable(feature = "box_from_mut_slice", since = "1.84.0")] +#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut CStr> for Box { /// Converts a `&mut CStr` into a `Box`, /// by copying the contents into a newly allocated [`Box`]. @@ -915,7 +921,7 @@ impl From<&CStr> for Arc { } #[cfg(target_has_atomic = "ptr")] -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut CStr> for Arc { /// Converts a `&mut CStr` into a `Arc`, /// by copying the contents into a newly allocated [`Arc`]. @@ -947,7 +953,7 @@ impl From<&CStr> for Rc { } } -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut CStr> for Rc { /// Converts a `&mut CStr` into a `Rc`, /// by copying the contents into a newly allocated [`Rc`]. @@ -965,9 +971,8 @@ impl Default for Rc { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - 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) } + let c_str: &CStr = Default::default(); + Rc::from(c_str) } } @@ -1163,12 +1168,11 @@ impl CStr { /// # Examples /// /// ``` - /// use std::ffi::{CStr, CString}; - /// - /// let boxed: Box = Box::from(c"foo"); - /// let c_string: CString = c"foo".to_owned(); + /// use std::ffi::CString; /// - /// assert_eq!(boxed.into_c_string(), c_string); + /// let c_string = CString::new(b"foo".to_vec()).expect("CString::new failed"); + /// let boxed = c_string.into_boxed_c_str(); + /// assert_eq!(boxed.into_c_string(), CString::new("foo").expect("CString::new failed")); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "`self` will be dropped if the result is not used"] diff --git a/alloc/tests/c_str2.rs b/alloc/src/ffi/c_str/tests.rs similarity index 97% rename from alloc/tests/c_str2.rs rename to alloc/src/ffi/c_str/tests.rs index 0f4c27fa12322..8b7172b3f20a9 100644 --- a/alloc/tests/c_str2.rs +++ b/alloc/src/ffi/c_str/tests.rs @@ -1,12 +1,11 @@ -use alloc::ffi::CString; -use alloc::rc::Rc; -use alloc::sync::Arc; use core::assert_matches::assert_matches; -use core::ffi::{CStr, FromBytesUntilNulError, c_char}; +use core::ffi::FromBytesUntilNulError; #[allow(deprecated)] use core::hash::SipHasher13 as DefaultHasher; use core::hash::{Hash, Hasher}; +use super::*; + #[test] fn c_to_rust() { let data = b"123\0"; @@ -160,7 +159,7 @@ fn boxed_default() { #[test] fn test_c_str_clone_into() { - let mut c_string = c"lorem".to_owned(); + let mut c_string = CString::new("lorem").unwrap(); let c_ptr = c_string.as_ptr(); let c_str = CStr::from_bytes_with_nul(b"ipsum\0").unwrap(); c_str.clone_into(&mut c_string); diff --git a/alloc/src/ffi/mod.rs b/alloc/src/ffi/mod.rs index 695d7ad07cf76..4f9dc40a3cfc9 100644 --- a/alloc/src/ffi/mod.rs +++ b/alloc/src/ffi/mod.rs @@ -83,7 +83,7 @@ #[doc(inline)] #[stable(feature = "alloc_c_string", since = "1.64.0")] pub use self::c_str::CString; -#[doc(inline)] +#[doc(no_inline)] #[stable(feature = "alloc_c_string", since = "1.64.0")] pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError}; diff --git a/alloc/src/fmt.rs b/alloc/src/fmt.rs index e40de13f3d4a9..695dddb25eeb4 100644 --- a/alloc/src/fmt.rs +++ b/alloc/src/fmt.rs @@ -596,8 +596,6 @@ pub use core::fmt::{Arguments, write}; pub use core::fmt::{Binary, Octal}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{Debug, Display}; -#[unstable(feature = "formatting_options", issue = "118117")] -pub use core::fmt::{DebugAsHex, FormattingOptions, Sign}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/alloc/src/lib.rs b/alloc/src/lib.rs index 0bb7c432cc35f..041ff37897f05 100644 --- a/alloc/src/lib.rs +++ b/alloc/src/lib.rs @@ -88,10 +88,11 @@ #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] #![deny(ffi_unwind_calls)] -#![warn(unreachable_pub)] // // Library features: // tidy-alphabetical-start +#![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))] +#![cfg_attr(not(no_global_oom_handling), feature(const_btree_len))] #![cfg_attr(test, feature(str_as_str))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] @@ -100,15 +101,19 @@ #![feature(array_windows)] #![feature(ascii_char)] #![feature(assert_matches)] +#![feature(async_closure)] #![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_align_of_val)] +#![feature(const_box)] #![feature(const_eval_select)] #![feature(const_heap)] +#![feature(const_maybe_uninit_write)] +#![feature(const_size_of_val)] +#![feature(const_vec_string_slice)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] @@ -119,7 +124,6 @@ #![feature(extend_one_unchecked)] #![feature(fmt_internals)] #![feature(fn_traits)] -#![feature(formatting_options)] #![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] @@ -129,7 +133,6 @@ #![feature(local_waker)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array_transpose)] -#![feature(nonnull_provenance)] #![feature(panic_internals)] #![feature(pattern)] #![feature(pin_coerce_unsized_trait)] @@ -146,7 +149,6 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] -#![feature(temporary_niche_types)] #![feature(trusted_fused)] #![feature(trusted_len)] #![feature(trusted_random_access)] @@ -161,6 +163,8 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] @@ -168,11 +172,11 @@ #![feature(allow_internal_unstable)] #![feature(cfg_sanitize)] #![feature(const_precise_live_drops)] +#![feature(const_try)] #![feature(decl_macro)] #![feature(dropck_eyepatch)] #![feature(fundamental)] #![feature(hashmap_internals)] -#![feature(intrinsics)] #![feature(lang_items)] #![feature(min_specialization)] #![feature(multiple_supertrait_upcastable)] @@ -184,7 +188,6 @@ #![feature(slice_internals)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] -#![feature(strict_provenance_lints)] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] #![feature(with_negative_coherence)] @@ -228,11 +231,9 @@ pub mod alloc; pub mod boxed; #[cfg(test)] mod boxed { - pub(crate) use std::boxed::Box; + pub 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; @@ -246,6 +247,8 @@ pub mod string; pub mod sync; #[cfg(all(not(no_global_oom_handling), not(no_rc), not(no_sync)))] pub mod task; +#[cfg(test)] +mod tests; pub mod vec; #[doc(hidden)] diff --git a/alloc/src/macros.rs b/alloc/src/macros.rs index c000fd6f4efa7..8c6a367869ce0 100644 --- a/alloc/src/macros.rs +++ b/alloc/src/macros.rs @@ -48,9 +48,10 @@ macro_rules! vec { ); ($($x:expr),+ $(,)?) => ( <[_]>::into_vec( - // Using the intrinsic produces a dramatic improvement in stack usage for - // unoptimized programs using this code path to construct large Vecs. - $crate::boxed::box_new([$($x),+]) + // This rustc_box is not required, but it produces a dramatic improvement in compile + // time when constructing arrays with many elements. + #[rustc_box] + $crate::boxed::Box::new([$($x),+]) ) ); } diff --git a/alloc/src/raw_vec.rs b/alloc/src/raw_vec.rs index b80d1fc788947..85a9120c7e255 100644 --- a/alloc/src/raw_vec.rs +++ b/alloc/src/raw_vec.rs @@ -33,15 +33,21 @@ enum AllocInit { Zeroed, } -type Cap = core::num::niche_types::UsizeNoHighBit; +#[repr(transparent)] +#[cfg_attr(target_pointer_width = "16", rustc_layout_scalar_valid_range_end(0x7fff))] +#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0x7fff_ffff))] +#[cfg_attr(target_pointer_width = "64", rustc_layout_scalar_valid_range_end(0x7fff_ffff_ffff_ffff))] +struct Cap(usize); -const ZERO_CAP: Cap = unsafe { Cap::new_unchecked(0) }; +impl Cap { + const ZERO: Cap = unsafe { Cap(0) }; -/// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. -/// -/// # Safety: cap must be <= `isize::MAX`. -unsafe fn new_cap(cap: usize) -> Cap { - if T::IS_ZST { ZERO_CAP } else { unsafe { Cap::new_unchecked(cap) } } + /// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. + /// + /// # Safety: cap must be <= `isize::MAX`. + unsafe fn new(cap: usize) -> Self { + if T::IS_ZST { Cap::ZERO } else { unsafe { Self(cap) } } + } } /// A low-level utility for more ergonomically allocating, reallocating, and deallocating @@ -97,7 +103,8 @@ impl RawVec { /// `RawVec` with capacity `usize::MAX`. Useful for implementing /// delayed allocation. #[must_use] - pub(crate) const fn new() -> Self { + #[cfg_attr(bootstrap, rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81"))] + pub const fn new() -> Self { Self::new_in(Global) } @@ -120,7 +127,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub(crate) fn with_capacity(capacity: usize) -> Self { + pub fn with_capacity(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } @@ -129,7 +136,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub(crate) fn with_capacity_zeroed(capacity: usize) -> Self { + pub fn with_capacity_zeroed(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), _marker: PhantomData, @@ -172,7 +179,8 @@ impl RawVec { /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. #[inline] - pub(crate) const fn new_in(alloc: A) -> Self { + #[cfg_attr(bootstrap, rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81"))] + pub const fn new_in(alloc: A) -> Self { Self { inner: RawVecInner::new_in(alloc, align_of::()), _marker: PhantomData } } @@ -181,7 +189,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { + pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -191,7 +199,7 @@ impl RawVec { /// Like `try_with_capacity`, but parameterized over the choice of /// allocator for the returned `RawVec`. #[inline] - pub(crate) fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { Ok(inner) => Ok(Self { inner, _marker: PhantomData }), Err(e) => Err(e), @@ -203,7 +211,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub(crate) fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { + pub 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 +230,7 @@ impl RawVec { /// /// Note, that the requested capacity and `self.capacity()` could differ, as /// an allocator could overallocate and return a greater memory block than requested. - pub(crate) unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { + pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { // Sanity-check one half of the safety requirement (we cannot check the other half). debug_assert!( len <= self.capacity(), @@ -247,11 +255,11 @@ impl RawVec { /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is /// guaranteed. #[inline] - pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { + pub 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(); - let capacity = new_cap::(capacity); + let capacity = Cap::new::(capacity); Self { inner: RawVecInner::from_raw_parts_in(ptr, capacity, alloc), _marker: PhantomData, @@ -265,11 +273,11 @@ impl RawVec { /// /// See [`RawVec::from_raw_parts_in`]. #[inline] - pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { + pub unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); - let capacity = new_cap::(capacity); + let capacity = Cap::new::(capacity); Self { inner: RawVecInner::from_nonnull_in(ptr, capacity, alloc), _marker: PhantomData } } } @@ -278,12 +286,12 @@ impl RawVec { /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. #[inline] - pub(crate) const fn ptr(&self) -> *mut T { + pub const fn ptr(&self) -> *mut T { self.inner.ptr() } #[inline] - pub(crate) fn non_null(&self) -> NonNull { + pub fn non_null(&self) -> NonNull { self.inner.non_null() } @@ -291,13 +299,13 @@ impl RawVec { /// /// This will always be `usize::MAX` if `T` is zero-sized. #[inline] - pub(crate) const fn capacity(&self) -> usize { + pub const fn capacity(&self) -> usize { self.inner.capacity(size_of::()) } /// Returns a shared reference to the allocator backing this `RawVec`. #[inline] - pub(crate) fn allocator(&self) -> &A { + pub fn allocator(&self) -> &A { self.inner.allocator() } @@ -323,7 +331,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub(crate) fn reserve(&mut self, len: usize, additional: usize) { + pub fn reserve(&mut self, len: usize, additional: usize) { self.inner.reserve(len, additional, T::LAYOUT) } @@ -332,16 +340,12 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline(never)] #[track_caller] - pub(crate) fn grow_one(&mut self) { + pub 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(crate) fn try_reserve( - &mut self, - len: usize, - additional: usize, - ) -> Result<(), TryReserveError> { + pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { self.inner.try_reserve(len, additional, T::LAYOUT) } @@ -364,12 +368,12 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] #[track_caller] - pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { + pub 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(crate) fn try_reserve_exact( + pub fn try_reserve_exact( &mut self, len: usize, additional: usize, @@ -390,7 +394,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[track_caller] #[inline] - pub(crate) fn shrink_to_fit(&mut self, cap: usize) { + pub fn shrink_to_fit(&mut self, cap: usize) { self.inner.shrink_to_fit(cap, T::LAYOUT) } } @@ -405,10 +409,11 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { impl RawVecInner { #[inline] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81"))] const fn new_in(alloc: A, align: usize) -> Self { let ptr = unsafe { core::mem::transmute(align) }; // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr, cap: ZERO_CAP, alloc } + Self { ptr, cap: Cap::ZERO, alloc } } #[cfg(not(no_global_oom_handling))] @@ -418,7 +423,7 @@ impl RawVecInner { match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) { Ok(this) => { unsafe { - // Make it more obvious that a subsequent Vec::reserve(capacity) will not allocate. + // Make it more obvious that a subsquent Vec::reserve(capacity) will not allocate. hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout)); } this @@ -481,11 +486,7 @@ impl RawVecInner { // Allocators currently return a `NonNull<[u8]>` whose length // matches the size requested. If that ever changes, the capacity // here should change to `ptr.len() / mem::size_of::()`. - Ok(Self { - ptr: Unique::from(ptr.cast()), - cap: unsafe { Cap::new_unchecked(capacity) }, - alloc, - }) + Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) } #[inline] @@ -510,7 +511,7 @@ impl RawVecInner { #[inline] const fn capacity(&self, elem_size: usize) -> usize { - if elem_size == 0 { usize::MAX } else { self.cap.as_inner() } + if elem_size == 0 { usize::MAX } else { self.cap.0 } } #[inline] @@ -520,7 +521,7 @@ impl RawVecInner { #[inline] fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { - if elem_layout.size() == 0 || self.cap.as_inner() == 0 { + if elem_layout.size() == 0 || self.cap.0 == 0 { None } else { // We could use Layout::array here which ensures the absence of isize and usize overflows @@ -528,7 +529,7 @@ impl RawVecInner { // has already been allocated so we know it can't overflow and currently Rust does not // support such types. So we can do better by skipping some checks and avoid an unwrap. unsafe { - let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner()); + let alloc_size = elem_layout.size().unchecked_mul(self.cap.0); let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); Some((self.ptr.into(), layout)) } @@ -564,7 +565,7 @@ impl RawVecInner { #[inline] #[track_caller] fn grow_one(&mut self, elem_layout: Layout) { - if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) { + if let Err(err) = self.grow_amortized(self.cap.0, 1, elem_layout) { handle_error(err); } } @@ -629,7 +630,7 @@ impl RawVecInner { // the size requested. If that ever changes, the capacity here should // change to `ptr.len() / mem::size_of::()`. self.ptr = Unique::from(ptr.cast()); - self.cap = unsafe { Cap::new_unchecked(cap) }; + self.cap = unsafe { Cap(cap) }; } fn grow_amortized( @@ -652,7 +653,7 @@ impl RawVecInner { // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. - let cap = cmp::max(self.cap.as_inner() * 2, required_cap); + let cap = cmp::max(self.cap.0 * 2, required_cap); let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); let new_layout = layout_array(cap, elem_layout)?; @@ -721,7 +722,7 @@ impl RawVecInner { unsafe { self.alloc.deallocate(ptr, layout) }; self.ptr = unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; - self.cap = ZERO_CAP; + self.cap = Cap::ZERO; } else { let ptr = unsafe { // Layout cannot overflow here because it would have @@ -756,9 +757,7 @@ impl RawVecInner { } } -// not marked inline(never) since we want optimizers to be able to observe the specifics of this -// function, see tests/codegen/vec-reserve-extend.rs. -#[cold] +#[inline(never)] fn finish_grow( new_layout: Layout, current_memory: Option<(NonNull, Layout)>, diff --git a/alloc/src/rc.rs b/alloc/src/rc.rs index 6d82f514aa683..3a9bd1b5bf119 100644 --- a/alloc/src/rc.rs +++ b/alloc/src/rc.rs @@ -252,7 +252,6 @@ use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; -use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] @@ -308,7 +307,7 @@ fn rc_inner_layout_for_value_layout(layout: Layout) -> Layout { /// `value.get_mut()`. This avoids conflicts with methods of the inner type `T`. /// /// [get_mut]: Rc::get_mut -#[doc(search_unbox)] +#[cfg_attr(not(bootstrap), doc(search_unbox))] #[cfg_attr(not(test), rustc_diagnostic_item = "Rc")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] @@ -796,7 +795,7 @@ impl Rc { let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); let init_ptr: NonNull> = uninit_ptr.cast(); - let weak = Weak { ptr: init_ptr, alloc }; + let weak = Weak { ptr: init_ptr, alloc: alloc }; // It's important we don't give up ownership of the weak pointer, or // else the memory might be freed by the time `data_fn` returns. If @@ -1085,26 +1084,6 @@ impl Rc<[T]> { )) } } - - /// Converts the reference-counted slice into a reference-counted array. - /// - /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. - /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[unstable(feature = "slice_as_array", issue = "133508")] - #[inline] - #[must_use] - pub fn into_array(self) -> Option> { - if self.len() == N { - let ptr = Self::into_raw(self) as *const [T; N]; - - // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. - let me = unsafe { Rc::from_raw(ptr) }; - Some(me) - } else { - None - } - } } impl Rc<[T], A> { @@ -1790,7 +1769,7 @@ impl Rc { /// let x: Rc<&str> = Rc::new("Hello, world!"); /// { /// let s = String::from("Oh, no!"); - /// let mut y: Rc<&str> = x.clone(); + /// let mut y: Rc<&str> = x.clone().into(); /// unsafe { /// // this is Undefined Behavior, because x's inner type /// // is &'long str, not &'short str @@ -2253,20 +2232,12 @@ impl Deref for Rc { #[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] unsafe impl PinCoerceUnsized for Rc {} -//#[unstable(feature = "unique_rc_arc", issue = "112566")] -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] -unsafe impl PinCoerceUnsized for UniqueRc {} - #[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] unsafe impl PinCoerceUnsized for Weak {} #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl DerefPure for Rc {} -//#[unstable(feature = "unique_rc_arc", issue = "112566")] -#[unstable(feature = "deref_pure_trait", issue = "87121")] -unsafe impl DerefPure for UniqueRc {} - #[unstable(feature = "legacy_receiver_trait", issue = "none")] impl LegacyReceiver for Rc {} @@ -2350,10 +2321,11 @@ impl Default for Rc { fn default() -> Rc { 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(), ) } @@ -2368,9 +2340,7 @@ impl Default for Rc { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - let rc = Rc::<[u8]>::default(); - // `[u8]` has the same layout as `str`. - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const str) } + Rc::from("") } } @@ -2689,7 +2659,7 @@ impl From<&[T]> for Rc<[T]> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut [T]> for Rc<[T]> { /// Allocates a reference-counted slice and fills it by cloning `v`'s items. /// @@ -2728,7 +2698,7 @@ impl From<&str> for Rc { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut str> for Rc { /// Allocates a reference-counted string slice and copies `v` into it. /// @@ -3029,7 +2999,12 @@ impl Weak { #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] #[must_use] pub const fn new() -> Weak { - Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } + Weak { + ptr: unsafe { + NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) + }, + alloc: Global, + } } } @@ -3051,7 +3026,12 @@ impl Weak { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(alloc: A) -> Weak { - Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } + Weak { + ptr: unsafe { + NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) + }, + alloc, + } } } @@ -3704,266 +3684,22 @@ fn data_offset_align(align: usize) -> usize { /// previous example, `UniqueRc` allows for more flexibility in the construction of cyclic data, /// including fallible or async constructors. #[unstable(feature = "unique_rc_arc", issue = "112566")] +#[derive(Debug)] pub struct UniqueRc< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, > { ptr: NonNull>, - // Define the ownership of `RcInner` for drop-check - _marker: PhantomData>, - // Invariance is necessary for soundness: once other `Weak` - // references exist, we already have a form of shared mutability! - _marker2: PhantomData<*mut T>, + phantom: PhantomData>, alloc: A, } -// Not necessary for correctness since `UniqueRc` contains `NonNull`, -// but having an explicit negative impl is nice for documentation purposes -// and results in nicer error messages. -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl !Send for UniqueRc {} - -// Not necessary for correctness since `UniqueRc` contains `NonNull`, -// but having an explicit negative impl is nice for documentation purposes -// and results in nicer error messages. -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl !Sync for UniqueRc {} - #[unstable(feature = "unique_rc_arc", issue = "112566")] impl, U: ?Sized, A: Allocator> CoerceUnsized> for UniqueRc { } -//#[unstable(feature = "unique_rc_arc", issue = "112566")] -#[unstable(feature = "dispatch_from_dyn", issue = "none")] -impl, U: ?Sized> DispatchFromDyn> for UniqueRc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl fmt::Display for UniqueRc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl fmt::Debug for UniqueRc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl fmt::Pointer for UniqueRc { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&(&raw const **self), f) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl borrow::Borrow for UniqueRc { - fn borrow(&self) -> &T { - &**self - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl borrow::BorrowMut for UniqueRc { - fn borrow_mut(&mut self) -> &mut T { - &mut **self - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl AsRef for UniqueRc { - fn as_ref(&self) -> &T { - &**self - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl AsMut for UniqueRc { - fn as_mut(&mut self) -> &mut T { - &mut **self - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Unpin for UniqueRc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl PartialEq for UniqueRc { - /// Equality for two `UniqueRc`s. - /// - /// Two `UniqueRc`s are equal if their inner values are equal. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::rc::UniqueRc; - /// - /// let five = UniqueRc::new(5); - /// - /// assert!(five == UniqueRc::new(5)); - /// ``` - #[inline] - fn eq(&self, other: &Self) -> bool { - PartialEq::eq(&**self, &**other) - } - - /// Inequality for two `UniqueRc`s. - /// - /// Two `UniqueRc`s are not equal if their inner values are not equal. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::rc::UniqueRc; - /// - /// let five = UniqueRc::new(5); - /// - /// assert!(five != UniqueRc::new(6)); - /// ``` - #[inline] - fn ne(&self, other: &Self) -> bool { - PartialEq::ne(&**self, &**other) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl PartialOrd for UniqueRc { - /// Partial comparison for two `UniqueRc`s. - /// - /// The two are compared by calling `partial_cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::rc::UniqueRc; - /// use std::cmp::Ordering; - /// - /// let five = UniqueRc::new(5); - /// - /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&UniqueRc::new(6))); - /// ``` - #[inline(always)] - fn partial_cmp(&self, other: &UniqueRc) -> Option { - (**self).partial_cmp(&**other) - } - - /// Less-than comparison for two `UniqueRc`s. - /// - /// The two are compared by calling `<` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::rc::UniqueRc; - /// - /// let five = UniqueRc::new(5); - /// - /// assert!(five < UniqueRc::new(6)); - /// ``` - #[inline(always)] - fn lt(&self, other: &UniqueRc) -> bool { - **self < **other - } - - /// 'Less than or equal to' comparison for two `UniqueRc`s. - /// - /// The two are compared by calling `<=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::rc::UniqueRc; - /// - /// let five = UniqueRc::new(5); - /// - /// assert!(five <= UniqueRc::new(5)); - /// ``` - #[inline(always)] - fn le(&self, other: &UniqueRc) -> bool { - **self <= **other - } - - /// Greater-than comparison for two `UniqueRc`s. - /// - /// The two are compared by calling `>` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::rc::UniqueRc; - /// - /// let five = UniqueRc::new(5); - /// - /// assert!(five > UniqueRc::new(4)); - /// ``` - #[inline(always)] - fn gt(&self, other: &UniqueRc) -> bool { - **self > **other - } - - /// 'Greater than or equal to' comparison for two `UniqueRc`s. - /// - /// The two are compared by calling `>=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::rc::UniqueRc; - /// - /// let five = UniqueRc::new(5); - /// - /// assert!(five >= UniqueRc::new(5)); - /// ``` - #[inline(always)] - fn ge(&self, other: &UniqueRc) -> bool { - **self >= **other - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Ord for UniqueRc { - /// Comparison for two `UniqueRc`s. - /// - /// The two are compared by calling `cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// #![feature(unique_rc_arc)] - /// use std::rc::UniqueRc; - /// use std::cmp::Ordering; - /// - /// let five = UniqueRc::new(5); - /// - /// assert_eq!(Ordering::Less, five.cmp(&UniqueRc::new(6))); - /// ``` - #[inline] - fn cmp(&self, other: &UniqueRc) -> Ordering { - (**self).cmp(&**other) - } -} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Eq for UniqueRc {} - -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Hash for UniqueRc { - fn hash(&self, state: &mut H) { - (**self).hash(state); - } -} - // Depends on A = Global impl UniqueRc { /// Creates a new `UniqueRc`. @@ -3999,7 +3735,7 @@ impl UniqueRc { }, alloc, )); - Self { ptr: ptr.into(), _marker: PhantomData, _marker2: PhantomData, alloc } + Self { ptr: ptr.into(), phantom: PhantomData, alloc } } } @@ -4055,6 +3791,9 @@ impl Deref for UniqueRc { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for UniqueRc {} + #[unstable(feature = "unique_rc_arc", issue = "112566")] impl DerefMut for UniqueRc { fn deref_mut(&mut self) -> &mut T { diff --git a/alloc/src/rc/tests.rs b/alloc/src/rc/tests.rs index 2210a7c24c06a..333e1bde31c1e 100644 --- a/alloc/src/rc/tests.rs +++ b/alloc/src/rc/tests.rs @@ -349,9 +349,9 @@ fn test_unsized() { #[test] fn test_maybe_thin_unsized() { // If/when custom thin DSTs exist, this test should be updated to use one - use std::ffi::CStr; + use std::ffi::{CStr, CString}; - let x: Rc = Rc::from(c"swordfish"); + let x: Rc = Rc::from(CString::new("swordfish").unwrap().into_boxed_c_str()); assert_eq!(format!("{x:?}"), "\"swordfish\""); let y: Weak = Rc::downgrade(&x); drop(x); diff --git a/alloc/src/slice.rs b/alloc/src/slice.rs index 1cedead7aa243..e3c7835f1d10b 100644 --- a/alloc/src/slice.rs +++ b/alloc/src/slice.rs @@ -27,8 +27,6 @@ pub use core::slice::ArrayChunksMut; pub use core::slice::ArrayWindows; #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub use core::slice::EscapeAscii; -#[unstable(feature = "get_many_mut", issue = "104642")] -pub use core::slice::GetManyMutError; #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use core::slice::SliceIndex; #[cfg(not(no_global_oom_handling))] @@ -85,7 +83,6 @@ 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/alloc/src/string.rs b/alloc/src/string.rs index b29f740ef0f2a..e0576c2551545 100644 --- a/alloc/src/string.rs +++ b/alloc/src/string.rs @@ -62,10 +62,10 @@ use crate::alloc::Allocator; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::collections::TryReserveError; -use crate::str::{self, CharIndices, Chars, Utf8Error, from_utf8_unchecked_mut}; +use crate::str::{self, Chars, Utf8Error, from_utf8_unchecked_mut}; #[cfg(not(no_global_oom_handling))] use crate::str::{FromStr, from_boxed_utf8_unchecked}; -use crate::vec::{self, Vec}; +use crate::vec::Vec; /// A UTF-8–encoded, growable string. /// @@ -712,8 +712,8 @@ impl String { } } - /// Decode a native endian UTF-16–encoded vector `v` into a `String`, - /// returning [`Err`] if `v` contains any invalid data. + /// Decode a 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 native endian UTF-16–encoded slice `v` into a `String`, - /// replacing invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. + /// Decode a 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 /// @@ -1952,61 +1952,6 @@ impl String { Drain { start, end, iter: chars_iter, string: self_ptr } } - /// Converts a `String` into an iterator over the [`char`]s of the string. - /// - /// As a string consists of valid UTF-8, we can iterate through a string - /// by [`char`]. This method returns such an iterator. - /// - /// It's important to remember that [`char`] represents a Unicode Scalar - /// Value, and might not match your idea of what a 'character' is. Iteration - /// over grapheme clusters may be what you actually want. That functionality - /// is not provided by Rust's standard library, check crates.io instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(string_into_chars)] - /// - /// let word = String::from("goodbye"); - /// - /// let mut chars = word.into_chars(); - /// - /// assert_eq!(Some('g'), chars.next()); - /// assert_eq!(Some('o'), chars.next()); - /// assert_eq!(Some('o'), chars.next()); - /// assert_eq!(Some('d'), chars.next()); - /// assert_eq!(Some('b'), chars.next()); - /// assert_eq!(Some('y'), chars.next()); - /// assert_eq!(Some('e'), chars.next()); - /// - /// assert_eq!(None, chars.next()); - /// ``` - /// - /// Remember, [`char`]s might not match your intuition about characters: - /// - /// ``` - /// #![feature(string_into_chars)] - /// - /// let y = String::from("y̆"); - /// - /// let mut chars = y.into_chars(); - /// - /// assert_eq!(Some('y'), chars.next()); // not 'y̆' - /// assert_eq!(Some('\u{0306}'), chars.next()); - /// - /// assert_eq!(None, chars.next()); - /// ``` - /// - /// [`char`]: prim@char - #[inline] - #[must_use = "`self` will be dropped if the result is not used"] - #[unstable(feature = "string_into_chars", issue = "133125")] - pub fn into_chars(self) -> IntoChars { - IntoChars { bytes: self.into_bytes().into_iter() } - } - /// Removes the specified range in the string, /// and replaces it with the given string. /// The given string doesn't need to be the same length as the range. @@ -2730,28 +2675,14 @@ pub trait ToString { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl ToString for T { - #[inline] - fn to_string(&self) -> String { - ::spec_to_string(self) - } -} - -#[cfg(not(no_global_oom_handling))] -trait SpecToString { - fn spec_to_string(&self) -> String; -} - -#[cfg(not(no_global_oom_handling))] -impl SpecToString for T { // A common guideline is to not inline generic functions. However, // removing `#[inline]` from this method causes non-negligible regressions. // See , the last attempt // to try to remove it. #[inline] - default fn spec_to_string(&self) -> String { + default fn to_string(&self) -> String { let mut buf = String::new(); - let mut formatter = - core::fmt::Formatter::new(&mut buf, core::fmt::FormattingOptions::new()); + let mut formatter = core::fmt::Formatter::new(&mut buf); // Bypass format_args!() to avoid write_str with zero-length strs fmt::Display::fmt(self, &mut formatter) .expect("a Display implementation returned an error unexpectedly"); @@ -2759,34 +2690,42 @@ impl SpecToString for T { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -impl SpecToString for core::ascii::Char { +#[unstable(feature = "ascii_char", issue = "110998")] +impl ToString for core::ascii::Char { #[inline] - fn spec_to_string(&self) -> String { + fn to_string(&self) -> String { self.as_str().to_owned() } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -impl SpecToString for char { +#[stable(feature = "char_to_string_specialization", since = "1.46.0")] +impl ToString for char { #[inline] - fn spec_to_string(&self) -> String { + fn to_string(&self) -> String { String::from(self.encode_utf8(&mut [0; 4])) } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -impl SpecToString for bool { +#[stable(feature = "bool_to_string_specialization", since = "1.68.0")] +impl ToString for bool { #[inline] - fn spec_to_string(&self) -> String { + fn to_string(&self) -> String { String::from(if *self { "true" } else { "false" }) } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -impl SpecToString for u8 { +#[stable(feature = "u8_to_string_specialization", since = "1.54.0")] +impl ToString for u8 { #[inline] - fn spec_to_string(&self) -> String { + fn to_string(&self) -> String { let mut buf = String::with_capacity(3); let mut n = *self; if n >= 10 { @@ -2802,10 +2741,12 @@ impl SpecToString for u8 { } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -impl SpecToString for i8 { +#[stable(feature = "i8_to_string_specialization", since = "1.54.0")] +impl ToString for i8 { #[inline] - fn spec_to_string(&self) -> String { + fn to_string(&self) -> String { let mut buf = String::with_capacity(4); if self.is_negative() { buf.push('-'); @@ -2846,9 +2787,11 @@ macro_rules! to_string_expr_wrap_in_deref { macro_rules! to_string_str { {$($($x:ident)*),+} => { $( - impl SpecToString for to_string_str_wrap_in_ref!($($x)*) { + #[doc(hidden)] + #[stable(feature = "str_to_string_specialization", since = "1.9.0")] + impl ToString for to_string_str_wrap_in_ref!($($x)*) { #[inline] - fn spec_to_string(&self) -> String { + fn to_string(&self) -> String { String::from(to_string_expr_wrap_in_deref!(self ; $($x)*)) } } @@ -2872,26 +2815,32 @@ to_string_str! { x, } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -impl SpecToString for Cow<'_, str> { +#[stable(feature = "cow_str_to_string_specialization", since = "1.17.0")] +impl ToString for Cow<'_, str> { #[inline] - fn spec_to_string(&self) -> String { + fn to_string(&self) -> String { self[..].to_owned() } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -impl SpecToString for String { +#[stable(feature = "string_to_string_specialization", since = "1.17.0")] +impl ToString for String { #[inline] - fn spec_to_string(&self) -> String { + fn to_string(&self) -> String { self.to_owned() } } +#[doc(hidden)] #[cfg(not(no_global_oom_handling))] -impl SpecToString for fmt::Arguments<'_> { +#[stable(feature = "fmt_arguments_to_string_specialization", since = "1.71.0")] +impl ToString for fmt::Arguments<'_> { #[inline] - fn spec_to_string(&self) -> String { + fn to_string(&self) -> String { crate::fmt::format(*self) } } @@ -3145,134 +3094,6 @@ impl fmt::Write for String { } } -/// An iterator over the [`char`]s of a string. -/// -/// This struct is created by the [`into_chars`] method on [`String`]. -/// See its documentation for more. -/// -/// [`char`]: prim@char -/// [`into_chars`]: String::into_chars -#[cfg_attr(not(no_global_oom_handling), derive(Clone))] -#[must_use = "iterators are lazy and do nothing unless consumed"] -#[unstable(feature = "string_into_chars", issue = "133125")] -pub struct IntoChars { - bytes: vec::IntoIter, -} - -#[unstable(feature = "string_into_chars", issue = "133125")] -impl fmt::Debug for IntoChars { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("IntoChars").field(&self.as_str()).finish() - } -} - -impl IntoChars { - /// Views the underlying data as a subslice of the original data. - /// - /// # Examples - /// - /// ``` - /// #![feature(string_into_chars)] - /// - /// let mut chars = String::from("abc").into_chars(); - /// - /// assert_eq!(chars.as_str(), "abc"); - /// chars.next(); - /// assert_eq!(chars.as_str(), "bc"); - /// chars.next(); - /// chars.next(); - /// assert_eq!(chars.as_str(), ""); - /// ``` - #[unstable(feature = "string_into_chars", issue = "133125")] - #[must_use] - #[inline] - pub fn as_str(&self) -> &str { - // SAFETY: `bytes` is a valid UTF-8 string. - unsafe { str::from_utf8_unchecked(self.bytes.as_slice()) } - } - - /// Consumes the `IntoChars`, returning the remaining string. - /// - /// # Examples - /// - /// ``` - /// #![feature(string_into_chars)] - /// - /// let chars = String::from("abc").into_chars(); - /// assert_eq!(chars.into_string(), "abc"); - /// - /// let mut chars = String::from("def").into_chars(); - /// chars.next(); - /// assert_eq!(chars.into_string(), "ef"); - /// ``` - #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "string_into_chars", issue = "133125")] - #[inline] - pub fn into_string(self) -> String { - // Safety: `bytes` are kept in UTF-8 form, only removing whole `char`s at a time. - unsafe { String::from_utf8_unchecked(self.bytes.collect()) } - } - - #[inline] - fn iter(&self) -> CharIndices<'_> { - self.as_str().char_indices() - } -} - -#[unstable(feature = "string_into_chars", issue = "133125")] -impl Iterator for IntoChars { - type Item = char; - - #[inline] - fn next(&mut self) -> Option { - let mut iter = self.iter(); - match iter.next() { - None => None, - Some((_, ch)) => { - let offset = iter.offset(); - // `offset` is a valid index. - let _ = self.bytes.advance_by(offset); - Some(ch) - } - } - } - - #[inline] - fn count(self) -> usize { - self.iter().count() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter().size_hint() - } - - #[inline] - fn last(mut self) -> Option { - self.next_back() - } -} - -#[unstable(feature = "string_into_chars", issue = "133125")] -impl DoubleEndedIterator for IntoChars { - #[inline] - fn next_back(&mut self) -> Option { - let len = self.as_str().len(); - let mut iter = self.iter(); - match iter.next_back() { - None => None, - Some((idx, ch)) => { - // `idx` is a valid index. - let _ = self.bytes.advance_back_by(len - idx); - Some(ch) - } - } - } -} - -#[unstable(feature = "string_into_chars", issue = "133125")] -impl FusedIterator for IntoChars {} - /// A draining iterator for `String`. /// /// This struct is created by the [`drain`] method on [`String`]. See its diff --git a/alloc/src/sync.rs b/alloc/src/sync.rs index dba1449347ac0..da2d6bb3bce24 100644 --- a/alloc/src/sync.rs +++ b/alloc/src/sync.rs @@ -18,7 +18,6 @@ use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; -use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, LegacyReceiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::{Pin, PinCoerceUnsized}; @@ -40,6 +39,9 @@ use crate::string::String; #[cfg(not(no_global_oom_handling))] use crate::vec::Vec; +#[cfg(test)] +mod tests; + /// A soft limit on the amount of references that may be made to an `Arc`. /// /// Going above this limit will abort your program (although not @@ -233,7 +235,7 @@ macro_rules! acquire { /// counting in general. /// /// [rc_examples]: crate::rc#examples -#[doc(search_unbox)] +#[cfg_attr(not(bootstrap), doc(search_unbox))] #[cfg_attr(not(test), rustc_diagnostic_item = "Arc")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] @@ -785,7 +787,7 @@ impl Arc { let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); let init_ptr: NonNull> = uninit_ptr.cast(); - let weak = Weak { ptr: init_ptr, alloc }; + let weak = Weak { ptr: init_ptr, alloc: alloc }; // It's important we don't give up ownership of the weak pointer, or // else the memory might be freed by the time `data_fn` returns. If @@ -1204,26 +1206,6 @@ impl Arc<[T]> { )) } } - - /// Converts the reference-counted slice into a reference-counted array. - /// - /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. - /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[unstable(feature = "slice_as_array", issue = "133508")] - #[inline] - #[must_use] - pub fn into_array(self) -> Option> { - if self.len() == N { - let ptr = Self::into_raw(self) as *const [T; N]; - - // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. - let me = unsafe { Arc::from_raw(ptr) }; - Some(me) - } else { - None - } - } } impl Arc<[T], A> { @@ -1397,8 +1379,6 @@ impl Arc { /// different types. See [`mem::transmute`][transmute] for more information /// on what restrictions apply in this case. /// - /// The raw pointer must point to a block of memory allocated by the global allocator. - /// /// The user of `from_raw` has to make sure a specific value of `T` is only /// dropped once. /// @@ -1454,8 +1434,7 @@ impl Arc { /// /// The pointer must have been obtained through `Arc::into_raw`, and the /// associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) for the duration of this method, and `ptr` must point to a block of memory - /// allocated by the global allocator. + /// least 1) for the duration of this method. /// /// # Examples /// @@ -1489,8 +1468,7 @@ impl Arc { /// /// The pointer must have been obtained through `Arc::into_raw`, and the /// associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) when invoking this method, and `ptr` must point to a block of memory - /// allocated by the global allocator. This method can be used to release the final + /// least 1) when invoking this method. This method can be used to release the final /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been /// released. /// @@ -2473,7 +2451,7 @@ impl Arc { /// let x: Arc<&str> = Arc::new("Hello, world!"); /// { /// let s = String::from("Oh, no!"); - /// let mut y: Arc<&str> = x.clone(); + /// let mut y: Arc<&str> = x.clone().into(); /// unsafe { /// // this is Undefined Behavior, because x's inner type /// // is &'long str, not &'short str @@ -2692,7 +2670,12 @@ impl Weak { #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] #[must_use] pub const fn new() -> Weak { - Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } + Weak { + ptr: unsafe { + NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) + }, + alloc: Global, + } } } @@ -2717,7 +2700,12 @@ impl Weak { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(alloc: A) -> Weak { - Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } + Weak { + ptr: unsafe { + NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) + }, + alloc, + } } } @@ -2740,7 +2728,7 @@ impl Weak { /// # Safety /// /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference, and must point to a block of memory allocated by global allocator. + /// weak reference. /// /// 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,14 +3454,11 @@ impl Default for Arc { fn default() -> Arc { 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(), ) } @@ -3633,7 +3618,7 @@ impl From<&[T]> for Arc<[T]> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut [T]> for Arc<[T]> { /// Allocates a reference-counted slice and fills it by cloning `v`'s items. /// @@ -3672,7 +3657,7 @@ impl From<&str> for Arc { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut str> for Arc { /// Allocates a reference-counted `str` and copies `v` into it. /// diff --git a/alloc/tests/sync.rs b/alloc/src/sync/tests.rs similarity index 98% rename from alloc/tests/sync.rs rename to alloc/src/sync/tests.rs index 6d3ab1b1d11e1..3f66c88992344 100644 --- a/alloc/tests/sync.rs +++ b/alloc/src/sync/tests.rs @@ -1,16 +1,14 @@ -use alloc::sync::*; -use std::alloc::{AllocError, Allocator, Layout}; -use std::any::Any; use std::clone::Clone; use std::mem::MaybeUninit; use std::option::Option::None; -use std::ptr::NonNull; use std::sync::Mutex; -use std::sync::atomic::Ordering::*; -use std::sync::atomic::{self, AtomicUsize}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; use std::sync::mpsc::channel; use std::thread; +use super::*; + struct Canary(*mut AtomicUsize); impl Drop for Canary { @@ -128,7 +126,6 @@ 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 @@ -415,9 +412,9 @@ fn test_unsized() { #[test] fn test_maybe_thin_unsized() { // If/when custom thin DSTs exist, this test should be updated to use one - use std::ffi::CStr; + use std::ffi::{CStr, CString}; - let x: Arc = Arc::from(c"swordfish"); + let x: Arc = Arc::from(CString::new("swordfish").unwrap().into_boxed_c_str()); assert_eq!(format!("{x:?}"), "\"swordfish\""); let y: Weak = Arc::downgrade(&x); drop(x); diff --git a/alloc/src/task.rs b/alloc/src/task.rs index b4116f4988b64..0f8e74300a491 100644 --- a/alloc/src/task.rs +++ b/alloc/src/task.rs @@ -199,6 +199,7 @@ fn raw_waker(waker: Arc) -> RawWaker { /// /// ```rust /// #![feature(local_waker)] +/// #![feature(noop_waker)] /// use std::task::{LocalWake, ContextBuilder, LocalWaker, Waker}; /// use std::future::Future; /// use std::pin::Pin; diff --git a/alloc/src/testing/crash_test.rs b/alloc/src/testing/crash_test.rs index 8e00e4f41e5d6..684bac60d9a86 100644 --- a/alloc/src/testing/crash_test.rs +++ b/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(crate) struct CrashTestDummy { +pub struct CrashTestDummy { pub id: usize, cloned: AtomicUsize, dropped: AtomicUsize, @@ -20,7 +20,7 @@ pub(crate) struct CrashTestDummy { impl CrashTestDummy { /// Creates a crash test dummy design. The `id` determines order and equality of instances. - pub(crate) fn new(id: usize) -> CrashTestDummy { + pub 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(crate) fn spawn(&self, panic: Panic) -> Instance<'_> { + pub fn spawn(&self, panic: Panic) -> Instance<'_> { Instance { origin: self, panic } } /// Returns how many times instances of the dummy have been cloned. - pub(crate) fn cloned(&self) -> usize { + pub fn cloned(&self) -> usize { self.cloned.load(SeqCst) } /// Returns how many times instances of the dummy have been dropped. - pub(crate) fn dropped(&self) -> usize { + pub fn dropped(&self) -> usize { self.dropped.load(SeqCst) } /// Returns how many times instances of the dummy have had their `query` member invoked. - pub(crate) fn queried(&self) -> usize { + pub fn queried(&self) -> usize { self.queried.load(SeqCst) } } #[derive(Debug)] -pub(crate) struct Instance<'a> { +pub struct Instance<'a> { origin: &'a CrashTestDummy, panic: Panic, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub(crate) enum Panic { +pub enum Panic { Never, InClone, InDrop, @@ -66,12 +66,12 @@ pub(crate) enum Panic { } impl Instance<'_> { - pub(crate) fn id(&self) -> usize { + pub fn id(&self) -> usize { self.origin.id } /// Some anonymous query, the result of which is already given. - pub(crate) fn query(&self, result: R) -> R { + pub fn query(&self, result: R) -> R { self.origin.queried.fetch_add(1, SeqCst); if self.panic == Panic::InQuery { panic!("panic in `query`"); diff --git a/alloc/src/testing/mod.rs b/alloc/src/testing/mod.rs index c8457daf93e5a..7a094f8a59522 100644 --- a/alloc/src/testing/mod.rs +++ b/alloc/src/testing/mod.rs @@ -1,3 +1,3 @@ -pub(crate) mod crash_test; -pub(crate) mod ord_chaos; -pub(crate) mod rng; +pub mod crash_test; +pub mod ord_chaos; +pub mod rng; diff --git a/alloc/src/testing/ord_chaos.rs b/alloc/src/testing/ord_chaos.rs index 55e1ae5e3deaa..96ce7c1579046 100644 --- a/alloc/src/testing/ord_chaos.rs +++ b/alloc/src/testing/ord_chaos.rs @@ -4,7 +4,7 @@ use std::ptr; // Minimal type with an `Ord` implementation violating transitivity. #[derive(Debug)] -pub(crate) enum Cyclic3 { +pub enum Cyclic3 { A, B, C, @@ -37,16 +37,16 @@ impl Eq for Cyclic3 {} // Controls the ordering of values wrapped by `Governed`. #[derive(Debug)] -pub(crate) struct Governor { +pub struct Governor { flipped: Cell, } impl Governor { - pub(crate) fn new() -> Self { + pub fn new() -> Self { Governor { flipped: Cell::new(false) } } - pub(crate) fn flip(&self) { + pub 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(crate) struct Governed<'a, T>(pub T, pub &'a Governor); +pub struct Governed<'a, T>(pub T, pub &'a Governor); impl PartialOrd for Governed<'_, T> { fn partial_cmp(&self, other: &Self) -> Option { diff --git a/alloc/src/testing/rng.rs b/alloc/src/testing/rng.rs index 77d3348f38a5d..ecf543bee035a 100644 --- a/alloc/src/testing/rng.rs +++ b/alloc/src/testing/rng.rs @@ -1,5 +1,5 @@ /// XorShiftRng -pub(crate) struct DeterministicRng { +pub struct DeterministicRng { count: usize, x: u32, y: u32, @@ -8,12 +8,12 @@ pub(crate) struct DeterministicRng { } impl DeterministicRng { - pub(crate) fn new() -> Self { + pub fn new() -> Self { DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } } /// Guarantees that each returned number is unique. - pub(crate) fn next(&mut self) -> u32 { + pub fn next(&mut self) -> u32 { self.count += 1; assert!(self.count <= 70029); let x = self.x; diff --git a/alloc/tests/misc_tests.rs b/alloc/src/tests.rs similarity index 100% rename from alloc/tests/misc_tests.rs rename to alloc/src/tests.rs diff --git a/alloc/src/vec/extract_if.rs b/alloc/src/vec/extract_if.rs index 4db13981596bc..72d51e8904488 100644 --- a/alloc/src/vec/extract_if.rs +++ b/alloc/src/vec/extract_if.rs @@ -1,4 +1,3 @@ -use core::ops::{Range, RangeBounds}; use core::{ptr, slice}; use super::Vec; @@ -15,7 +14,7 @@ use crate::alloc::{Allocator, Global}; /// #![feature(extract_if)] /// /// let mut v = vec![0, 1, 2]; -/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0); +/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(|x| *x % 2 == 0); /// ``` #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] #[derive(Debug)] @@ -25,32 +24,24 @@ pub struct ExtractIf< T, F, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> { - vec: &'a mut Vec, +> where + F: FnMut(&mut T) -> bool, +{ + pub(super) vec: &'a mut Vec, /// The index of the item that will be inspected by the next call to `next`. - idx: usize, - /// Elements at and beyond this point will be retained. Must be equal or smaller than `old_len`. - end: usize, + pub(super) idx: usize, /// The number of items that have been drained (removed) thus far. - del: usize, + pub(super) del: usize, /// The original length of `vec` prior to draining. - old_len: usize, + pub(super) old_len: usize, /// The filter test predicate. - pred: F, + pub(super) pred: F, } -impl<'a, T, F, A: Allocator> ExtractIf<'a, T, F, A> { - pub(super) fn new>(vec: &'a mut Vec, pred: F, range: R) -> Self { - let old_len = vec.len(); - let Range { start, end } = slice::range(range, ..old_len); - - // Guard against the vec getting leaked (leak amplification) - unsafe { - vec.set_len(0); - } - ExtractIf { vec, idx: start, del: 0, end, old_len, pred } - } - +impl ExtractIf<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ /// Returns a reference to the underlying allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] @@ -68,7 +59,7 @@ where fn next(&mut self) -> Option { unsafe { - while self.idx < self.end { + while self.idx < self.old_len { let i = self.idx; let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len); let drained = (self.pred)(&mut v[i]); @@ -91,15 +82,24 @@ where } fn size_hint(&self) -> (usize, Option) { - (0, Some(self.end - self.idx)) + (0, Some(self.old_len - self.idx)) } } #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] -impl Drop for ExtractIf<'_, T, F, A> { +impl Drop for ExtractIf<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ fn drop(&mut self) { unsafe { if self.idx < self.old_len && self.del > 0 { + // This is a pretty messed up state, and there isn't really an + // obviously right thing to do. We don't want to keep trying + // to execute `pred`, so we just backshift all the unprocessed + // elements and tell the vec that they still exist. The backshift + // is required to prevent a double-drop of the last successfully + // drained item prior to a panic in the predicate. let ptr = self.vec.as_mut_ptr(); let src = ptr.add(self.idx); let dst = src.sub(self.del); diff --git a/alloc/src/vec/is_zero.rs b/alloc/src/vec/is_zero.rs index a3ddd6f6e230e..ba57d940d8c99 100644 --- a/alloc/src/vec/is_zero.rs +++ b/alloc/src/vec/is_zero.rs @@ -40,8 +40,19 @@ impl_is_zero!(char, |x| x == '\0'); impl_is_zero!(f32, |x: f32| x.to_bits() == 0); impl_is_zero!(f64, |x: f64| x.to_bits() == 0); -// `IsZero` cannot be soundly implemented for pointers because of provenance -// (see #135338). +unsafe impl IsZero for *const T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} + +unsafe impl IsZero for *mut T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} unsafe impl IsZero for [T; N] { #[inline] diff --git a/alloc/src/vec/mod.rs b/alloc/src/vec/mod.rs index 48afcf6e0645b..990b7e8f76127 100644 --- a/alloc/src/vec/mod.rs +++ b/alloc/src/vec/mod.rs @@ -56,6 +56,7 @@ #[cfg(not(no_global_oom_handling))] use core::cmp; use core::cmp::Ordering; +use core::fmt; use core::hash::{Hash, Hasher}; #[cfg(not(no_global_oom_handling))] use core::iter; @@ -64,7 +65,6 @@ use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::ops::{self, Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice::{self, SliceIndex}; -use core::{fmt, intrinsics}; #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] pub use self::extract_if::ExtractIf; @@ -427,7 +427,7 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the vector will not allocate. + /// `capacity`. If `capacity` is 0, the vector will not allocate. /// /// It is important to note that although the returned vector has the /// minimum *capacity* specified, the vector will have a zero *length*. For @@ -487,7 +487,7 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the vector will not allocate. + /// `capacity`. If `capacity` is 0, the vector will not allocate. /// /// # Errors /// @@ -745,7 +745,7 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the vector will not allocate. + /// `capacity`. If `capacity` is 0, the vector will not allocate. /// /// It is important to note that although the returned vector has the /// minimum *capacity* specified, the vector will have a zero *length*. For @@ -808,7 +808,7 @@ impl Vec { /// /// The vector will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the vector will not allocate. + /// `capacity`. If `capacity` is 0, the vector will not allocate. /// /// # Errors /// @@ -1267,7 +1267,6 @@ impl Vec { #[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); } @@ -1663,7 +1662,7 @@ impl Vec { #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_never_returns_null_ptr] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[inline] pub const fn as_ptr(&self) -> *const T { // We shadow the slice method of the same name to avoid going through @@ -1726,7 +1725,7 @@ impl Vec { #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_never_returns_null_ptr] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[inline] pub const fn as_mut_ptr(&mut self) -> *mut T { // We shadow the slice method of the same name to avoid going through @@ -1825,10 +1824,7 @@ impl Vec { /// /// # Examples /// - /// See [`spare_capacity_mut()`] for an example with safe - /// initialization of capacity elements and use of this method. - /// - /// `set_len()` can be useful for situations in which the vector + /// This method can be useful for situations in which the vector /// is serving as a buffer for other code, particularly over FFI: /// /// ```no_run @@ -1888,8 +1884,6 @@ impl Vec { /// /// Normally, here, one would use [`clear`] instead to correctly drop /// the contents and thus not leak memory. - /// - /// [`spare_capacity_mut()`]: Vec::spare_capacity_mut #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn set_len(&mut self, new_len: usize) { @@ -1959,11 +1953,11 @@ impl Vec { /// # Examples /// /// ``` - /// let mut vec = vec!['a', 'b', 'c']; - /// vec.insert(1, 'd'); - /// assert_eq!(vec, ['a', 'd', 'b', 'c']); - /// vec.insert(4, 'e'); - /// assert_eq!(vec, ['a', 'd', 'b', 'c', 'e']); + /// let mut vec = vec![1, 2, 3]; + /// vec.insert(1, 4); + /// assert_eq!(vec, [1, 4, 2, 3]); + /// vec.insert(4, 5); + /// assert_eq!(vec, [1, 4, 2, 3, 5]); /// ``` /// /// # Time complexity @@ -2030,9 +2024,9 @@ impl Vec { /// # Examples /// /// ``` - /// let mut v = vec!['a', 'b', 'c']; - /// assert_eq!(v.remove(1), 'b'); - /// assert_eq!(v, ['a', 'c']); + /// let mut v = vec![1, 2, 3]; + /// assert_eq!(v.remove(1), 2); + /// assert_eq!(v, [1, 3]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] @@ -2512,9 +2506,9 @@ impl Vec { } } - /// Removes and returns the last element from a vector if the predicate + /// Removes and returns the last element in a vector if the predicate /// returns `true`, or [`None`] if the predicate returns false or the vector - /// is empty (the predicate will not be called in that case). + /// is empty. /// /// # Examples /// @@ -2529,9 +2523,12 @@ impl Vec { /// assert_eq!(vec.pop_if(pred), None); /// ``` #[unstable(feature = "vec_pop_if", issue = "122741")] - pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { + pub fn pop_if(&mut self, f: F) -> Option + where + F: FnOnce(&mut T) -> bool, + { let last = self.last_mut()?; - if predicate(last) { self.pop() } else { None } + if f(last) { self.pop() } else { None } } /// Moves all the elements of `other` into `self`, leaving `other` empty. @@ -2572,11 +2569,9 @@ impl Vec { self.len += count; } - /// 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. + /// 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. /// /// The returned iterator keeps a mutable borrow on the vector to optimize /// its implementation. @@ -2680,14 +2675,7 @@ impl Vec { #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_confusables("length", "size")] pub const fn len(&self) -> usize { - let len = self.len; - - // SAFETY: The maximum capacity of `Vec` is `isize::MAX` bytes, so the maximum value can - // be returned is `usize::checked_div(mem::size_of::()).unwrap_or(usize::MAX)`, which - // matches the definition of `T::MAX_SLICE_LEN`. - unsafe { intrinsics::assume(len <= T::MAX_SLICE_LEN) }; - - len + self.len } /// Returns `true` if the vector contains no elements. @@ -2727,10 +2715,10 @@ impl Vec { /// # Examples /// /// ``` - /// let mut vec = vec!['a', 'b', 'c']; + /// let mut vec = vec![1, 2, 3]; /// let vec2 = vec.split_off(1); - /// assert_eq!(vec, ['a']); - /// assert_eq!(vec2, ['b', 'c']); + /// assert_eq!(vec, [1]); + /// assert_eq!(vec2, [2, 3]); /// ``` #[cfg(not(no_global_oom_handling))] #[inline] @@ -2994,9 +2982,9 @@ impl Vec { /// vec.resize(3, "world"); /// assert_eq!(vec, ["hello", "world", "world"]); /// - /// let mut vec = vec!['a', 'b', 'c', 'd']; - /// vec.resize(2, '_'); - /// assert_eq!(vec, ['a', 'b']); + /// let mut vec = vec![1, 2, 3, 4]; + /// vec.resize(2, 0); + /// assert_eq!(vec, [1, 2]); /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_resize", since = "1.5.0")] @@ -3016,9 +3004,10 @@ impl Vec { /// 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 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. + /// 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). /// /// # Examples /// @@ -3036,29 +3025,26 @@ impl Vec { self.spec_extend(other.iter()) } - /// Given a range `src`, clones a slice of elements in that range and appends it to the end. - /// - /// `src` must be a range that can form a valid subslice of the `Vec`. + /// Copies elements from `src` range to the end of the vector. /// /// # Panics /// - /// Panics if starting index is greater than the end index - /// or if the index is greater than the length of the vector. + /// Panics if the starting point is greater than the end point or if + /// the end point is greater than the length of the vector. /// /// # Examples /// /// ``` - /// let mut characters = vec!['a', 'b', 'c', 'd', 'e']; - /// characters.extend_from_within(2..); - /// assert_eq!(characters, ['a', 'b', 'c', 'd', 'e', 'c', 'd', 'e']); + /// let mut vec = vec![0, 1, 2, 3, 4]; /// - /// let mut numbers = vec![0, 1, 2, 3, 4]; - /// numbers.extend_from_within(..2); - /// assert_eq!(numbers, [0, 1, 2, 3, 4, 0, 1]); + /// vec.extend_from_within(2..); + /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4]); /// - /// let mut strings = vec![String::from("hello"), String::from("world"), String::from("!")]; - /// strings.extend_from_within(1..=2); - /// assert_eq!(strings, ["hello", "world", "!", "world", "!"]); + /// vec.extend_from_within(..2); + /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1]); + /// + /// vec.extend_from_within(4..8); + /// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]); /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_extend_from_within", since = "1.53.0")] @@ -3586,7 +3572,7 @@ impl Vec { /// with the given `replace_with` iterator and yields the removed items. /// `replace_with` does not need to be the same length as `range`. /// - /// `range` is removed even if the `Splice` iterator is not consumed before it is dropped. + /// `range` is removed even if the iterator is not consumed until the end. /// /// It is unspecified how many elements are removed from the vector /// if the `Splice` value is leaked. @@ -3612,18 +3598,8 @@ impl Vec { /// let mut v = vec![1, 2, 3, 4]; /// let new = [7, 8, 9]; /// let u: Vec<_> = v.splice(1..3, new).collect(); - /// assert_eq!(v, [1, 7, 8, 9, 4]); - /// assert_eq!(u, [2, 3]); - /// ``` - /// - /// Using `splice` to insert new items into a vector efficiently at a specific position - /// indicated by an empty range: - /// - /// ``` - /// let mut v = vec![1, 5]; - /// let new = [2, 3, 4]; - /// v.splice(1..1, new); - /// assert_eq!(v, [1, 2, 3, 4, 5]); + /// assert_eq!(v, &[1, 7, 8, 9, 4]); + /// assert_eq!(u, &[2, 3]); /// ``` #[cfg(not(no_global_oom_handling))] #[inline] @@ -3636,15 +3612,12 @@ impl Vec { Splice { drain: self.drain(range), replace_with: replace_with.into_iter() } } - /// Creates an iterator which uses a closure to determine if element in the range should be removed. + /// Creates an iterator which uses a closure to determine if an element should be removed. /// /// If the closure returns true, then the element is removed and yielded. /// If the closure returns false, the element will remain in the vector and will not be yielded /// by the iterator. /// - /// Only elements that fall in the provided range are considered for extraction, but any elements - /// after the range will still have to be moved if any element has been extracted. - /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. /// Use [`retain`] with a negated predicate if you do not need the returned iterator. @@ -3654,12 +3627,10 @@ impl Vec { /// Using this method is equivalent to the following code: /// /// ``` - /// # use std::cmp::min; /// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 }; /// # let mut vec = vec![1, 2, 3, 4, 5, 6]; - /// # let range = 1..4; - /// let mut i = range.start; - /// while i < min(vec.len(), range.end) { + /// let mut i = 0; + /// while i < vec.len() { /// if some_predicate(&mut vec[i]) { /// let val = vec.remove(i); /// // your code here @@ -3674,12 +3645,8 @@ impl Vec { /// But `extract_if` is easier to use. `extract_if` is also more efficient, /// because it can backshift the elements of the array in bulk. /// - /// Note that `extract_if` also lets you mutate the elements passed to the filter closure, - /// regardless of whether you choose to keep or remove them. - /// - /// # Panics - /// - /// If `range` is out of bounds. + /// Note that `extract_if` also lets you mutate every element in the filter closure, + /// regardless of whether you choose to keep or remove it. /// /// # Examples /// @@ -3689,29 +3656,25 @@ impl Vec { /// #![feature(extract_if)] /// let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]; /// - /// let evens = numbers.extract_if(.., |x| *x % 2 == 0).collect::>(); + /// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::>(); /// let odds = numbers; /// /// assert_eq!(evens, vec![2, 4, 6, 8, 14]); /// assert_eq!(odds, vec![1, 3, 5, 9, 11, 13, 15]); /// ``` - /// - /// Using the range argument to only process a part of the vector: - /// - /// ``` - /// #![feature(extract_if)] - /// let mut items = vec![0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 1, 2]; - /// let ones = items.extract_if(7.., |x| *x == 1).collect::>(); - /// assert_eq!(items, vec![0, 0, 0, 0, 0, 0, 0, 2, 2, 2]); - /// assert_eq!(ones.len(), 3); - /// ``` #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] - pub fn extract_if(&mut self, range: R, filter: F) -> ExtractIf<'_, T, F, A> + pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, - R: RangeBounds, { - ExtractIf::new(self, filter, range) + let old_len = self.len(); + + // Guard against us getting leaked (leak amplification) + unsafe { + self.set_len(0); + } + + ExtractIf { vec: self, idx: 0, del: 0, old_len, pred: filter } } } diff --git a/alloc/tests/boxed.rs b/alloc/tests/boxed.rs index 94389cf2de933..6a8ba5c92fb30 100644 --- a/alloc/tests/boxed.rs +++ b/alloc/tests/boxed.rs @@ -4,7 +4,7 @@ use core::mem::MaybeUninit; use core::ptr::NonNull; #[test] -#[expect(dangling_pointers_from_temporaries)] +#[cfg_attr(not(bootstrap), expect(dangling_pointers_from_temporaries))] fn uninitialized_zero_size_box() { assert_eq!( &*Box::<()>::new_uninit() as *const _, diff --git a/alloc/tests/collections/mod.rs b/alloc/tests/collections/mod.rs deleted file mode 100644 index e73f3aaef8c83..0000000000000 --- a/alloc/tests/collections/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod binary_heap; diff --git a/alloc/tests/lib.rs b/alloc/tests/lib.rs index 391ff04a4b8e4..699a8e6776e6d 100644 --- a/alloc/tests/lib.rs +++ b/alloc/tests/lib.rs @@ -4,11 +4,11 @@ #![feature(assert_matches)] #![feature(btree_extract_if)] #![feature(cow_is_borrowed)] +#![feature(const_heap)] +#![feature(const_try)] #![feature(core_intrinsics)] -#![feature(downcast_unchecked)] #![feature(extract_if)] #![feature(exact_size_is_empty)] -#![feature(hashmap_internals)] #![feature(linked_list_cursors)] #![feature(map_try_insert)] #![feature(pattern)] @@ -31,47 +31,37 @@ #![feature(const_str_from_utf8)] #![feature(panic_update_hook)] #![feature(pointer_is_aligned_to)] -#![feature(test)] #![feature(thin_box)] +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(drain_keep_rest)] #![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)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] -extern crate test; - use std::hash::{DefaultHasher, Hash, Hasher}; -mod alloc; mod arc; mod autotraits; mod borrow; mod boxed; mod btree_set_hash; mod c_str; -mod c_str2; -mod collections; mod const_fns; mod cow_str; mod fmt; mod heap; mod linked_list; -mod misc_tests; mod rc; mod slice; mod sort; mod str; mod string; -mod sync; mod task; -mod testing; mod thin_box; mod vec; mod vec_deque; @@ -82,18 +72,9 @@ fn hash(t: &T) -> u64 { s.finish() } -/// Copied from `std::test_helpers::test_rng`, since these tests rely on the -/// seed not being the same for every RNG invocation too. -fn test_rng() -> rand_xorshift::XorShiftRng { - use std::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = std::hash::RandomState::new().build_hasher(); - std::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - 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/alloc/tests/slice.rs b/alloc/tests/slice.rs index f990a41b679fa..9625e3d2b5e08 100644 --- a/alloc/tests/slice.rs +++ b/alloc/tests/slice.rs @@ -1414,6 +1414,7 @@ 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/alloc/tests/sort/tests.rs b/alloc/tests/sort/tests.rs index d321f8df51898..14e6013f965d8 100644 --- a/alloc/tests/sort/tests.rs +++ b/alloc/tests/sort/tests.rs @@ -11,14 +11,7 @@ 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]; -// 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")))] +#[cfg(not(miri))] 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, @@ -40,7 +33,7 @@ fn check_is_sorted(v: &mut [T]) { known_good_stable_sort::sort(known_good_sorted_vec.as_mut_slice()); if is_small_test { - eprintln!("Original: {:?}", v_orig); + eprintln!("Orginal: {:?}", v_orig); eprintln!("Expected: {:?}", known_good_sorted_vec); eprintln!("Got: {:?}", v); } else { diff --git a/alloc/tests/str.rs b/alloc/tests/str.rs index 23a0e5e525643..6f930ab08535c 100644 --- a/alloc/tests/str.rs +++ b/alloc/tests/str.rs @@ -1524,14 +1524,18 @@ 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] @@ -1974,88 +1978,73 @@ 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] @@ -2308,21 +2297,21 @@ fn utf8_chars() { assert_eq!(schs.len(), 4); assert_eq!(schs.iter().cloned().collect::(), 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/alloc/tests/string.rs b/alloc/tests/string.rs index d996c55f94660..1c8bff1564db2 100644 --- a/alloc/tests/string.rs +++ b/alloc/tests/string.rs @@ -154,28 +154,19 @@ 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/alloc/tests/testing/crash_test.rs b/alloc/tests/testing/crash_test.rs deleted file mode 100644 index 502fe6c10c6fd..0000000000000 --- a/alloc/tests/testing/crash_test.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::cmp::Ordering; -use std::fmt::Debug; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering::SeqCst; - -/// A blueprint for crash test dummy instances that monitor drops. -/// Some instances may be configured to panic at some point. -/// -/// 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 id: usize, - dropped: AtomicUsize, -} - -impl CrashTestDummy { - /// Creates a crash test dummy design. The `id` determines order and equality of instances. - pub fn new(id: usize) -> CrashTestDummy { - CrashTestDummy { id, dropped: AtomicUsize::new(0) } - } - - /// Creates an instance of a crash test dummy that records what events it experiences - /// and optionally panics. - pub fn spawn(&self, panic: Panic) -> Instance<'_> { - Instance { origin: self, panic } - } - - /// Returns how many times instances of the dummy have been dropped. - pub fn dropped(&self) -> usize { - self.dropped.load(SeqCst) - } -} - -#[derive(Debug)] -pub struct Instance<'a> { - origin: &'a CrashTestDummy, - panic: Panic, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Panic { - Never, - InDrop, -} - -impl Instance<'_> { - pub fn id(&self) -> usize { - self.origin.id - } -} - -impl Drop for Instance<'_> { - fn drop(&mut self) { - self.origin.dropped.fetch_add(1, SeqCst); - if self.panic == Panic::InDrop { - panic!("panic in `drop`"); - } - } -} - -impl PartialOrd for Instance<'_> { - fn partial_cmp(&self, other: &Self) -> Option { - self.id().partial_cmp(&other.id()) - } -} - -impl Ord for Instance<'_> { - fn cmp(&self, other: &Self) -> Ordering { - self.id().cmp(&other.id()) - } -} - -impl PartialEq for Instance<'_> { - fn eq(&self, other: &Self) -> bool { - self.id().eq(&other.id()) - } -} - -impl Eq for Instance<'_> {} diff --git a/alloc/tests/testing/mod.rs b/alloc/tests/testing/mod.rs deleted file mode 100644 index 0a3dd191dc891..0000000000000 --- a/alloc/tests/testing/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod crash_test; diff --git a/alloc/tests/vec.rs b/alloc/tests/vec.rs index fe1db56414e0c..0f27fdff3e182 100644 --- a/alloc/tests/vec.rs +++ b/alloc/tests/vec.rs @@ -1204,16 +1204,22 @@ fn test_from_iter_specialization_with_iterator_adapters() { #[test] fn test_in_place_specialization_step_up_down() { fn assert_in_place_trait(_: &T) {} - - let src = vec![0u8; 1024]; + let src = vec![[0u8; 4]; 256]; let srcptr = src.as_ptr(); - let src_bytes = src.capacity(); - let iter = src.into_iter().array_chunks::<4>(); + let src_cap = src.capacity(); + let iter = src.into_iter().flatten(); + assert_in_place_trait(&iter); + let sink = iter.collect::>(); + let sinkptr = sink.as_ptr(); + assert_eq!(srcptr as *const u8, sinkptr); + assert_eq!(src_cap * 4, sink.capacity()); + + let iter = sink.into_iter().array_chunks::<4>(); assert_in_place_trait(&iter); let sink = iter.collect::>(); let sinkptr = sink.as_ptr(); - assert_eq!(srcptr.addr(), sinkptr.addr()); - assert_eq!(src_bytes, sink.capacity() * 4); + assert_eq!(srcptr, sinkptr); + assert_eq!(src_cap, sink.capacity()); let mut src: Vec = Vec::with_capacity(17); let src_bytes = src.capacity(); @@ -1230,6 +1236,13 @@ fn test_in_place_specialization_step_up_down() { let sink: Vec<[u8; 2]> = iter.collect(); assert_eq!(sink.len(), 8); assert!(sink.capacity() <= 25); + + let src = vec![[0u8; 4]; 256]; + let srcptr = src.as_ptr(); + let iter = src.into_iter().flat_map(|a| a.into_iter().map(|b| b.wrapping_add(1))); + assert_in_place_trait(&iter); + let sink = iter.collect::>(); + assert_eq!(srcptr as *const u8, sink.as_ptr()); } #[test] @@ -1337,20 +1350,6 @@ fn test_collect_after_iterator_clone() { assert_eq!(v, [1, 1, 1, 1, 1]); assert!(v.len() <= v.capacity()); } - -// regression test for #135103, similar to the one above Flatten/FlatMap had an unsound InPlaceIterable -// implementation. -#[test] -fn test_flatten_clone() { - const S: String = String::new(); - - let v = vec![[S, "Hello World!".into()], [S, S]]; - let mut i = v.into_iter().flatten(); - let _ = i.next(); - let result: Vec = i.clone().collect(); - assert_eq!(result, ["Hello World!", "", ""]); -} - #[test] fn test_cow_from() { let borrowed: &[_] = &["borrowed", "(slice)"]; @@ -1415,7 +1414,7 @@ fn extract_if_empty() { let mut vec: Vec = vec![]; { - let mut iter = vec.extract_if(.., |_| true); + let mut iter = vec.extract_if(|_| true); assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); assert_eq!(iter.size_hint(), (0, Some(0))); @@ -1432,7 +1431,7 @@ fn extract_if_zst() { let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.extract_if(.., |_| true); + let mut iter = vec.extract_if(|_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -1455,7 +1454,7 @@ fn extract_if_false() { let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.extract_if(.., |_| false); + let mut iter = vec.extract_if(|_| false); assert_eq!(iter.size_hint(), (0, Some(initial_len))); for _ in iter.by_ref() { count += 1; @@ -1477,7 +1476,7 @@ fn extract_if_true() { let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.extract_if(.., |_| true); + let mut iter = vec.extract_if(|_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -1493,31 +1492,6 @@ fn extract_if_true() { assert_eq!(vec, vec![]); } -#[test] -fn extract_if_ranges() { - let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - - let mut count = 0; - let it = vec.extract_if(1..=3, |_| { - count += 1; - true - }); - assert_eq!(it.collect::>(), vec![1, 2, 3]); - assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); - assert_eq!(count, 3); - - let it = vec.extract_if(1..=3, |_| false); - assert_eq!(it.collect::>(), vec![]); - assert_eq!(vec, vec![0, 4, 5, 6, 7, 8, 9, 10]); -} - -#[test] -#[should_panic] -fn extract_if_out_of_bounds() { - let mut vec = vec![0, 1]; - let _ = vec.extract_if(5.., |_| true).for_each(drop); -} - #[test] fn extract_if_complex() { { @@ -1527,7 +1501,7 @@ fn extract_if_complex() { 39, ]; - let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); @@ -1541,7 +1515,7 @@ fn extract_if_complex() { 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39, ]; - let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); @@ -1554,7 +1528,7 @@ fn extract_if_complex() { let mut vec = vec![2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]; - let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); @@ -1566,7 +1540,7 @@ fn extract_if_complex() { // [xxxxxxxxxx+++++++++++] let mut vec = vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]; - let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); @@ -1578,7 +1552,7 @@ fn extract_if_complex() { // [+++++++++++xxxxxxxxxx] let mut vec = vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]; - let removed = vec.extract_if(.., |x| *x % 2 == 0).collect::>(); + let removed = vec.extract_if(|x| *x % 2 == 0).collect::>(); assert_eq!(removed.len(), 10); assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]); @@ -1587,7 +1561,9 @@ 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; @@ -1624,7 +1600,7 @@ fn extract_if_consumed_panic() { } c.index < 6 }; - let drain = data.extract_if(.., filter); + let drain = data.extract_if(filter); // NOTE: The ExtractIf is explicitly consumed drain.for_each(drop); @@ -1638,7 +1614,9 @@ 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; @@ -1675,7 +1653,7 @@ fn extract_if_unconsumed_panic() { } c.index < 6 }; - let _drain = data.extract_if(.., filter); + let _drain = data.extract_if(filter); // NOTE: The ExtractIf is dropped without being consumed }); @@ -1691,7 +1669,7 @@ fn extract_if_unconsumed_panic() { #[test] fn extract_if_unconsumed() { let mut vec = vec![1, 2, 3, 4]; - let drain = vec.extract_if(.., |&mut x| x % 2 != 0); + let drain = vec.extract_if(|&mut x| x % 2 != 0); drop(drain); assert_eq!(vec, [1, 2, 3, 4]); } @@ -2738,13 +2716,3 @@ fn max_swap_remove() { let mut v = vec![0]; v.swap_remove(usize::MAX); } - -// Regression test for #135338 -#[test] -fn vec_null_ptr_roundtrip() { - let ptr = std::ptr::from_ref(&42); - let zero = ptr.with_addr(0); - let roundtripped = vec![zero; 1].pop().unwrap(); - let new = roundtripped.with_addr(ptr.addr()); - unsafe { new.read() }; -} diff --git a/alloc/tests/vec_deque.rs b/alloc/tests/vec_deque.rs index 1b03c29e5bda1..4b8d3c735f72b 100644 --- a/alloc/tests/vec_deque.rs +++ b/alloc/tests/vec_deque.rs @@ -80,45 +80,6 @@ fn test_parameterized(a: T, b: T, c: T, d: T) { assert_eq!(deq[3].clone(), d.clone()); } -#[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::::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(); diff --git a/backtrace b/backtrace index f8cc6ac9acc4e..230570f2dac80 160000 --- a/backtrace +++ b/backtrace @@ -1 +1 @@ -Subproject commit f8cc6ac9acc4e663ecd96f9bcf1ff4542636d1b9 +Subproject commit 230570f2dac80a601f5c0b30da00cc9480bd35eb diff --git a/core/Cargo.toml b/core/Cargo.toml index b7c6db6c78dde..94f343d06705e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,6 +15,19 @@ 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 = [] @@ -30,7 +43,8 @@ check-cfg = [ 'cfg(bootstrap)', 'cfg(no_fp_fmt_parse)', 'cfg(stdarch_intel_sde)', - 'cfg(target_arch, values("xtensa"))', + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', # core use #[path] imports to portable-simd `core_simd` crate # and to stdarch `core_arch` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg diff --git a/coretests/benches/any.rs b/core/benches/any.rs similarity index 100% rename from coretests/benches/any.rs rename to core/benches/any.rs diff --git a/coretests/benches/array.rs b/core/benches/array.rs similarity index 100% rename from coretests/benches/array.rs rename to core/benches/array.rs diff --git a/coretests/benches/ascii.rs b/core/benches/ascii.rs similarity index 100% rename from coretests/benches/ascii.rs rename to core/benches/ascii.rs diff --git a/coretests/benches/ascii/is_ascii.rs b/core/benches/ascii/is_ascii.rs similarity index 60% rename from coretests/benches/ascii/is_ascii.rs rename to core/benches/ascii/is_ascii.rs index ced7084fb0e48..4b2920c5eb45f 100644 --- a/coretests/benches/ascii/is_ascii.rs +++ b/core/benches/ascii/is_ascii.rs @@ -10,12 +10,9 @@ macro_rules! benches { // Ensure we benchmark cases where the functions are called with strings // that are not perfectly aligned or have a length which is not a // multiple of size_of::() (or both) - benches!(mod unaligned_head_medium MEDIUM[1..] $($name $arg $body)+); - benches!(mod unaligned_tail_medium MEDIUM[..(MEDIUM.len() - 1)] $($name $arg $body)+); - benches!(mod unaligned_both_medium MEDIUM[1..(MEDIUM.len() - 1)] $($name $arg $body)+); - benches!(mod unaligned_head_long LONG[1..] $($name $arg $body)+); - benches!(mod unaligned_tail_long LONG[..(LONG.len() - 1)] $($name $arg $body)+); - benches!(mod unaligned_both_long LONG[1..(LONG.len() - 1)] $($name $arg $body)+); + benches!(mod unaligned_head MEDIUM[1..] $($name $arg $body)+); + benches!(mod unaligned_tail MEDIUM[..(MEDIUM.len() - 1)] $($name $arg $body)+); + benches!(mod unaligned_both MEDIUM[1..(MEDIUM.len() - 1)] $($name $arg $body)+); }; (mod $mod_name: ident $input: ident [$range: expr] $($name: ident $arg: ident $body: block)+) => { @@ -52,44 +49,6 @@ benches! { fn case03_align_to_unrolled(bytes: &[u8]) { is_ascii_align_to_unrolled(bytes) } - - fn case04_while_loop(bytes: &[u8]) { - // Process chunks of 32 bytes at a time in the fast path to enable - // auto-vectorization and use of `pmovmskb`. Two 128-bit vector registers - // can be OR'd together and then the resulting vector can be tested for - // non-ASCII bytes. - const CHUNK_SIZE: usize = 32; - - let mut i = 0; - - while i + CHUNK_SIZE <= bytes.len() { - let chunk_end = i + CHUNK_SIZE; - - // Get LLVM to produce a `pmovmskb` instruction on x86-64 which - // creates a mask from the most significant bit of each byte. - // ASCII bytes are less than 128 (0x80), so their most significant - // bit is unset. - let mut count = 0; - while i < chunk_end { - count += bytes[i].is_ascii() as u8; - i += 1; - } - - // All bytes should be <= 127 so count is equal to chunk size. - if count != CHUNK_SIZE as u8 { - return false; - } - } - - // Process the remaining `bytes.len() % N` bytes. - let mut is_ascii = true; - while i < bytes.len() { - is_ascii &= bytes[i].is_ascii(); - i += 1; - } - - is_ascii - } } // These are separate since it's easier to debug errors if they don't go through diff --git a/coretests/benches/char/methods.rs b/core/benches/char/methods.rs similarity index 100% rename from coretests/benches/char/methods.rs rename to core/benches/char/methods.rs diff --git a/coretests/benches/char/mod.rs b/core/benches/char/mod.rs similarity index 100% rename from coretests/benches/char/mod.rs rename to core/benches/char/mod.rs diff --git a/coretests/benches/fmt.rs b/core/benches/fmt.rs similarity index 90% rename from coretests/benches/fmt.rs rename to core/benches/fmt.rs index ee8e981b46b97..ed478b0f1e055 100644 --- a/coretests/benches/fmt.rs +++ b/core/benches/fmt.rs @@ -124,41 +124,42 @@ fn write_str_macro_debug_ascii(bh: &mut Bencher) { #[bench] fn write_u128_max(bh: &mut Bencher) { bh.iter(|| { - black_box(format!("{}", black_box(u128::MAX))); + test::black_box(format!("{}", u128::MAX)); }); } #[bench] fn write_u128_min(bh: &mut Bencher) { bh.iter(|| { - black_box(format!("{}", black_box(u128::MIN))); + let s = format!("{}", 0u128); + test::black_box(s); }); } #[bench] fn write_u64_max(bh: &mut Bencher) { bh.iter(|| { - black_box(format!("{}", black_box(u64::MAX))); + test::black_box(format!("{}", u64::MAX)); }); } #[bench] fn write_u64_min(bh: &mut Bencher) { bh.iter(|| { - black_box(format!("{}", black_box(u64::MIN))); + test::black_box(format!("{}", 0u64)); }); } #[bench] fn write_u8_max(bh: &mut Bencher) { bh.iter(|| { - black_box(format!("{}", black_box(u8::MAX))); + test::black_box(format!("{}", u8::MAX)); }); } #[bench] fn write_u8_min(bh: &mut Bencher) { bh.iter(|| { - black_box(format!("{}", black_box(u8::MIN))); + test::black_box(format!("{}", 0u8)); }); } diff --git a/coretests/benches/hash/mod.rs b/core/benches/hash/mod.rs similarity index 100% rename from coretests/benches/hash/mod.rs rename to core/benches/hash/mod.rs diff --git a/coretests/benches/hash/sip.rs b/core/benches/hash/sip.rs similarity index 100% rename from coretests/benches/hash/sip.rs rename to core/benches/hash/sip.rs diff --git a/coretests/benches/iter.rs b/core/benches/iter.rs similarity index 100% rename from coretests/benches/iter.rs rename to core/benches/iter.rs diff --git a/coretests/benches/lib.rs b/core/benches/lib.rs similarity index 100% rename from coretests/benches/lib.rs rename to core/benches/lib.rs diff --git a/coretests/benches/net/addr_parser.rs b/core/benches/net/addr_parser.rs similarity index 100% rename from coretests/benches/net/addr_parser.rs rename to core/benches/net/addr_parser.rs diff --git a/coretests/benches/net/mod.rs b/core/benches/net/mod.rs similarity index 100% rename from coretests/benches/net/mod.rs rename to core/benches/net/mod.rs diff --git a/coretests/benches/num/dec2flt/mod.rs b/core/benches/num/dec2flt/mod.rs similarity index 100% rename from coretests/benches/num/dec2flt/mod.rs rename to core/benches/num/dec2flt/mod.rs diff --git a/coretests/benches/num/flt2dec/mod.rs b/core/benches/num/flt2dec/mod.rs similarity index 100% rename from coretests/benches/num/flt2dec/mod.rs rename to core/benches/num/flt2dec/mod.rs diff --git a/coretests/benches/num/flt2dec/strategy/dragon.rs b/core/benches/num/flt2dec/strategy/dragon.rs similarity index 100% rename from coretests/benches/num/flt2dec/strategy/dragon.rs rename to core/benches/num/flt2dec/strategy/dragon.rs diff --git a/coretests/benches/num/flt2dec/strategy/grisu.rs b/core/benches/num/flt2dec/strategy/grisu.rs similarity index 100% rename from coretests/benches/num/flt2dec/strategy/grisu.rs rename to core/benches/num/flt2dec/strategy/grisu.rs diff --git a/coretests/benches/num/int_log/mod.rs b/core/benches/num/int_log/mod.rs similarity index 100% rename from coretests/benches/num/int_log/mod.rs rename to core/benches/num/int_log/mod.rs diff --git a/coretests/benches/num/int_pow/mod.rs b/core/benches/num/int_pow/mod.rs similarity index 98% rename from coretests/benches/num/int_pow/mod.rs rename to core/benches/num/int_pow/mod.rs index 46f47028d56e6..6cf9021358283 100644 --- a/coretests/benches/num/int_pow/mod.rs +++ b/core/benches/num/int_pow/mod.rs @@ -25,7 +25,7 @@ macro_rules! pow_bench_template { let mut exp_iter = black_box(&exp_array).into_iter(); (0..ITERATIONS).fold((0 as IntType, false), |acc, _| { - // Sometimes constants don't propagate all the way to the + // Sometimes constants don't propogate all the way to the // inside of the loop, so we call a custom expression every cycle // rather than iter::repeat(CONST) let base: IntType = $base_macro!(base_iter); diff --git a/coretests/benches/num/int_sqrt/mod.rs b/core/benches/num/int_sqrt/mod.rs similarity index 100% rename from coretests/benches/num/int_sqrt/mod.rs rename to core/benches/num/int_sqrt/mod.rs diff --git a/coretests/benches/num/mod.rs b/core/benches/num/mod.rs similarity index 100% rename from coretests/benches/num/mod.rs rename to core/benches/num/mod.rs diff --git a/coretests/benches/ops.rs b/core/benches/ops.rs similarity index 100% rename from coretests/benches/ops.rs rename to core/benches/ops.rs diff --git a/coretests/benches/pattern.rs b/core/benches/pattern.rs similarity index 100% rename from coretests/benches/pattern.rs rename to core/benches/pattern.rs diff --git a/coretests/benches/slice.rs b/core/benches/slice.rs similarity index 100% rename from coretests/benches/slice.rs rename to core/benches/slice.rs diff --git a/coretests/benches/str.rs b/core/benches/str.rs similarity index 100% rename from coretests/benches/str.rs rename to core/benches/str.rs diff --git a/coretests/benches/str/char_count.rs b/core/benches/str/char_count.rs similarity index 100% rename from coretests/benches/str/char_count.rs rename to core/benches/str/char_count.rs diff --git a/coretests/benches/str/corpora.rs b/core/benches/str/corpora.rs similarity index 100% rename from coretests/benches/str/corpora.rs rename to core/benches/str/corpora.rs diff --git a/coretests/benches/str/debug.rs b/core/benches/str/debug.rs similarity index 100% rename from coretests/benches/str/debug.rs rename to core/benches/str/debug.rs diff --git a/coretests/benches/str/iter.rs b/core/benches/str/iter.rs similarity index 100% rename from coretests/benches/str/iter.rs rename to core/benches/str/iter.rs diff --git a/coretests/benches/tuple.rs b/core/benches/tuple.rs similarity index 100% rename from coretests/benches/tuple.rs rename to core/benches/tuple.rs diff --git a/core/src/alloc/layout.rs b/core/src/alloc/layout.rs index 17f4d68867e1e..f412ca1716338 100644 --- a/core/src/alloc/layout.rs +++ b/core/src/alloc/layout.rs @@ -157,6 +157,7 @@ impl Layout { #[must_use = "this returns the minimum alignment, \ without modifying the layout"] #[inline] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(ptr_alignment_type))] pub const fn align(&self) -> usize { self.align.as_usize() } @@ -178,7 +179,7 @@ impl Layout { /// allocate backing structure for `T` (which could be a trait /// or other unsized type like a slice). #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] #[must_use] #[inline] pub const fn for_value(t: &T) -> Self { @@ -215,6 +216,7 @@ impl Layout { /// [trait object]: ../../book/ch17-02-trait-objects.html /// [extern type]: ../../unstable-book/language-features/extern-types.html #[unstable(feature = "layout_for_ptr", issue = "69835")] + #[rustc_const_unstable(feature = "layout_for_ptr", issue = "69835")] #[must_use] pub const unsafe fn for_value_raw(t: *const T) -> Self { // SAFETY: we pass along the prerequisites of these functions to the caller @@ -233,7 +235,8 @@ impl Layout { #[must_use] #[inline] pub const fn dangling(&self) -> NonNull { - NonNull::without_provenance(self.align.as_nonzero()) + // SAFETY: align is guaranteed to be non-zero + unsafe { NonNull::new_unchecked(crate::ptr::without_provenance_mut::(self.align())) } } /// Creates a layout describing the record that can hold a value @@ -251,7 +254,8 @@ impl Layout { /// Returns an error if the combination of `self.size()` and the given /// `align` violates the conditions listed in [`Layout::from_size_align`]. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] #[inline] pub const fn align_to(&self, align: usize) -> Result { if let Some(align) = Alignment::new(align) { @@ -326,7 +330,8 @@ impl Layout { /// This is equivalent to adding the result of `padding_needed_for` /// to the layout's current size. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] #[must_use = "this returns a new `Layout`, \ without modifying the original"] #[inline] @@ -425,7 +430,8 @@ impl Layout { /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16]))); /// ``` #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] #[inline] pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> { let new_align = Alignment::max(self.align, next.align); @@ -488,7 +494,8 @@ impl Layout { /// On arithmetic overflow or when the total size would exceed /// `isize::MAX`, returns `LayoutError`. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] + #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] #[inline] pub const fn array(n: usize) -> Result { // Reduce the amount of code we need to monomorphize per `T`. diff --git a/core/src/alloc/mod.rs b/core/src/alloc/mod.rs index dcab6136ae8a1..aa841db045ce7 100644 --- a/core/src/alloc/mod.rs +++ b/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. -/// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the +/// `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 /// allocated memory. /// -/// 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. +/// 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. /// /// ### Currently allocated memory /// -/// 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. +/// Some of the methods require that a memory block be *currently allocated* via an allocator. This +/// means that: /// -/// 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. +/// * 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. /// /// [`allocate`]: Allocator::allocate /// [`grow`]: Allocator::grow @@ -77,28 +77,32 @@ impl fmt::Display for AllocError { /// /// ### Memory fitting /// -/// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the +/// 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 /// following conditions must hold: -/// * 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`]. +/// +/// * 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`]. /// /// [`layout.align()`]: Layout::align /// [`layout.size()`]: Layout::size /// /// # Safety /// -/// 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. +/// * 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. /// -/// 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. +/// * 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 /// -/// A memory block which is [*currently allocated*] may be passed to -/// any method of the allocator that accepts such an argument. +/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other +/// method of the allocator. /// /// [*currently allocated*]: #currently-allocated-memory #[unstable(feature = "allocator_api", issue = "32838")] diff --git a/core/src/any.rs b/core/src/any.rs index 17d9455592787..58107b1e7d074 100644 --- a/core/src/any.rs +++ b/core/src/any.rs @@ -423,8 +423,7 @@ impl dyn Any + Send { /// /// # Safety /// - /// The contained value must be of type `T`. Calling this method - /// with the incorrect type is *undefined behavior*. + /// Same as the method on the type `dyn Any`. #[unstable(feature = "downcast_unchecked", issue = "90850")] #[inline] pub unsafe fn downcast_ref_unchecked(&self) -> &T { @@ -452,8 +451,7 @@ impl dyn Any + Send { /// /// # Safety /// - /// The contained value must be of type `T`. Calling this method - /// with the incorrect type is *undefined behavior*. + /// Same as the method on the type `dyn Any`. #[unstable(feature = "downcast_unchecked", issue = "90850")] #[inline] pub unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { @@ -554,10 +552,6 @@ impl dyn Any + Send + Sync { /// assert_eq!(*x.downcast_ref_unchecked::(), 1); /// } /// ``` - /// # Safety - /// - /// The contained value must be of type `T`. Calling this method - /// with the incorrect type is *undefined behavior*. #[unstable(feature = "downcast_unchecked", issue = "90850")] #[inline] pub unsafe fn downcast_ref_unchecked(&self) -> &T { @@ -582,10 +576,6 @@ impl dyn Any + Send + Sync { /// /// assert_eq!(*x.downcast_ref::().unwrap(), 2); /// ``` - /// # Safety - /// - /// The contained value must be of type `T`. Calling this method - /// with the incorrect type is *undefined behavior*. #[unstable(feature = "downcast_unchecked", issue = "90850")] #[inline] pub unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { diff --git a/core/src/arch.rs b/core/src/arch.rs index 81d828a971c80..57f456c98b3c6 100644 --- a/core/src/arch.rs +++ b/core/src/arch.rs @@ -1,14 +1,6 @@ #![doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")] -#[allow( - // some targets don't have anything to reexport, which - // makes the `pub use` unused and unreachable, allow - // both lints as to not have `#[cfg]`s - // - // cf. https://github.com/rust-lang/rust/pull/116033#issuecomment-1760085575 - unused_imports, - unreachable_pub -)] +#[allow(unused_imports)] #[stable(feature = "simd_arch", since = "1.27.0")] pub use crate::core_arch::arch::*; @@ -50,29 +42,3 @@ pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { /* compiler built-in */ } - -/// Compiles to a target-specific software breakpoint instruction or equivalent. -/// -/// This will typically abort the program. It may result in a core dump, and/or the system logging -/// debug information. Additional target-specific capabilities may be possible depending on -/// debuggers or other tooling; in particular, a debugger may be able to resume execution. -/// -/// If possible, this will produce an instruction sequence that allows a debugger to resume *after* -/// the breakpoint, rather than resuming *at* the breakpoint; however, the exact behavior is -/// target-specific and debugger-specific, and not guaranteed. -/// -/// If the target platform does not have any kind of debug breakpoint instruction, this may compile -/// to a trapping instruction (e.g. an undefined instruction) instead, or to some other form of -/// target-specific abort that may or may not support convenient resumption. -/// -/// The precise behavior and the precise instruction generated are not guaranteed, except that in -/// normal execution with no debug tooling involved this will not continue executing. -/// -/// - On x86 targets, this produces an `int3` instruction. -/// - On aarch64 targets, this produces a `brk #0xf000` instruction. -// When stabilizing this, update the comment on `core::intrinsics::breakpoint`. -#[unstable(feature = "breakpoint", issue = "133724")] -#[inline(always)] -pub fn breakpoint() { - core::intrinsics::breakpoint(); -} diff --git a/core/src/array/iter.rs b/core/src/array/iter.rs index 1edade41597f7..9ce0eb61e0814 100644 --- a/core/src/array/iter.rs +++ b/core/src/array/iter.rs @@ -214,7 +214,7 @@ impl IntoIter { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { let slice = self.data.get_unchecked(self.alive.clone()); - slice.assume_init_ref() + MaybeUninit::slice_assume_init_ref(slice) } } @@ -224,7 +224,7 @@ impl IntoIter { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { let slice = self.data.get_unchecked_mut(self.alive.clone()); - slice.assume_init_mut() + MaybeUninit::slice_assume_init_mut(slice) } } } @@ -285,7 +285,7 @@ impl Iterator for IntoIter { // SAFETY: These elements are currently initialized, so it's fine to drop them. unsafe { let slice = self.data.get_unchecked_mut(range_to_drop); - slice.assume_init_drop(); + ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice)); } NonZero::new(remaining).map_or(Ok(()), Err) @@ -340,7 +340,7 @@ impl DoubleEndedIterator for IntoIter { // SAFETY: These elements are currently initialized, so it's fine to drop them. unsafe { let slice = self.data.get_unchecked_mut(range_to_drop); - slice.assume_init_drop(); + ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice)); } NonZero::new(remaining).map_or(Ok(()), Err) diff --git a/core/src/array/mod.rs b/core/src/array/mod.rs index 28329bb090845..67fbda34bb935 100644 --- a/core/src/array/mod.rs +++ b/core/src/array/mod.rs @@ -156,6 +156,7 @@ pub const fn from_mut(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(()); @@ -213,8 +214,8 @@ impl BorrowMut<[T]> for [T; N] { } } -/// Tries to create an array `[T; N]` by copying from a slice `&[T]`. -/// Succeeds if `slice.len() == N`. +/// Tries to create an array `[T; N]` by copying from a slice `&[T]`. Succeeds if +/// `slice.len() == N`. /// /// ``` /// let bytes: [u8; 3] = [1, 0, 2]; @@ -281,7 +282,13 @@ impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a [T; N] { #[inline] fn try_from(slice: &'a [T]) -> Result<&'a [T; N], TryFromSliceError> { - slice.as_array().ok_or(TryFromSliceError(())) + if slice.len() == N { + let ptr = slice.as_ptr() as *const [T; N]; + // SAFETY: ok because we just checked that the length fits + unsafe { Ok(&*ptr) } + } else { + Err(TryFromSliceError(())) + } } } @@ -303,7 +310,13 @@ impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] { #[inline] fn try_from(slice: &'a mut [T]) -> Result<&'a mut [T; N], TryFromSliceError> { - slice.as_mut_array().ok_or(TryFromSliceError(())) + if slice.len() == N { + let ptr = slice.as_mut_ptr() as *mut [T; N]; + // SAFETY: ok because we just checked that the length fits + unsafe { Ok(&mut *ptr) } + } else { + Err(TryFromSliceError(())) + } } } @@ -892,7 +905,7 @@ impl Guard<'_, T> { /// /// No more than N elements must be initialized. #[inline] - pub(crate) unsafe fn push_unchecked(&mut self, item: T) { + pub unsafe fn push_unchecked(&mut self, item: T) { // SAFETY: If `initialized` was correct before and the caller does not // invoke this method more than N times then writes will be in-bounds // and slots will not be initialized more than once. @@ -910,7 +923,9 @@ impl Drop for Guard<'_, T> { // SAFETY: this slice will contain only initialized objects. unsafe { - self.array_mut.get_unchecked_mut(..self.initialized).assume_init_drop(); + crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( + self.array_mut.get_unchecked_mut(..self.initialized), + )); } } } diff --git a/core/src/bool.rs b/core/src/bool.rs index 3c589ca5dfa7e..58a870d2e0725 100644 --- a/core/src/bool.rs +++ b/core/src/bool.rs @@ -54,59 +54,10 @@ impl bool { /// // `then`. /// assert_eq!(a, 1); /// ``` - #[doc(alias = "then_with")] #[stable(feature = "lazy_bool_to_option", since = "1.50.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "bool_then")] #[inline] pub fn then T>(self, f: F) -> Option { if self { Some(f()) } else { None } } - - /// Returns either `true_val` or `false_val` depending on the value of - /// `self`, with a hint to the compiler that `self` is unlikely - /// to be correctly predicted by a CPU’s branch predictor. - /// - /// This method is functionally equivalent to - /// ```ignore (this is just for illustrative purposes) - /// fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { - /// if b { true_val } else { false_val } - /// } - /// ``` - /// but might generate different assembly. In particular, on platforms with - /// a conditional move or select instruction (like `cmov` on x86 or `csel` - /// on ARM) the optimizer might use these instructions to avoid branches, - /// which can benefit performance if the branch predictor is struggling - /// with predicting `condition`, such as in an implementation of binary - /// search. - /// - /// Note however that this lowering is not guaranteed (on any platform) and - /// should not be relied upon when trying to write constant-time code. Also - /// be aware that this lowering might *decrease* performance if `condition` - /// is well-predictable. It is advisable to perform benchmarks to tell if - /// this function is useful. - /// - /// # Examples - /// - /// Distribute values evenly between two buckets: - /// ``` - /// #![feature(select_unpredictable)] - /// - /// use std::hash::BuildHasher; - /// - /// fn append(hasher: &H, v: i32, bucket_one: &mut Vec, bucket_two: &mut Vec) { - /// let hash = hasher.hash_one(&v); - /// let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two); - /// bucket.push(v); - /// } - /// # let hasher = std::collections::hash_map::RandomState::new(); - /// # let mut bucket_one = Vec::new(); - /// # let mut bucket_two = Vec::new(); - /// # append(&hasher, 42, &mut bucket_one, &mut bucket_two); - /// # assert_eq!(bucket_one.len() + bucket_two.len(), 1); - /// ``` - #[inline(always)] - #[unstable(feature = "select_unpredictable", issue = "133962")] - pub fn select_unpredictable(self, true_val: T, false_val: T) -> T { - crate::intrinsics::select_unpredictable(self, true_val, false_val) - } } diff --git a/core/src/bstr.rs b/core/src/bstr.rs deleted file mode 100644 index 74e07f3d242cd..0000000000000 --- a/core/src/bstr.rs +++ /dev/null @@ -1,581 +0,0 @@ -//! 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>(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 for ByteStr { - #[inline] - fn as_ref(&self) -> &ByteStr { - self - } -} - -// `impl AsRef for [u8]` omitted to avoid widespread inference failures - -#[unstable(feature = "bstr", issue = "134915")] -impl AsRef 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 for [u8]` omitted to avoid widespread inference failures - -// `impl Borrow for [u8]` omitted to avoid widespread inference failures - -// `impl Borrow 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 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(&self, state: &mut H) { - self.0.hash(state); - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index for ByteStr { - type Output = u8; - - #[inline] - fn index(&self, idx: usize) -> &u8 { - &self.0[idx] - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, _: RangeFull) -> &ByteStr { - self - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: Range) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeInclusive) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeFrom) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeTo) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl Index> for ByteStr { - type Output = ByteStr; - - #[inline] - fn index(&self, r: RangeToInclusive) -> &ByteStr { - ByteStr::from_bytes(&self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut for ByteStr { - #[inline] - fn index_mut(&mut self, idx: usize) -> &mut u8 { - &mut self.0[idx] - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut for ByteStr { - #[inline] - fn index_mut(&mut self, _: RangeFull) -> &mut ByteStr { - self - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: Range) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: RangeInclusive) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: RangeFrom) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: RangeTo) -> &mut ByteStr { - ByteStr::from_bytes_mut(&mut self.0[r]) - } -} - -#[unstable(feature = "bstr", issue = "134915")] -impl IndexMut> for ByteStr { - #[inline] - fn index_mut(&mut self, r: RangeToInclusive) -> &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 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 { - 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 { - 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 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 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 { - 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 { - 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 { - crate::str::from_utf8_mut(&mut s.0) - } -} diff --git a/core/src/cell.rs b/core/src/cell.rs index cbf00106c5173..bfd2a71f97b2c 100644 --- a/core/src/cell.rs +++ b/core/src/cell.rs @@ -22,8 +22,8 @@ //! (mutable via `&T`), in contrast with typical Rust types that exhibit 'inherited mutability' //! (mutable only via `&mut T`). //! -//! Cell types come in four flavors: `Cell`, `RefCell`, `OnceCell`, and `LazyCell`. -//! Each provides a different way of providing safe interior mutability. +//! Cell types come in three flavors: `Cell`, `RefCell`, and `OnceCell`. Each provides +//! a different way of providing safe interior mutability. //! //! ## `Cell` //! @@ -252,7 +252,7 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; -use crate::marker::{PhantomData, PointerLike, Unsize}; +use crate::marker::{PhantomData, Unsize}; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; use crate::pin::PinCoerceUnsized; @@ -587,7 +587,7 @@ impl Cell { #[inline] #[stable(feature = "cell_as_ptr", since = "1.12.0")] #[rustc_const_stable(feature = "const_cell_as_ptr", since = "1.32.0")] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[rustc_never_returns_null_ptr] pub const fn as_ptr(&self) -> *mut T { self.value.get() @@ -677,9 +677,6 @@ impl, U> CoerceUnsized> for Cell {} #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U> DispatchFromDyn> for Cell {} -#[unstable(feature = "pointer_like_trait", issue = "none")] -impl PointerLike for Cell {} - impl Cell<[T]> { /// Returns a `&[Cell]` from a `&Cell<[T]>` /// @@ -716,6 +713,7 @@ impl Cell<[T; N]> { /// let array_cell: &[Cell; 3] = cell_array.as_array_of_cells(); /// ``` #[unstable(feature = "as_array_of_cells", issue = "88248")] + #[rustc_const_unstable(feature = "as_array_of_cells", issue = "88248")] pub const fn as_array_of_cells(&self) -> &[Cell; N] { // SAFETY: `Cell` has the same memory layout as `T`. unsafe { &*(self as *const Cell<[T; N]> as *const [Cell; N]) } @@ -1152,7 +1150,7 @@ impl RefCell { /// ``` #[inline] #[stable(feature = "cell_as_ptr", since = "1.12.0")] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[rustc_never_returns_null_ptr] pub fn as_ptr(&self) -> *mut T { self.value.get() @@ -1590,10 +1588,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,10 +1756,11 @@ 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. @@ -2117,35 +2116,6 @@ impl UnsafeCell { 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 UnsafeCell { @@ -2163,8 +2133,9 @@ impl UnsafeCell { /// assert_eq!(*uc.get_mut(), 41); /// ``` #[inline(always)] - #[stable(feature = "unsafe_cell_from_mut", since = "1.84.0")] - #[rustc_const_stable(feature = "unsafe_cell_from_mut", since = "1.84.0")] + #[stable(feature = "unsafe_cell_from_mut", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unsafe_cell_from_mut", since = "CURRENT_RUSTC_VERSION")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] pub const fn from_mut(value: &mut T) -> &mut UnsafeCell { // SAFETY: `UnsafeCell` has the same memory layout as `T` due to #[repr(transparent)]. unsafe { &mut *(value as *mut T as *mut UnsafeCell) } @@ -2189,7 +2160,7 @@ impl UnsafeCell { #[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_unsafecell_get", since = "1.32.0")] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[rustc_never_returns_null_ptr] pub const fn get(&self) -> *mut T { // We can just cast the pointer from `UnsafeCell` to `T` because of @@ -2258,61 +2229,6 @@ impl UnsafeCell { // 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")] @@ -2344,9 +2260,6 @@ impl, U> CoerceUnsized> for UnsafeCell {} #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl, U> DispatchFromDyn> for UnsafeCell {} -#[unstable(feature = "pointer_like_trait", issue = "none")] -impl PointerLike for UnsafeCell {} - /// [`UnsafeCell`], but [`Sync`]. /// /// This is just an `UnsafeCell`, except it implements `Sync` @@ -2395,7 +2308,7 @@ impl SyncUnsafeCell { /// when casting to `&mut T`, and ensure that there are no mutations /// or mutable aliases going on when casting to `&T` #[inline] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[rustc_never_returns_null_ptr] pub const fn get(&self) -> *mut T { self.value.get() @@ -2453,9 +2366,6 @@ impl, U> CoerceUnsized> for SyncUnsafeCell //#[unstable(feature = "sync_unsafe_cell", issue = "95439")] impl, U> DispatchFromDyn> for SyncUnsafeCell {} -#[unstable(feature = "pointer_like_trait", issue = "none")] -impl PointerLike for SyncUnsafeCell {} - #[allow(unused)] fn assert_coerce_unsized( a: UnsafeCell<&i32>, diff --git a/core/src/cell/lazy.rs b/core/src/cell/lazy.rs index 84cbbc71f40ae..5ac33516684d7 100644 --- a/core/src/cell/lazy.rs +++ b/core/src/cell/lazy.rs @@ -219,7 +219,7 @@ impl T> LazyCell { } impl LazyCell { - /// Returns a mutable reference to the value if initialized, or `None` if not. + /// Returns a reference to the value if initialized, or `None` if not. /// /// # Examples /// @@ -245,7 +245,7 @@ impl LazyCell { } } - /// Returns a reference to the value if initialized, or `None` if not. + /// Returns a mutable reference to the value if initialized, or `None` if not. /// /// # Examples /// diff --git a/core/src/cell/once.rs b/core/src/cell/once.rs index c6c96571d33c9..c14afe0f4761c 100644 --- a/core/src/cell/once.rs +++ b/core/src/cell/once.rs @@ -8,9 +8,6 @@ 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 @@ -38,7 +35,7 @@ pub struct OnceCell { } impl OnceCell { - /// Creates a new uninitialized cell. + /// Creates a new empty cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -49,7 +46,7 @@ impl OnceCell { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is uninitialized. + /// Returns `None` if the cell is empty. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -59,19 +56,19 @@ impl OnceCell { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is uninitialized. + /// Returns `None` if the cell is empty. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { self.inner.get_mut().as_mut() } - /// Initializes the contents of the cell to `value`. + /// Sets the contents of the cell to `value`. /// /// # Errors /// - /// This method returns `Ok(())` if the cell was uninitialized - /// and `Err(value)` if it was already initialized. + /// This method returns `Ok(())` if the cell was empty and `Err(value)` if + /// it was full. /// /// # Examples /// @@ -95,13 +92,13 @@ impl OnceCell { } } - /// Initializes the contents of the cell to `value` if the cell was - /// uninitialized, then returns a reference to it. + /// Sets the contents of the cell to `value` if the cell was empty, then + /// returns a reference to it. /// /// # Errors /// - /// This method returns `Ok(&value)` if the cell was uninitialized - /// and `Err((¤t_value, value))` if it was already initialized. + /// This method returns `Ok(&value)` if the cell was empty and + /// `Err(¤t_value, value)` if it was full. /// /// # Examples /// @@ -133,12 +130,12 @@ impl OnceCell { Ok(slot.insert(value)) } - /// Gets the contents of the cell, initializing it to `f()` - /// if the cell was uninitialized. + /// Gets the contents of the cell, initializing it with `f` + /// if the cell was empty. /// /// # 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 @@ -167,11 +164,11 @@ impl OnceCell { } /// Gets the mutable reference of the contents of the cell, - /// initializing it to `f()` if the cell was uninitialized. + /// initializing it with `f` if the cell was empty. /// /// # 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 @@ -202,13 +199,13 @@ impl OnceCell { } } - /// 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. + /// 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. /// /// # 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 @@ -242,12 +239,12 @@ impl OnceCell { } /// Gets the mutable reference of 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. + /// it with `f` if the cell was empty. If the cell was empty 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 @@ -259,15 +256,13 @@ impl OnceCell { /// /// let mut cell: OnceCell = OnceCell::new(); /// - /// // Failed attempts to initialize the cell do not change its contents + /// // Failed initializers do not change the value /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// /// let value = cell.get_mut_or_try_init(|| "1234".parse()); /// assert_eq!(value, Ok(&mut 1234)); - /// - /// let Ok(value) = value else { return; }; - /// *value += 2; + /// *value.unwrap() += 2; /// assert_eq!(cell.get(), Some(&1236)) /// ``` #[unstable(feature = "once_cell_get_mut", issue = "121641")] @@ -298,7 +293,7 @@ impl OnceCell { /// Consumes the cell, returning the wrapped value. /// - /// Returns `None` if the cell was uninitialized. + /// Returns `None` if the cell was empty. /// /// # Examples /// @@ -309,8 +304,8 @@ impl OnceCell { /// assert_eq!(cell.into_inner(), None); /// /// let cell = OnceCell::new(); - /// let _ = cell.set("hello".to_owned()); - /// assert_eq!(cell.into_inner(), Some("hello".to_owned())); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.into_inner(), Some("hello".to_string())); /// ``` #[inline] #[stable(feature = "once_cell", since = "1.70.0")] @@ -324,7 +319,7 @@ impl OnceCell { /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceCell` is uninitialized. + /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. /// /// Safety is guaranteed by requiring a mutable reference. /// @@ -337,8 +332,8 @@ impl OnceCell { /// assert_eq!(cell.take(), None); /// /// let mut cell = OnceCell::new(); - /// let _ = cell.set("hello".to_owned()); - /// assert_eq!(cell.take(), Some("hello".to_owned())); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.take(), Some("hello".to_string())); /// assert_eq!(cell.get(), None); /// ``` #[inline] diff --git a/core/src/char/methods.rs b/core/src/char/methods.rs index ccfdbf0eb704d..974e7baccf7bc 100644 --- a/core/src/char/methods.rs +++ b/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 native endian UTF-16 encoded code points in `iter`, + /// Creates an iterator over the UTF-16 encoded code points in `iter`, /// returning unpaired surrogates as `Err`s. /// /// # Examples @@ -394,21 +394,17 @@ impl char { ); // check radix to remove letter handling code when radix is a known constant let value = if self > '9' && radix > 10 { - // mask to convert ASCII letters to uppercase - const TO_UPPERCASE_MASK: u32 = !0b0010_0000; - // Converts an ASCII letter to its corresponding integer value: - // A-Z => 10-35, a-z => 10-35. Other characters produce values >= 36. - // - // Add Overflow Safety: - // By applying the mask after the subtraction, the first addendum is - // constrained such that it never exceeds u32::MAX - 0x20. - ((self as u32).wrapping_sub('A' as u32) & TO_UPPERCASE_MASK) + 10 + // convert ASCII letters to lowercase + let lower = self as u32 | 0x20; + // convert an ASCII letter to the corresponding value, + // non-letters convert to values > 36 + lower.wrapping_sub('a' as u32) as u64 + 10 } else { // convert digit to value, non-digits wrap to values > 36 - (self as u32).wrapping_sub('0' as u32) + (self as u32).wrapping_sub('0' as u32) as u64 }; // FIXME(const-hack): once then_some is const fn, use it here - if value < radix { Some(value) } else { None } + if value < radix as u64 { Some(value as u32) } else { None } } /// Returns an iterator that yields the hexadecimal Unicode escape of a @@ -704,7 +700,7 @@ impl char { unsafe { from_utf8_unchecked_mut(encode_utf8_raw(self as u32, dst)) } } - /// Encodes this character as native endian UTF-16 into the provided `u16` buffer, + /// Encodes this character as UTF-16 into the provided `u16` buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// /// # Panics @@ -733,7 +729,7 @@ impl char { /// '𝕊'.encode_utf16(&mut b); /// ``` #[stable(feature = "unicode_encode_char", since = "1.15.0")] - #[rustc_const_stable(feature = "const_char_encode_utf16", since = "1.84.0")] + #[rustc_const_stable(feature = "const_char_encode_utf16", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] { encode_utf16_raw(self as u32, dst) @@ -1303,7 +1299,7 @@ impl char { /// /// [`to_ascii_uppercase()`]: #method.to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); @@ -1329,7 +1325,7 @@ impl char { /// /// [`to_ascii_lowercase()`]: #method.to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); @@ -1791,6 +1787,7 @@ const fn len_utf16(code: u32) -> usize { /// Panics if the buffer is not large enough. /// A buffer of length four is large enough to encode any `char`. #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_char_encode_utf8", since = "1.83.0"))] #[doc(hidden)] #[inline] pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { @@ -1828,7 +1825,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 native endian UTF-16 into the provided `u16` buffer, +/// Encodes a raw `u32` value as 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. @@ -1839,6 +1836,10 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { /// Panics if the buffer is not large enough. /// A buffer of length 2 is large enough to encode any `char`. #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_char_encode_utf16", since = "CURRENT_RUSTC_VERSION") +)] #[doc(hidden)] #[inline] pub const fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { diff --git a/core/src/clone.rs b/core/src/clone.rs index 00300328b64c1..ec1aed53eaf72 100644 --- a/core/src/clone.rs +++ b/core/src/clone.rs @@ -311,16 +311,6 @@ 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/core/src/cmp.rs b/core/src/cmp.rs index 594236cf1d96f..5a3b9365cd220 100644 --- a/core/src/cmp.rs +++ b/core/src/cmp.rs @@ -796,7 +796,7 @@ impl Clone for Reverse { /// } /// /// impl Ord for Character { -/// fn cmp(&self, other: &Self) -> Ordering { +/// fn cmp(&self, other: &Self) -> std::cmp::Ordering { /// self.experience /// .cmp(&other.experience) /// .then(self.health.cmp(&other.health)) @@ -973,24 +973,6 @@ pub trait Ord: Eq + PartialOrd { /// 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 { 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] @@ -999,7 +981,7 @@ pub trait Ord: Eq + PartialOrd { where Self: Sized, { - if other < self { self } else { other } + max_by(self, other, Ord::cmp) } /// Compares and returns the minimum of two values. @@ -1012,24 +994,6 @@ pub trait Ord: Eq + PartialOrd { /// 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 { 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] @@ -1038,7 +1002,7 @@ pub trait Ord: Eq + PartialOrd { where Self: Sized, { - if other < self { other } else { self } + min_by(self, other, Ord::cmp) } /// Restrict a value to a certain interval. @@ -1450,24 +1414,6 @@ 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 { 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")] @@ -1485,22 +1431,20 @@ pub fn min(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); -/// -/// 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); +/// let result = cmp::min_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); /// assert_eq!(result, 1); +/// +/// let result = cmp::min_by(-2, 3, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); +/// assert_eq!(result, -2); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { - if compare(&v2, &v1).is_lt() { v2 } else { v1 } + match compare(&v1, &v2) { + Ordering::Less | Ordering::Equal => v1, + Ordering::Greater => v2, + } } /// Returns the element that gives the minimum value from the specified function. @@ -1512,20 +1456,17 @@ pub fn min_by 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, -3, |x: &i32| x.abs()); -/// assert_eq!(result, 2); -/// -/// let result = cmp::min_by_key(1, -1, |x: &i32| x.abs()); +/// 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); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - if f(&v2) < f(&v1) { v2 } else { v1 } + min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) } /// Compares and returns the maximum of two values. @@ -1542,24 +1483,6 @@ pub fn min_by_key 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 { 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")] @@ -1577,22 +1500,20 @@ pub fn max(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// 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); +/// let result = cmp::max_by(-2, 1, |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); +/// let result = cmp::max_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())) ; +/// assert_eq!(result, 2); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { - if compare(&v2, &v1).is_lt() { v1 } else { v2 } + match compare(&v1, &v2) { + Ordering::Less | Ordering::Equal => v2, + Ordering::Greater => v1, + } } /// Returns the element that gives the maximum value from the specified function. @@ -1604,20 +1525,17 @@ pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` /// use std::cmp; /// -/// 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()); +/// let result = cmp::max_by_key(-2, 1, |x: &i32| x.abs()); /// assert_eq!(result, -2); /// -/// let result = cmp::max_by_key(1, -1, |x: &i32| x.abs()); -/// assert_eq!(result, -1); +/// let result = cmp::max_by_key(-2, 2, |x: &i32| x.abs()); +/// assert_eq!(result, 2); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - if f(&v2) < f(&v1) { v1 } else { v2 } + max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) } /// Compares and sorts two values, returning minimum and maximum. @@ -1631,32 +1549,13 @@ pub fn max_by_key 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, 1), [1, 2]); +/// assert_eq!(cmp::minmax(2, 2), [2, 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 { 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")] @@ -1664,7 +1563,7 @@ pub fn minmax(v1: T, v2: T) -> [T; 2] where T: Ord, { - if v2 < v1 { [v2, v1] } else { [v1, v2] } + if v1 <= v2 { [v1, v2] } else { [v2, v1] } } /// Returns minimum and maximum values with respect to the specified comparison function. @@ -1677,14 +1576,11 @@ where /// #![feature(cmp_minmax)] /// use std::cmp; /// -/// 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]); +/// 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]); /// /// // You can destructure the result using array patterns -/// let [min, max] = cmp::minmax_by(-42, 17, abs_cmp); +/// let [min, max] = cmp::minmax_by(-42, 17, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); /// assert_eq!(min, 17); /// assert_eq!(max, -42); /// ``` @@ -1695,7 +1591,7 @@ pub fn minmax_by(v1: T, v2: T, compare: F) -> [T; 2] where F: FnOnce(&T, &T) -> Ordering, { - if compare(&v2, &v1).is_lt() { [v2, v1] } else { [v1, v2] } + if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] } } /// Returns minimum and maximum values with respect to the specified key function. @@ -1724,7 +1620,7 @@ where F: FnMut(&T) -> K, K: Ord, { - if f(&v2) < f(&v1) { [v2, v1] } else { [v1, v2] } + minmax_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) } // Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types diff --git a/core/src/contracts.rs b/core/src/contracts.rs deleted file mode 100644 index c769e219e4d49..0000000000000 --- a/core/src/contracts.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! 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(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/core/src/convert/mod.rs b/core/src/convert/mod.rs index e468f4f0f7e66..432e55e8c9a4c 100644 --- a/core/src/convert/mod.rs +++ b/core/src/convert/mod.rs @@ -443,7 +443,6 @@ pub trait AsMut { /// [`Vec`]: ../../std/vec/struct.Vec.html #[rustc_diagnostic_item = "Into"] #[stable(feature = "rust1", since = "1.0.0")] -#[doc(search_unbox)] pub trait Into: Sized { /// Converts this type into the (usually inferred) input type. #[must_use] @@ -578,7 +577,6 @@ pub trait Into: Sized { all(_Self = "&str", T = "alloc::string::String"), note = "to coerce a `{T}` into a `{Self}`, use `&*` as a prefix", ))] -#[doc(search_unbox)] pub trait From: Sized { /// Converts to this type from the input type. #[rustc_diagnostic_item = "from_fn"] diff --git a/core/src/error.rs b/core/src/error.rs index 9dbea57fa1f86..95a39cc3aed38 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -2,7 +2,7 @@ #![stable(feature = "error_in_core", since = "1.81.0")] use crate::any::TypeId; -use crate::fmt::{self, Debug, Display, Formatter}; +use crate::fmt::{Debug, Display, Formatter, Result}; /// `Error` is a trait representing the basic expectations for error values, /// i.e., values of type `E` in [`Result`]. @@ -857,7 +857,7 @@ impl<'a> Request<'a> { #[unstable(feature = "error_generic_member_access", issue = "99301")] impl<'a> Debug for Request<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.debug_struct("Request").finish_non_exhaustive() } } @@ -1076,4 +1076,4 @@ impl Error for crate::time::TryFromFloatSecsError {} impl Error for crate::ffi::FromBytesUntilNulError {} #[unstable(feature = "get_many_mut", issue = "104642")] -impl Error for crate::slice::GetManyMutError {} +impl Error for crate::slice::GetManyMutError {} diff --git a/core/src/escape.rs b/core/src/escape.rs index 0c3329f676eeb..0685f525dca83 100644 --- a/core/src/escape.rs +++ b/core/src/escape.rs @@ -163,28 +163,28 @@ pub(crate) struct EscapeIterInner { } impl EscapeIterInner { - pub(crate) const fn backslash(c: ascii::Char) -> Self { + pub const fn backslash(c: ascii::Char) -> Self { let (data, range) = backslash(c); Self { data, alive: range } } - pub(crate) const fn ascii(c: u8) -> Self { + pub const fn ascii(c: u8) -> Self { let (data, range) = escape_ascii(c); Self { data, alive: range } } - pub(crate) const fn unicode(c: char) -> Self { + pub const fn unicode(c: char) -> Self { let (data, range) = escape_unicode(c); Self { data, alive: range } } #[inline] - pub(crate) const fn empty() -> Self { + pub const fn empty() -> Self { Self { data: [ascii::Char::Null; N], alive: 0..0 } } #[inline] - pub(crate) fn as_ascii(&self) -> &[ascii::Char] { + pub fn as_ascii(&self) -> &[ascii::Char] { // SAFETY: `self.alive` is guaranteed to be a valid range for indexing `self.data`. unsafe { self.data.get_unchecked(usize::from(self.alive.start)..usize::from(self.alive.end)) @@ -192,34 +192,34 @@ impl EscapeIterInner { } #[inline] - pub(crate) fn as_str(&self) -> &str { + pub fn as_str(&self) -> &str { self.as_ascii().as_str() } #[inline] - pub(crate) fn len(&self) -> usize { + pub fn len(&self) -> usize { usize::from(self.alive.end - self.alive.start) } - pub(crate) fn next(&mut self) -> Option { + pub fn next(&mut self) -> Option { let i = self.alive.next()?; // SAFETY: `i` is guaranteed to be a valid index for `self.data`. unsafe { Some(self.data.get_unchecked(usize::from(i)).to_u8()) } } - pub(crate) fn next_back(&mut self) -> Option { + pub fn next_back(&mut self) -> Option { let i = self.alive.next_back()?; // SAFETY: `i` is guaranteed to be a valid index for `self.data`. unsafe { Some(self.data.get_unchecked(usize::from(i)).to_u8()) } } - pub(crate) fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + pub fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { self.alive.advance_by(n) } - pub(crate) fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + pub fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { self.alive.advance_back_by(n) } } diff --git a/core/src/ffi/c_str.rs b/core/src/ffi/c_str.rs index 7180593edf0d0..9e32f74227cf9 100644 --- a/core/src/ffi/c_str.rs +++ b/core/src/ffi/c_str.rs @@ -124,25 +124,39 @@ pub struct CStr { /// /// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err(); /// ``` -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "core_c_str", since = "1.64.0")] -pub enum FromBytesWithNulError { - /// Data provided contains an interior nul byte at byte `position`. - InteriorNul { - /// The position of the interior nul byte. - position: usize, - }, - /// Data provided is not nul terminated. +pub struct FromBytesWithNulError { + kind: FromBytesWithNulErrorKind, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +enum FromBytesWithNulErrorKind { + InteriorNul(usize), NotNulTerminated, } +// FIXME: const stability attributes should not be required here, I think +impl FromBytesWithNulError { + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0"))] + const fn interior_nul(pos: usize) -> FromBytesWithNulError { + FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) } + } + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0"))] + const fn not_nul_terminated() -> FromBytesWithNulError { + FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated } + } +} + #[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")] impl Error for FromBytesWithNulError { #[allow(deprecated)] fn description(&self) -> &str { - match self { - Self::InteriorNul { .. } => "data provided contains an interior nul byte", - Self::NotNulTerminated => "data provided is not nul terminated", + match self.kind { + FromBytesWithNulErrorKind::InteriorNul(..) => { + "data provided contains an interior nul byte" + } + FromBytesWithNulErrorKind::NotNulTerminated => "data provided is not nul terminated", } } } @@ -187,8 +201,8 @@ impl fmt::Display for FromBytesWithNulError { #[allow(deprecated, deprecated_in_future)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.description())?; - if let Self::InteriorNul { position } = self { - write!(f, " at byte pos {position}")?; + if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind { + write!(f, " at byte pos {pos}")?; } Ok(()) } @@ -337,25 +351,25 @@ impl CStr { /// use std::ffi::CStr; /// /// let cstr = CStr::from_bytes_with_nul(b"hello\0"); - /// assert_eq!(cstr, Ok(c"hello")); + /// assert!(cstr.is_ok()); /// ``` /// /// Creating a `CStr` without a trailing nul terminator is an error: /// /// ``` - /// use std::ffi::{CStr, FromBytesWithNulError}; + /// use std::ffi::CStr; /// /// let cstr = CStr::from_bytes_with_nul(b"hello"); - /// assert_eq!(cstr, Err(FromBytesWithNulError::NotNulTerminated)); + /// assert!(cstr.is_err()); /// ``` /// /// Creating a `CStr` with an interior nul byte is an error: /// /// ``` - /// use std::ffi::{CStr, FromBytesWithNulError}; + /// use std::ffi::CStr; /// /// let cstr = CStr::from_bytes_with_nul(b"he\0llo\0"); - /// assert_eq!(cstr, Err(FromBytesWithNulError::InteriorNul { position: 2 })); + /// assert!(cstr.is_err()); /// ``` #[stable(feature = "cstr_from_bytes", since = "1.10.0")] #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] @@ -367,8 +381,8 @@ impl CStr { // of the byte slice. Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) }) } - Some(position) => Err(FromBytesWithNulError::InteriorNul { position }), - None => Err(FromBytesWithNulError::NotNulTerminated), + Some(nul_pos) => Err(FromBytesWithNulError::interior_nul(nul_pos)), + None => Err(FromBytesWithNulError::not_nul_terminated()), } } @@ -450,7 +464,8 @@ impl CStr { /// /// ```no_run /// # #![allow(unused_must_use)] - /// # #![expect(dangling_pointers_from_temporaries)] + /// # #![cfg_attr(bootstrap, expect(temporary_cstring_as_ptr))] + /// # #![cfg_attr(not(bootstrap), expect(dangling_pointers_from_temporaries))] /// use std::ffi::CString; /// /// // Do not do this: @@ -485,7 +500,7 @@ impl CStr { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_as_ptr", since = "1.32.0")] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[rustc_never_returns_null_ptr] pub const fn as_ptr(&self) -> *const c_char { self.inner.as_ptr() @@ -717,6 +732,7 @@ impl AsRef for CStr { /// located within `isize::MAX` from `ptr`. #[inline] #[unstable(feature = "cstr_internals", issue = "none")] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0"))] #[rustc_allow_const_fn_unstable(const_eval_select)] const unsafe fn strlen(ptr: *const c_char) -> usize { const_eval_select!( diff --git a/core/src/ffi/mod.rs b/core/src/ffi/mod.rs index 50968c57adc62..dc107c5d22cdd 100644 --- a/core/src/ffi/mod.rs +++ b/core/src/ffi/mod.rs @@ -12,10 +12,10 @@ #[doc(inline)] #[stable(feature = "core_c_str", since = "1.64.0")] pub use self::c_str::CStr; -#[doc(inline)] +#[doc(no_inline)] #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] pub use self::c_str::FromBytesUntilNulError; -#[doc(inline)] +#[doc(no_inline)] #[stable(feature = "core_c_str", since = "1.64.0")] pub use self::c_str::FromBytesWithNulError; use crate::fmt; @@ -37,14 +37,146 @@ pub use self::va_list::{VaList, VaListImpl}; )] pub mod va_list; -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, -}; +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. +#[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 use self::primitives::{c_ptrdiff_t, c_size_t, c_ssize_t}; +pub type c_ssize_t = isize; + +mod c_char_definition { + cfg_if! { + // These are the targets on which c_char is unsigned. + if #[cfg(any( + all( + target_os = "linux", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "hexagon", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "riscv64", + target_arch = "riscv32", + target_arch = "csky" + ) + ), + all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), + all(target_os = "l4re", target_arch = "x86_64"), + all( + any(target_os = "freebsd", target_os = "openbsd", target_os = "rtems"), + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "riscv64" + ) + ), + all( + target_os = "netbsd", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "riscv64" + ) + ), + all( + target_os = "vxworks", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc64", + target_arch = "powerpc" + ) + ), + all( + target_os = "fuchsia", + any(target_arch = "aarch64", target_arch = "riscv64") + ), + all(target_os = "nto", target_arch = "aarch64"), + target_os = "horizon", + target_os = "aix", + ))] { + pub type c_char = u8; + } else { + // On every other target, c_char is signed. + pub type c_char = i8; + } + } +} + +mod c_int_definition { + cfg_if! { + if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] { + pub type c_int = i16; + pub type c_uint = u16; + } else { + pub type c_int = i32; + pub type c_uint = u32; + } + } +} + +mod c_long_definition { + cfg_if! { + if #[cfg(all(target_pointer_width = "64", not(windows)))] { + pub type c_long = i64; + pub type c_ulong = u64; + } else { + // The minimal size of `long` in the C standard is 32 bits + pub type c_long = i32; + pub type c_ulong = u32; + } + } +} // 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 diff --git a/core/src/ffi/primitives.rs b/core/src/ffi/primitives.rs deleted file mode 100644 index ece3c7538dabb..0000000000000 --- a/core/src/ffi/primitives.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! 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/core/src/ffi/va_list.rs b/core/src/ffi/va_list.rs index cceb186b31e79..3a224e4d8fe5f 100644 --- a/core/src/ffi/va_list.rs +++ b/core/src/ffi/va_list.rs @@ -15,7 +15,6 @@ use crate::ops::{Deref, DerefMut}; not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "s390x"), - not(target_arch = "xtensa"), not(target_arch = "x86_64") ), all(target_arch = "aarch64", target_vendor = "apple"), @@ -38,7 +37,6 @@ pub struct VaListImpl<'f> { not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "s390x"), - not(target_arch = "xtensa"), not(target_arch = "x86_64") ), all(target_arch = "aarch64", target_vendor = "apple"), @@ -115,18 +113,6 @@ pub struct VaListImpl<'f> { _marker: PhantomData<&'f mut &'f c_void>, } -/// Xtensa ABI implementation of a `va_list`. -#[cfg(target_arch = "xtensa")] -#[repr(C)] -#[derive(Debug)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - stk: *mut i32, - reg: *mut i32, - ndx: i32, - _marker: PhantomData<&'f mut &'f c_void>, -} - /// A wrapper for a `va_list` #[repr(transparent)] #[derive(Debug)] @@ -138,7 +124,6 @@ pub struct VaList<'a, 'f: 'a> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - target_arch = "xtensa", all(target_arch = "aarch64", target_vendor = "apple"), target_family = "wasm", target_os = "uefi", @@ -153,7 +138,6 @@ pub struct VaList<'a, 'f: 'a> { target_arch = "s390x", target_arch = "x86_64" ), - not(target_arch = "xtensa"), any(not(target_arch = "aarch64"), not(target_vendor = "apple")), not(target_family = "wasm"), not(target_os = "uefi"), @@ -171,7 +155,6 @@ pub struct VaList<'a, 'f: 'a> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - target_arch = "xtensa", all(target_arch = "aarch64", target_vendor = "apple"), target_family = "wasm", target_os = "uefi", @@ -190,10 +173,8 @@ impl<'f> VaListImpl<'f> { target_arch = "aarch64", target_arch = "powerpc", target_arch = "s390x", - target_arch = "xtensa", target_arch = "x86_64" ), - not(target_arch = "xtensa"), any(not(target_arch = "aarch64"), not(target_vendor = "apple")), not(target_family = "wasm"), not(target_os = "uefi"), @@ -302,28 +283,18 @@ impl<'f> Drop for VaListImpl<'f> { } } -/// 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!() -} +extern "rust-intrinsic" { + /// Destroy the arglist `ap` after initialization with `va_start` or + /// `va_copy`. + #[rustc_nounwind] + fn va_end(ap: &mut VaListImpl<'_>); -/// 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!() -} + /// Copies the current location of arglist `src` to the arglist `dst`. + #[rustc_nounwind] + fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); -/// 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(_ap: &mut VaListImpl<'_>) -> T { - unreachable!() + /// Loads an argument of type `T` from the `va_list` `ap` and increment the + /// argument `ap` points to. + #[rustc_nounwind] + fn va_arg(ap: &mut VaListImpl<'_>) -> T; } diff --git a/core/src/fmt/builders.rs b/core/src/fmt/builders.rs index 665b05b12ec07..1862be0e86c5d 100644 --- a/core/src/fmt/builders.rs +++ b/core/src/fmt/builders.rs @@ -1228,7 +1228,6 @@ 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) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } diff --git a/core/src/fmt/float.rs b/core/src/fmt/float.rs index 3f10158193d76..04230b1610aae 100644 --- a/core/src/fmt/float.rs +++ b/core/src/fmt/float.rs @@ -86,7 +86,7 @@ where true => flt2dec::Sign::MinusPlus, }; - if let Some(precision) = fmt.options.precision { + if let Some(precision) = fmt.precision { float_to_decimal_common_exact(fmt, num, sign, precision) } else { let min_precision = 0; @@ -162,7 +162,7 @@ where true => flt2dec::Sign::MinusPlus, }; - if let Some(precision) = fmt.options.precision { + if let Some(precision) = fmt.precision { // 1 integral digit + `precision` fractional digits = `precision + 1` total digits float_to_exponential_common_exact(fmt, num, sign, precision + 1, upper) } else { @@ -180,7 +180,7 @@ where true => flt2dec::Sign::MinusPlus, }; - if let Some(precision) = fmt.options.precision { + if let Some(precision) = fmt.precision { // this behavior of {:.PREC?} predates exponential formatting for {:?} float_to_decimal_common_exact(fmt, num, sign, precision) } else { diff --git a/core/src/fmt/mod.rs b/core/src/fmt/mod.rs index a1bf3a4d7a706..2b1692a195e50 100644 --- a/core/src/fmt/mod.rs +++ b/core/src/fmt/mod.rs @@ -33,19 +33,6 @@ pub enum Alignment { Center, } -#[doc(hidden)] -#[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")] -impl From for Option { - fn from(value: rt::Alignment) -> Self { - match value { - rt::Alignment::Left => Some(Alignment::Left), - rt::Alignment::Right => Some(Alignment::Right), - rt::Alignment::Center => Some(Alignment::Center), - rt::Alignment::Unknown => None, - } - } -} - #[stable(feature = "debug_builders", since = "1.2.0")] pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; #[unstable(feature = "debug_closure_helpers", issue = "117729")] @@ -152,9 +139,8 @@ pub trait Write { /// } /// /// let mut buf = String::new(); - /// writer(&mut buf, "hola")?; + /// writer(&mut buf, "hola").unwrap(); /// assert_eq!(&buf, "hola"); - /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn write_str(&mut self, s: &str) -> Result; @@ -180,10 +166,9 @@ pub trait Write { /// } /// /// let mut buf = String::new(); - /// writer(&mut buf, 'a')?; - /// writer(&mut buf, 'b')?; + /// writer(&mut buf, 'a').unwrap(); + /// writer(&mut buf, 'b').unwrap(); /// assert_eq!(&buf, "ab"); - /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "fmt_write_char", since = "1.1.0")] fn write_char(&mut self, c: char) -> Result { @@ -210,9 +195,8 @@ pub trait Write { /// } /// /// let mut buf = String::new(); - /// writer(&mut buf, "world")?; + /// writer(&mut buf, "world").unwrap(); /// assert_eq!(&buf, "world"); - /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn write_fmt(&mut self, args: Arguments<'_>) -> Result { @@ -263,260 +247,6 @@ impl Write for &mut W { } } -/// The signedness of a [`Formatter`] (or of a [`FormattingOptions`]). -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[unstable(feature = "formatting_options", issue = "118117")] -pub enum Sign { - /// Represents the `+` flag. - Plus, - /// Represents the `-` flag. - Minus, -} - -/// Specifies whether the [`Debug`] trait should use lower-/upper-case -/// hexadecimal or normal integers. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[unstable(feature = "formatting_options", issue = "118117")] -pub enum DebugAsHex { - /// Use lower-case hexadecimal integers for the `Debug` trait (like [the `x?` type](../../std/fmt/index.html#formatting-traits)). - Lower, - /// Use upper-case hexadecimal integers for the `Debug` trait (like [the `X?` type](../../std/fmt/index.html#formatting-traits)). - Upper, -} - -/// Options for formatting. -/// -/// `FormattingOptions` is a [`Formatter`] without an attached [`Write`] trait. -/// It is mainly used to construct `Formatter` instances. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[unstable(feature = "formatting_options", issue = "118117")] -pub struct FormattingOptions { - flags: u32, - fill: char, - align: Option, - width: Option, - precision: Option, -} - -impl FormattingOptions { - /// Construct a new `FormatterBuilder` with the supplied `Write` trait - /// object for output that is equivalent to the `{}` formatting - /// specifier: - /// - /// - no flags, - /// - filled with spaces, - /// - no alignment, - /// - no width, - /// - no precision, and - /// - no [`DebugAsHex`] output mode. - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn new() -> Self { - Self { flags: 0, fill: ' ', align: None, width: None, precision: None } - } - - /// Sets or removes the sign (the `+` or the `-` flag). - /// - /// - `+`: This is intended for numeric types and indicates that the sign - /// should always be printed. By default only the negative sign of signed - /// values is printed, and the sign of positive or unsigned values is - /// omitted. This flag indicates that the correct sign (+ or -) should - /// always be printed. - /// - `-`: Currently not used - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn sign(&mut self, sign: Option) -> &mut Self { - self.flags = - self.flags & !(1 << rt::Flag::SignMinus as u32 | 1 << rt::Flag::SignPlus as u32); - match sign { - None => {} - Some(Sign::Plus) => self.flags |= 1 << rt::Flag::SignPlus as u32, - Some(Sign::Minus) => self.flags |= 1 << rt::Flag::SignMinus as u32, - } - self - } - /// Sets or unsets the `0` flag. - /// - /// This is used to indicate for integer formats that the padding to width should both be done with a 0 character as well as be sign-aware - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn sign_aware_zero_pad(&mut self, sign_aware_zero_pad: bool) -> &mut Self { - if sign_aware_zero_pad { - self.flags |= 1 << rt::Flag::SignAwareZeroPad as u32 - } else { - self.flags &= !(1 << rt::Flag::SignAwareZeroPad as u32) - } - self - } - /// Sets or unsets the `#` flag. - /// - /// This flag indicates that the "alternate" form of printing should be - /// used. The alternate forms are: - /// - [`Debug`] : pretty-print the [`Debug`] formatting (adds linebreaks and indentation) - /// - [`LowerHex`] as well as [`UpperHex`] - precedes the argument with a `0x` - /// - [`Octal`] - precedes the argument with a `0b` - /// - [`Binary`] - precedes the argument with a `0o` - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn alternate(&mut self, alternate: bool) -> &mut Self { - if alternate { - self.flags |= 1 << rt::Flag::Alternate as u32 - } else { - self.flags &= !(1 << rt::Flag::Alternate as u32) - } - self - } - /// Sets the fill character. - /// - /// The optional fill character and alignment is provided normally in - /// conjunction with the width parameter. This indicates that if the value - /// being formatted is smaller than width some extra characters will be - /// printed around it. - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn fill(&mut self, fill: char) -> &mut Self { - self.fill = fill; - self - } - /// Sets or removes the alignment. - /// - /// The alignment specifies how the value being formatted should be - /// positioned if it is smaller than the width of the formatter. - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn align(&mut self, align: Option) -> &mut Self { - self.align = align; - self - } - /// Sets or removes the width. - /// - /// This is a parameter for the “minimum width” that the format should take - /// up. If the value’s string does not fill up this many characters, then - /// the padding specified by [`FormattingOptions::fill`]/[`FormattingOptions::align`] - /// will be used to take up the required space. - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn width(&mut self, width: Option) -> &mut Self { - self.width = width; - self - } - /// Sets or removes the precision. - /// - /// - For non-numeric types, this can be considered a “maximum width”. If - /// the resulting string is longer than this width, then it is truncated - /// down to this many characters and that truncated value is emitted with - /// proper fill, alignment and width if those parameters are set. - /// - For integral types, this is ignored. - /// - For floating-point types, this indicates how many digits after the - /// decimal point should be printed. - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn precision(&mut self, precision: Option) -> &mut Self { - self.precision = precision; - self - } - /// Specifies whether the [`Debug`] trait should use lower-/upper-case - /// hexadecimal or normal integers - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn debug_as_hex(&mut self, debug_as_hex: Option) -> &mut Self { - self.flags = self.flags - & !(1 << rt::Flag::DebugUpperHex as u32 | 1 << rt::Flag::DebugLowerHex as u32); - match debug_as_hex { - None => {} - Some(DebugAsHex::Upper) => self.flags |= 1 << rt::Flag::DebugUpperHex as u32, - Some(DebugAsHex::Lower) => self.flags |= 1 << rt::Flag::DebugLowerHex as u32, - } - self - } - - /// Returns the current sign (the `+` or the `-` flag). - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_sign(&self) -> Option { - const SIGN_PLUS_BITFIELD: u32 = 1 << rt::Flag::SignPlus as u32; - const SIGN_MINUS_BITFIELD: u32 = 1 << rt::Flag::SignMinus as u32; - match self.flags & ((1 << rt::Flag::SignPlus as u32) | (1 << rt::Flag::SignMinus as u32)) { - SIGN_PLUS_BITFIELD => Some(Sign::Plus), - SIGN_MINUS_BITFIELD => Some(Sign::Minus), - 0 => None, - _ => panic!("Invalid sign bits set in flags"), - } - } - /// Returns the current `0` flag. - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_sign_aware_zero_pad(&self) -> bool { - self.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0 - } - /// Returns the current `#` flag. - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_alternate(&self) -> bool { - self.flags & (1 << rt::Flag::Alternate as u32) != 0 - } - /// Returns the current fill character. - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_fill(&self) -> char { - self.fill - } - /// Returns the current alignment. - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_align(&self) -> Option { - self.align - } - /// Returns the current width. - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_width(&self) -> Option { - self.width - } - /// Returns the current precision. - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_precision(&self) -> Option { - self.precision - } - /// Returns the current precision. - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn get_debug_as_hex(&self) -> Option { - const DEBUG_UPPER_BITFIELD: u32 = 1 << rt::Flag::DebugUpperHex as u32; - const DEBUG_LOWER_BITFIELD: u32 = 1 << rt::Flag::DebugLowerHex as u32; - match self.flags - & ((1 << rt::Flag::DebugUpperHex as u32) | (1 << rt::Flag::DebugLowerHex as u32)) - { - DEBUG_UPPER_BITFIELD => Some(DebugAsHex::Upper), - DEBUG_LOWER_BITFIELD => Some(DebugAsHex::Lower), - 0 => None, - _ => panic!("Invalid hex debug bits set in flags"), - } - } - - /// Creates a [`Formatter`] that writes its output to the given [`Write`] trait. - /// - /// You may alternatively use [`Formatter::new()`]. - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn create_formatter<'a>(self, write: &'a mut (dyn Write + 'a)) -> Formatter<'a> { - Formatter { options: self, buf: write } - } - - #[doc(hidden)] - #[unstable( - feature = "fmt_internals", - reason = "internal routines only exposed for testing", - issue = "none" - )] - /// Flags for formatting - pub fn flags(&mut self, flags: u32) { - self.flags = flags - } - #[doc(hidden)] - #[unstable( - feature = "fmt_internals", - reason = "internal routines only exposed for testing", - issue = "none" - )] - /// Flags for formatting - pub fn get_flags(&self) -> u32 { - self.flags - } -} - -#[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 @@ -530,28 +260,34 @@ impl Default for FormattingOptions { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Formatter"] pub struct Formatter<'a> { - options: FormattingOptions, + flags: u32, + fill: char, + align: rt::Alignment, + width: Option, + precision: Option, buf: &'a mut (dyn Write + 'a), } impl<'a> Formatter<'a> { - /// Creates a new formatter with given [`FormattingOptions`]. + /// Creates a new formatter with default settings. /// - /// If `write` is a reference to a formatter, it is recommended to use - /// [`Formatter::with_options`] instead as this can borrow the underlying - /// `write`, thereby bypassing one layer of indirection. + /// This can be used as a micro-optimization in cases where a full `Arguments` + /// structure (as created by `format_args!`) is not necessary; `Arguments` + /// is a little more expensive to use in simple formatting scenarios. /// - /// You may alternatively use [`FormattingOptions::create_formatter()`]. - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn new(write: &'a mut (dyn Write + 'a), options: FormattingOptions) -> Self { - Formatter { options, buf: write } - } - - /// Creates a new formatter based on this one with given [`FormattingOptions`]. - #[unstable(feature = "formatting_options", issue = "118117")] - pub fn with_options<'b>(&'b mut self, options: FormattingOptions) -> Formatter<'b> { - Formatter { options, buf: self.buf } + /// Currently not intended for use outside of the standard library. + #[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")] + #[doc(hidden)] + pub fn new(buf: &'a mut (dyn Write + 'a)) -> Formatter<'a> { + Formatter { + flags: 0, + fill: ' ', + align: rt::Alignment::Unknown, + width: None, + precision: None, + buf, + } } } @@ -597,6 +333,10 @@ pub struct Arguments<'a> { #[unstable(feature = "fmt_internals", issue = "none")] impl<'a> Arguments<'a> { #[inline] + #[cfg_attr( + bootstrap, + rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none") + )] pub const fn new_const(pieces: &'a [&'static str; N]) -> Self { const { assert!(N <= 1) }; Arguments { pieces, fmt: None, args: &[] } @@ -605,7 +345,7 @@ impl<'a> Arguments<'a> { /// When using the format_args!() macro, this function is used to generate the /// Arguments structure. #[inline] - pub const fn new_v1( + pub fn new_v1( pieces: &'a [&'static str; P], args: &'a [rt::Argument<'a>; A], ) -> Arguments<'a> { @@ -621,7 +361,7 @@ impl<'a> Arguments<'a> { /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. #[inline] - pub const fn new_v1_formatted( + pub fn new_v1_formatted( pieces: &'a [&'static str], args: &'a [rt::Argument<'a>], fmt: &'a [rt::Placeholder], @@ -698,7 +438,7 @@ impl<'a> Arguments<'a> { /// assert_eq!(format_args!("{:?}", std::env::current_dir()).as_str(), None); /// ``` #[stable(feature = "fmt_as_str", since = "1.52.0")] - #[rustc_const_stable(feature = "const_arguments_as_str", since = "1.84.0")] + #[rustc_const_stable(feature = "const_arguments_as_str", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn as_str(&self) -> Option<&'static str> { @@ -1429,7 +1169,7 @@ pub trait UpperExp { /// [`write!`]: crate::write! #[stable(feature = "rust1", since = "1.0.0")] pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { - let mut formatter = Formatter::new(output, FormattingOptions::new()); + let mut formatter = Formatter::new(output); let mut idx = 0; match args.fmt { @@ -1478,14 +1218,14 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { } unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result { - fmt.options.fill = arg.fill; - fmt.options.align = arg.align.into(); - fmt.options.flags = arg.flags; + fmt.fill = arg.fill; + fmt.align = arg.align; + fmt.flags = arg.flags; // SAFETY: arg and args come from the same Arguments, // which guarantees the indexes are always within bounds. unsafe { - fmt.options.width = getcount(args, &arg.width); - fmt.options.precision = getcount(args, &arg.precision); + fmt.width = getcount(args, &arg.width); + fmt.precision = getcount(args, &arg.precision); } // Extract the correct argument @@ -1544,7 +1284,11 @@ impl<'a> Formatter<'a> { buf: wrap(self.buf), // And preserve these - options: self.options, + flags: self.flags, + fill: self.fill, + align: self.align, + width: self.width, + precision: self.precision, } } @@ -1625,7 +1369,7 @@ impl<'a> Formatter<'a> { } // The `width` field is more of a `min-width` parameter at this point. - match self.options.width { + match self.width { // If there's no minimum length requirements then we can just // write the bytes. None => { @@ -1641,15 +1385,14 @@ impl<'a> Formatter<'a> { // The sign and prefix goes before the padding if the fill character // is zero Some(min) if self.sign_aware_zero_pad() => { - let old_fill = crate::mem::replace(&mut self.options.fill, '0'); - let old_align = - crate::mem::replace(&mut self.options.align, Some(Alignment::Right)); + let old_fill = crate::mem::replace(&mut self.fill, '0'); + let old_align = crate::mem::replace(&mut self.align, rt::Alignment::Right); write_prefix(self, sign, prefix)?; let post_padding = self.padding(min - width, Alignment::Right)?; self.buf.write_str(buf)?; post_padding.write(self)?; - self.options.fill = old_fill; - self.options.align = old_align; + self.fill = old_fill; + self.align = old_align; Ok(()) } // Otherwise, the sign and prefix goes after the padding @@ -1694,12 +1437,12 @@ impl<'a> Formatter<'a> { #[stable(feature = "rust1", since = "1.0.0")] pub fn pad(&mut self, s: &str) -> Result { // Make sure there's a fast path up front - if self.options.width.is_none() && self.options.precision.is_none() { + if self.width.is_none() && self.precision.is_none() { return self.buf.write_str(s); } // The `precision` field can be interpreted as a `max-width` for the // string being formatted. - let s = if let Some(max) = self.options.precision { + let s = if let Some(max) = self.precision { // If our string is longer that the precision, then we must have // truncation. However other flags like `fill`, `width` and `align` // must act as always. @@ -1716,7 +1459,7 @@ impl<'a> Formatter<'a> { &s }; // The `width` field is more of a `min-width` parameter at this point. - match self.options.width { + match self.width { // If we're under the maximum length, and there's no minimum length // requirements, then we can just emit the string None => self.buf.write_str(s), @@ -1748,7 +1491,12 @@ impl<'a> Formatter<'a> { padding: usize, default: Alignment, ) -> result::Result { - let align = self.align().unwrap_or(default); + let align = match self.align { + rt::Alignment::Unknown => default, + rt::Alignment::Left => Alignment::Left, + rt::Alignment::Right => Alignment::Right, + rt::Alignment::Center => Alignment::Center, + }; let (pre_pad, post_pad) = match align { Alignment::Left => (0, padding), @@ -1757,10 +1505,10 @@ impl<'a> Formatter<'a> { }; for _ in 0..pre_pad { - self.buf.write_char(self.options.fill)?; + self.buf.write_char(self.fill)?; } - Ok(PostPadding::new(self.options.fill, post_pad)) + Ok(PostPadding::new(self.fill, post_pad)) } /// Takes the formatted parts and applies the padding. @@ -1772,12 +1520,12 @@ impl<'a> Formatter<'a> { /// /// Any `numfmt::Part::Copy` parts in `formatted` must contain valid UTF-8. unsafe fn pad_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result { - if let Some(mut width) = self.options.width { + if let Some(mut width) = self.width { // for the sign-aware zero padding, we render the sign first and // behave as if we had no sign from the beginning. let mut formatted = formatted.clone(); - let old_fill = self.options.fill; - let old_align = self.options.align; + let old_fill = self.fill; + let old_align = self.align; if self.sign_aware_zero_pad() { // a sign always goes first let sign = formatted.sign; @@ -1786,8 +1534,8 @@ impl<'a> Formatter<'a> { // remove the sign from the formatted parts formatted.sign = ""; width = width.saturating_sub(sign.len()); - self.options.fill = '0'; - self.options.align = Some(Alignment::Right); + self.fill = '0'; + self.align = rt::Alignment::Right; } // remaining parts go through the ordinary padding process. @@ -1804,8 +1552,8 @@ impl<'a> Formatter<'a> { } post_padding.write(self) }; - self.options.fill = old_fill; - self.options.align = old_align; + self.fill = old_fill; + self.align = old_align; ret } else { // this is the common case and we take a shortcut @@ -1931,7 +1679,7 @@ impl<'a> Formatter<'a> { or `sign_aware_zero_pad` methods instead" )] pub fn flags(&self) -> u32 { - self.options.flags + self.flags } /// Returns the character used as 'fill' whenever there is alignment. @@ -1964,7 +1712,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn fill(&self) -> char { - self.options.fill + self.fill } /// Returns a flag indicating what form of alignment was requested. @@ -1999,7 +1747,12 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags_align", since = "1.28.0")] pub fn align(&self) -> Option { - self.options.align + match self.align { + rt::Alignment::Left => Some(Alignment::Left), + rt::Alignment::Right => Some(Alignment::Right), + rt::Alignment::Center => Some(Alignment::Center), + rt::Alignment::Unknown => None, + } } /// Returns the optionally specified integer width that the output should be. @@ -2029,7 +1782,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn width(&self) -> Option { - self.options.width + self.width } /// Returns the optionally specified precision for numeric types. @@ -2060,7 +1813,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn precision(&self) -> Option { - self.options.precision + self.precision } /// Determines if the `+` flag was specified. @@ -2092,7 +1845,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_plus(&self) -> bool { - self.options.flags & (1 << rt::Flag::SignPlus as u32) != 0 + self.flags & (1 << rt::Flag::SignPlus as u32) != 0 } /// Determines if the `-` flag was specified. @@ -2121,7 +1874,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_minus(&self) -> bool { - self.options.flags & (1 << rt::Flag::SignMinus as u32) != 0 + self.flags & (1 << rt::Flag::SignMinus as u32) != 0 } /// Determines if the `#` flag was specified. @@ -2149,7 +1902,7 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn alternate(&self) -> bool { - self.options.flags & (1 << rt::Flag::Alternate as u32) != 0 + self.flags & (1 << rt::Flag::Alternate as u32) != 0 } /// Determines if the `0` flag was specified. @@ -2175,17 +1928,17 @@ impl<'a> Formatter<'a> { #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] pub fn sign_aware_zero_pad(&self) -> bool { - self.options.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0 + self.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0 } // FIXME: Decide what public API we want for these two flags. // https://github.com/rust-lang/rust/issues/48584 fn debug_lower_hex(&self) -> bool { - self.options.flags & (1 << rt::Flag::DebugLowerHex as u32) != 0 + self.flags & (1 << rt::Flag::DebugLowerHex as u32) != 0 } fn debug_upper_hex(&self) -> bool { - self.options.flags & (1 << rt::Flag::DebugUpperHex as u32) != 0 + self.flags & (1 << rt::Flag::DebugUpperHex as u32) != 0 } /// Creates a [`DebugStruct`] builder designed to assist with creation of @@ -2601,18 +2354,6 @@ impl<'a> Formatter<'a> { pub fn debug_map<'b>(&'b mut self) -> DebugMap<'b, 'a> { builders::debug_map_new(self) } - - /// Returns the sign of this formatter (`+` or `-`). - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn sign(&self) -> Option { - self.options.get_sign() - } - - /// Returns the formatting options this formatter corresponds to. - #[unstable(feature = "formatting_options", issue = "118117")] - pub const fn options(&self) -> FormattingOptions { - self.options - } } #[stable(since = "1.2.0", feature = "formatter_write")] @@ -2765,7 +2506,7 @@ impl Debug for char { #[stable(feature = "rust1", since = "1.0.0")] impl Display for char { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - if f.options.width.is_none() && f.options.precision.is_none() { + if f.width.is_none() && f.precision.is_none() { f.write_char(*self) } else { f.pad(self.encode_utf8(&mut [0; 4])) @@ -2789,26 +2530,26 @@ impl Pointer for *const T { /// /// [problematic]: https://github.com/rust-lang/rust/issues/95489 pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Result { - let old_width = f.options.width; - let old_flags = f.options.flags; + let old_width = f.width; + let old_flags = f.flags; // The alternate flag is already treated by LowerHex as being special- // it denotes whether to prefix with 0x. We use it to work out whether // or not to zero extend, and then unconditionally set it to get the // prefix. if f.alternate() { - f.options.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32); + f.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32); - if f.options.width.is_none() { - f.options.width = Some((usize::BITS / 4) as usize + 2); + if f.width.is_none() { + f.width = Some((usize::BITS / 4) as usize + 2); } } - f.options.flags |= 1 << (rt::Flag::Alternate as u32); + f.flags |= 1 << (rt::Flag::Alternate as u32); let ret = LowerHex::fmt(&ptr_addr, f); - f.options.width = old_width; - f.options.flags = old_flags; + f.width = old_width; + f.flags = old_flags; ret } diff --git a/core/src/fmt/num.rs b/core/src/fmt/num.rs index 4467b37bd4510..683e45b35f70a 100644 --- a/core/src/fmt/num.rs +++ b/core/src/fmt/num.rs @@ -192,8 +192,7 @@ 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\ @@ -233,89 +232,83 @@ macro_rules! impl_Display { #[cfg(not(feature = "optimize_for_size"))] impl $unsigned { - 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::::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::() > 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]); - } + 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::::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); + } + } - // 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 <= 9999, so at most 4 chars long + let mut n = self as usize; // possibly reduce 64bit math + + // 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 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; + // 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); + } } - // 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(), - )) + // 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)) }; - f.pad_integral(is_nonnegative, "", as_str) + f.pad_integral(is_nonnegative, "", buf_slice) } })* #[cfg(feature = "optimize_for_size")] fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1; - let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; - let mut curr = MAX_DEC_N; + const SIZE: usize = $u::MAX.ilog(10) as usize + 1; + let mut buf = [MaybeUninit::::uninit(); SIZE]; + let mut curr = buf.len(); 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 @@ -733,9 +726,28 @@ fn udiv_1e19(n: u128) -> (u128, u64) { let quot = if n < 1 << 83 { ((n >> 19) as u64 / (DIV >> 19)) as u128 } else { - n.widening_mul(FACTOR).1 >> 62 + u128_mulhi(n, FACTOR) >> 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/core/src/fmt/rt.rs b/core/src/fmt/rt.rs index 85d089a079082..af6f0da88de67 100644 --- a/core/src/fmt/rt.rs +++ b/core/src/fmt/rt.rs @@ -19,7 +19,7 @@ pub struct Placeholder { } impl Placeholder { - #[inline] + #[inline(always)] pub const fn new( position: usize, fill: char, @@ -95,13 +95,13 @@ pub struct Argument<'a> { #[rustc_diagnostic_item = "ArgumentMethods"] impl Argument<'_> { - #[inline] - const fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> { + #[inline(always)] + fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> { Argument { // INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and // a `fn(&T, ...)`, so the invariant is maintained. ty: ArgumentType::Placeholder { - value: NonNull::from_ref(x).cast(), + value: NonNull::from(x).cast(), // SAFETY: function pointers always have the same layout. formatter: unsafe { mem::transmute(f) }, _lifetime: PhantomData, @@ -109,48 +109,48 @@ impl Argument<'_> { } } - #[inline] + #[inline(always)] pub fn new_display(x: &T) -> Argument<'_> { Self::new(x, Display::fmt) } - #[inline] + #[inline(always)] pub fn new_debug(x: &T) -> Argument<'_> { Self::new(x, Debug::fmt) } - #[inline] + #[inline(always)] pub fn new_debug_noop(x: &T) -> Argument<'_> { Self::new(x, |_, _| Ok(())) } - #[inline] + #[inline(always)] pub fn new_octal(x: &T) -> Argument<'_> { Self::new(x, Octal::fmt) } - #[inline] + #[inline(always)] pub fn new_lower_hex(x: &T) -> Argument<'_> { Self::new(x, LowerHex::fmt) } - #[inline] + #[inline(always)] pub fn new_upper_hex(x: &T) -> Argument<'_> { Self::new(x, UpperHex::fmt) } - #[inline] + #[inline(always)] pub fn new_pointer(x: &T) -> Argument<'_> { Self::new(x, Pointer::fmt) } - #[inline] + #[inline(always)] pub fn new_binary(x: &T) -> Argument<'_> { Self::new(x, Binary::fmt) } - #[inline] + #[inline(always)] pub fn new_lower_exp(x: &T) -> Argument<'_> { Self::new(x, LowerExp::fmt) } - #[inline] + #[inline(always)] pub fn new_upper_exp(x: &T) -> Argument<'_> { Self::new(x, UpperExp::fmt) } - #[inline] - pub const fn from_usize(x: &usize) -> Argument<'_> { + #[inline(always)] + pub fn from_usize(x: &usize) -> Argument<'_> { Argument { ty: ArgumentType::Count(*x) } } @@ -164,7 +164,7 @@ impl Argument<'_> { // it here is an explicit CFI violation. #[allow(inline_no_sanitize)] #[no_sanitize(cfi, kcfi)] - #[inline] + #[inline(always)] pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self.ty { // SAFETY: @@ -180,8 +180,8 @@ impl Argument<'_> { } } - #[inline] - pub(super) const fn as_usize(&self) -> Option { + #[inline(always)] + pub(super) fn as_usize(&self) -> Option { match self.ty { ArgumentType::Count(count) => Some(count), ArgumentType::Placeholder { .. } => None, @@ -198,8 +198,8 @@ impl Argument<'_> { /// let f = format_args!("{}", "a"); /// println!("{f}"); /// ``` - #[inline] - pub const fn none() -> [Self; 0] { + #[inline(always)] + pub fn none() -> [Self; 0] { [] } } @@ -215,8 +215,8 @@ pub struct UnsafeArg { impl UnsafeArg { /// See documentation where `UnsafeArg` is required to know when it is safe to /// create and use `UnsafeArg`. - #[inline] - pub const unsafe fn new() -> Self { + #[inline(always)] + pub unsafe fn new() -> Self { Self { _private: () } } } diff --git a/core/src/future/async_drop.rs b/core/src/future/async_drop.rs index f1778a4d782af..7de5fe67cd096 100644 --- a/core/src/future/async_drop.rs +++ b/core/src/future/async_drop.rs @@ -133,8 +133,7 @@ pub trait AsyncDrop { } #[lang = "async_destruct"] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_deny_explicit_impl(implement_via_object = false)] trait AsyncDestruct { type AsyncDestructor: Future; } diff --git a/core/src/future/future.rs b/core/src/future/future.rs index cfbd88bbe7998..234914c20fc31 100644 --- a/core/src/future/future.rs +++ b/core/src/future/future.rs @@ -25,7 +25,7 @@ use crate::task::{Context, Poll}; /// [`async`]: ../../std/keyword.async.html /// [`Waker`]: crate::task::Waker #[doc(notable_trait)] -#[doc(search_unbox)] +#[cfg_attr(not(bootstrap), doc(search_unbox))] #[must_use = "futures do nothing unless you `.await` or poll them"] #[stable(feature = "futures_api", since = "1.36.0")] #[lang = "future_trait"] diff --git a/core/src/hash/mod.rs b/core/src/hash/mod.rs index 7a6630c82d0da..061690e88ddf8 100644 --- a/core/src/hash/mod.rs +++ b/core/src/hash/mod.rs @@ -752,8 +752,11 @@ pub struct BuildHasherDefault(marker::PhantomData H>); impl BuildHasherDefault { /// Creates a new BuildHasherDefault for Hasher `H`. - #[stable(feature = "build_hasher_default_const_new", since = "1.85.0")] - #[rustc_const_stable(feature = "build_hasher_default_const_new", since = "1.85.0")] + #[unstable( + feature = "build_hasher_default_const_new", + issue = "123197", + reason = "recently added" + )] pub const fn new() -> Self { BuildHasherDefault(marker::PhantomData) } diff --git a/core/src/hint.rs b/core/src/hint.rs index e5c1a64c12ee5..78df51f2bc47d 100644 --- a/core/src/hint.rs +++ b/core/src/hint.rs @@ -310,8 +310,6 @@ pub fn spin_loop() { /// behavior in the calling code. This property makes `black_box` useful for writing code in which /// certain optimizations are not desired, such as benchmarks. /// -///
-/// /// Note however, that `black_box` is only (and can only be) provided on a "best-effort" basis. The /// extent to which it can block optimisations may vary depending upon the platform and code-gen /// backend used. Programs cannot rely on `black_box` for *correctness*, beyond it behaving as the @@ -319,8 +317,6 @@ pub fn spin_loop() { /// This also means that this function does not offer any guarantees for cryptographic or security /// purposes. /// -///
-/// /// [`std::convert::identity`]: crate::convert::identity /// /// # When is this useful? @@ -361,7 +357,7 @@ pub fn spin_loop() { /// ``` /// use std::hint::black_box; /// -/// // Same `contains` function. +/// // Same `contains` function /// fn contains(haystack: &[&str], needle: &str) -> bool { /// haystack.iter().any(|x| x == &needle) /// } @@ -370,13 +366,8 @@ pub fn spin_loop() { /// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"]; /// let needle = "ghi"; /// for _ in 0..10 { -/// // Force the compiler to run `contains`, even though it is a pure function whose -/// // results are unused. -/// black_box(contains( -/// // Prevent the compiler from making assumptions about the input. -/// black_box(&haystack), -/// black_box(needle), -/// )); +/// // Adjust our benchmark loop contents +/// black_box(contains(black_box(&haystack), black_box(needle))); /// } /// } /// ``` @@ -391,88 +382,9 @@ pub fn spin_loop() { /// /// This makes our benchmark much more realistic to how the function would actually be used, where /// arguments are usually not known at compile time and the result is used in some way. -/// -/// # How to use this -/// -/// In practice, `black_box` serves two purposes: -/// -/// 1. It prevents the compiler from making optimizations related to the value returned by `black_box` -/// 2. It forces the value passed to `black_box` to be calculated, even if the return value of `black_box` is unused -/// -/// ``` -/// use std::hint::black_box; -/// -/// let zero = 0; -/// let five = 5; -/// -/// // The compiler will see this and remove the `* five` call, because it knows that multiplying -/// // any integer by 0 will result in 0. -/// let c = zero * five; -/// -/// // Adding `black_box` here disables the compiler's ability to reason about the first operand in the multiplication. -/// // It is forced to assume that it can be any possible number, so it cannot remove the `* five` -/// // operation. -/// let c = black_box(zero) * five; -/// ``` -/// -/// While most cases will not be as clear-cut as the above example, it still illustrates how -/// `black_box` can be used. When benchmarking a function, you usually want to wrap its inputs in -/// `black_box` so the compiler cannot make optimizations that would be unrealistic in real-life -/// use. -/// -/// ``` -/// use std::hint::black_box; -/// -/// // This is a simple function that increments its input by 1. Note that it is pure, meaning it -/// // has no side-effects. This function has no effect if its result is unused. (An example of a -/// // function *with* side-effects is `println!()`.) -/// fn increment(x: u8) -> u8 { -/// x + 1 -/// } -/// -/// // Here, we call `increment` but discard its result. The compiler, seeing this and knowing that -/// // `increment` is pure, will eliminate this function call entirely. This may not be desired, -/// // though, especially if we're trying to track how much time `increment` takes to execute. -/// let _ = increment(black_box(5)); -/// -/// // Here, we force `increment` to be executed. This is because the compiler treats `black_box` -/// // as if it has side-effects, and thus must compute its input. -/// let _ = black_box(increment(black_box(5))); -/// ``` -/// -/// There may be additional situations where you want to wrap the result of a function in -/// `black_box` to force its execution. This is situational though, and may not have any effect -/// (such as when the function returns a zero-sized type such as [`()` unit][unit]). -/// -/// Note that `black_box` has no effect on how its input is treated, only its output. As such, -/// expressions passed to `black_box` may still be optimized: -/// -/// ``` -/// use std::hint::black_box; -/// -/// // The compiler sees this... -/// let y = black_box(5 * 10); -/// -/// // ...as this. As such, it will likely simplify `5 * 10` to just `50`. -/// let _0 = 5 * 10; -/// let y = black_box(_0); -/// ``` -/// -/// In the above example, the `5 * 10` expression is considered distinct from the `black_box` call, -/// and thus is still optimized by the compiler. You can prevent this by moving the multiplication -/// operation outside of `black_box`: -/// -/// ``` -/// use std::hint::black_box; -/// -/// // 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_stable(feature = "const_black_box", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_unstable(feature = "const_black_box", issue = "none")] pub const fn black_box(dummy: T) -> T { crate::intrinsics::black_box(dummy) } @@ -594,143 +506,9 @@ pub const fn black_box(dummy: T) -> T { /// # } /// ``` #[unstable(feature = "hint_must_use", issue = "94745")] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "hint_must_use", issue = "94745"))] #[must_use] // <-- :) #[inline(always)] pub const fn must_use(value: T) -> T { value } - -/// Hints to the compiler that a branch condition is likely to be true. -/// Returns the value passed to it. -/// -/// It can be used with `if` or boolean `match` expressions. -/// -/// When used outside of a branch condition, it may still influence a nearby branch, but -/// probably will not have any effect. -/// -/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to -/// compound expressions, such as `likely(a && b)`. When applied to compound expressions, it has -/// the following effect: -/// ```text -/// likely(!a) => !unlikely(a) -/// likely(a && b) => likely(a) && likely(b) -/// likely(a || b) => a || likely(b) -/// ``` -/// -/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code. -/// -/// # Examples -/// -/// ``` -/// #![feature(likely_unlikely)] -/// use core::hint::likely; -/// -/// fn foo(x: i32) { -/// if likely(x > 0) { -/// println!("this branch is likely to be taken"); -/// } else { -/// println!("this branch is unlikely to be taken"); -/// } -/// -/// match likely(x > 0) { -/// true => println!("this branch is likely to be taken"), -/// false => println!("this branch is unlikely to be taken"), -/// } -/// -/// // Use outside of a branch condition may still influence a nearby branch -/// let cond = likely(x != 0); -/// if cond { -/// println!("this branch is likely to be taken"); -/// } -/// } -/// ``` -/// -/// -#[unstable(feature = "likely_unlikely", issue = "26179")] -#[inline(always)] -pub const fn likely(b: bool) -> bool { - crate::intrinsics::likely(b) -} - -/// Hints to the compiler that a branch condition is unlikely to be true. -/// Returns the value passed to it. -/// -/// It can be used with `if` or boolean `match` expressions. -/// -/// When used outside of a branch condition, it may still influence a nearby branch, but -/// probably will not have any effect. -/// -/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to -/// compound expressions, such as `unlikely(a && b)`. When applied to compound expressions, it has -/// the following effect: -/// ```text -/// unlikely(!a) => !likely(a) -/// unlikely(a && b) => a && unlikely(b) -/// unlikely(a || b) => unlikely(a) || unlikely(b) -/// ``` -/// -/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code. -/// -/// # Examples -/// -/// ``` -/// #![feature(likely_unlikely)] -/// use core::hint::unlikely; -/// -/// fn foo(x: i32) { -/// if unlikely(x > 0) { -/// println!("this branch is unlikely to be taken"); -/// } else { -/// println!("this branch is likely to be taken"); -/// } -/// -/// match unlikely(x > 0) { -/// true => println!("this branch is unlikely to be taken"), -/// false => println!("this branch is likely to be taken"), -/// } -/// -/// // Use outside of a branch condition may still influence a nearby branch -/// let cond = unlikely(x != 0); -/// if cond { -/// println!("this branch is likely to be taken"); -/// } -/// } -/// ``` -#[unstable(feature = "likely_unlikely", issue = "26179")] -#[inline(always)] -pub const fn unlikely(b: bool) -> bool { - crate::intrinsics::unlikely(b) -} - -/// Hints to the compiler that given path is cold, i.e., unlikely to be taken. The compiler may -/// choose to optimize paths that are not cold at the expense of paths that are cold. -/// -/// # Examples -/// -/// ``` -/// #![feature(cold_path)] -/// use core::hint::cold_path; -/// -/// fn foo(x: &[i32]) { -/// if let Some(first) = x.get(0) { -/// // this is the fast path -/// } else { -/// // this path is unlikely -/// cold_path(); -/// } -/// } -/// -/// fn bar(x: i32) -> i32 { -/// match x { -/// 1 => 10, -/// 2 => 100, -/// 3 => { cold_path(); 1000 }, // this branch is unlikely -/// _ => { cold_path(); 10000 }, // this is also unlikely -/// } -/// } -/// ``` -#[unstable(feature = "cold_path", issue = "26179")] -#[inline(always)] -pub const fn cold_path() { - crate::intrinsics::cold_path() -} diff --git a/core/src/intrinsics/fallback.rs b/core/src/intrinsics/fallback.rs deleted file mode 100644 index eec5c4d646d07..0000000000000 --- a/core/src/intrinsics/fallback.rs +++ /dev/null @@ -1,150 +0,0 @@ -#![unstable( - feature = "core_intrinsics_fallbacks", - reason = "The fallbacks will never be stable, as they exist only to be called \ - by the fallback MIR, but they're exported so they can be tested on \ - platforms where the fallback MIR isn't actually used", - issue = "none" -)] -#![allow(missing_docs)] - -#[const_trait] -#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] -pub trait CarryingMulAdd: Copy + 'static { - type Unsigned: Copy + 'static; - fn carrying_mul_add( - self, - multiplicand: Self, - addend: Self, - carry: Self, - ) -> (Self::Unsigned, Self); -} - -macro_rules! impl_carrying_mul_add_by_widening { - ($($t:ident $u:ident $w:ident,)+) => {$( - #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] - impl const CarryingMulAdd for $t { - type Unsigned = $u; - #[inline] - fn carrying_mul_add(self, a: Self, b: Self, c: Self) -> ($u, $t) { - let wide = (self as $w) * (a as $w) + (b as $w) + (c as $w); - (wide as _, (wide >> Self::BITS) as _) - } - } - )+}; -} -impl_carrying_mul_add_by_widening! { - u8 u8 u16, - u16 u16 u32, - u32 u32 u64, - u64 u64 u128, - usize usize UDoubleSize, - i8 u8 i16, - i16 u16 i32, - i32 u32 i64, - i64 u64 i128, - isize usize UDoubleSize, -} - -#[cfg(target_pointer_width = "16")] -type UDoubleSize = u32; -#[cfg(target_pointer_width = "32")] -type UDoubleSize = u64; -#[cfg(target_pointer_width = "64")] -type UDoubleSize = u128; - -#[inline] -const fn wide_mul_u128(a: u128, b: u128) -> (u128, u128) { - #[inline] - const fn to_low_high(x: u128) -> [u128; 2] { - const MASK: u128 = u64::MAX as _; - [x & MASK, x >> 64] - } - #[inline] - const fn from_low_high(x: [u128; 2]) -> u128 { - x[0] | (x[1] << 64) - } - #[inline] - const fn scalar_mul(low_high: [u128; 2], k: u128) -> [u128; 3] { - let [x, c] = to_low_high(k * low_high[0]); - let [y, z] = to_low_high(k * low_high[1] + c); - [x, y, z] - } - let a = to_low_high(a); - let b = to_low_high(b); - let low = scalar_mul(a, b[0]); - let high = scalar_mul(a, b[1]); - let r0 = low[0]; - let [r1, c] = to_low_high(low[1] + high[0]); - let [r2, c] = to_low_high(low[2] + high[1] + c); - let r3 = high[2] + c; - (from_low_high([r0, r1]), from_low_high([r2, r3])) -} - -#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] -impl const CarryingMulAdd for u128 { - type Unsigned = u128; - #[inline] - fn carrying_mul_add(self, b: u128, c: u128, d: u128) -> (u128, u128) { - let (low, mut high) = wide_mul_u128(self, b); - let (low, carry) = u128::overflowing_add(low, c); - high += carry as u128; - let (low, carry) = u128::overflowing_add(low, d); - high += carry as u128; - (low, high) - } -} - -#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] -impl const CarryingMulAdd for i128 { - type Unsigned = u128; - #[inline] - fn carrying_mul_add(self, b: i128, c: i128, d: i128) -> (u128, i128) { - let (low, high) = wide_mul_u128(self as u128, b as u128); - let mut high = high as i128; - high = high.wrapping_add(i128::wrapping_mul(self >> 127, b)); - high = high.wrapping_add(i128::wrapping_mul(self, b >> 127)); - let (low, carry) = u128::overflowing_add(low, c as u128); - high = high.wrapping_add((carry as i128) + (c >> 127)); - let (low, carry) = u128::overflowing_add(low, d as u128); - high = high.wrapping_add((carry as i128) + (d >> 127)); - (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/core/src/intrinsics/mir.rs b/core/src/intrinsics/mir.rs index 55dcf7cd47e97..6539964bc0956 100644 --- a/core/src/intrinsics/mir.rs +++ b/core/src/intrinsics/mir.rs @@ -249,39 +249,6 @@ //! `Call(ret_val = function(arg1, arg2, ...), ReturnTo(next_block), UnwindContinue())`. //! - [`TailCall`] does not have a return destination or next block, so its syntax is just //! `TailCall(function(arg1, arg2, ...))`. -//! -//! #### Debuginfo -//! -//! Debuginfo associates source code variable names (of variables that may not exist any more) with -//! MIR expressions that indicate where the value of that variable is stored. The syntax to do so -//! is: -//! ```text -//! debug source_var_name => expression; -//! ``` -//! Both places and constants are supported in the `expression`. -//! -//! ```rust -//! #![allow(internal_features)] -//! #![feature(core_intrinsics, custom_mir)] -//! -//! use core::intrinsics::mir::*; -//! -//! #[custom_mir(dialect = "built")] -//! fn debuginfo(arg: Option<&i32>) { -//! mir!( -//! // Debuginfo for a source variable `plain_local` that just duplicates `arg`. -//! debug plain_local => arg; -//! // Debuginfo for a source variable `projection` that can be computed by dereferencing -//! // a field of `arg`. -//! debug projection => *Field::<&i32>(Variant(arg, 1), 0); -//! // Debuginfo for a source variable `constant` that always holds the value `5`. -//! debug constant => 5_usize; -//! { -//! Return() -//! } -//! ) -//! } -//! ``` #![unstable( feature = "custom_mir", diff --git a/core/src/intrinsics/mod.rs b/core/src/intrinsics/mod.rs index 3b249c835f237..2f75bfae988f2 100644 --- a/core/src/intrinsics/mod.rs +++ b/core/src/intrinsics/mod.rs @@ -68,7 +68,6 @@ use crate::marker::{DiscriminantKind, Tuple}; use crate::mem::SizedTypeProperties; use crate::{ptr, ub_checks}; -pub mod fallback; pub mod mir; pub mod simd; @@ -78,11 +77,7 @@ pub mod simd; use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, Ordering}; #[stable(feature = "drop_in_place", since = "1.8.0")] -#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] -#[cfg_attr( - not(bootstrap), - rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead" -)] +#[rustc_allowed_through_unstable_modules] #[deprecated(note = "no longer an intrinsic - use `ptr::drop_in_place` directly", since = "1.52.0")] #[inline] pub unsafe fn drop_in_place(to_drop: *mut T) { @@ -1386,7 +1381,7 @@ pub unsafe fn prefetch_write_instruction(_data: *const T, _locality: i32) { #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] #[rustc_nounwind] -pub fn breakpoint() { +pub unsafe fn breakpoint() { unreachable!() } @@ -1436,7 +1431,11 @@ pub fn abort() -> ! { /// reach code marked with this function. /// /// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`]. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0") +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1454,7 +1453,8 @@ pub const unsafe fn unreachable() -> ! { /// own, or if it does not enable any significant optimizations. /// /// The stabilized version of this intrinsic is [`core::hint::assert_unchecked`]. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_assume", since = "1.77.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] @@ -1474,7 +1474,8 @@ pub const unsafe fn assume(b: bool) { /// /// This intrinsic does not have a stable counterpart. #[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +#[cfg(not(bootstrap))] #[rustc_nounwind] #[miri::intrinsic_fallback_is_spec] #[cold] @@ -1491,10 +1492,19 @@ pub const fn cold_path() {} /// any safety invariants. /// /// This intrinsic does not have a stable counterpart. +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_likely", since = "CURRENT_RUSTC_VERSION") +)] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[inline(always)] pub const fn likely(b: bool) -> bool { + #[cfg(bootstrap)] + { + b + } + #[cfg(not(bootstrap))] if b { true } else { @@ -1514,10 +1524,19 @@ pub const fn likely(b: bool) -> bool { /// any safety invariants. /// /// This intrinsic does not have a stable counterpart. +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_likely", since = "CURRENT_RUSTC_VERSION") +)] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[inline(always)] pub const fn unlikely(b: bool) -> bool { + #[cfg(bootstrap)] + { + b + } + #[cfg(not(bootstrap))] if b { cold_path(); true @@ -1537,7 +1556,7 @@ pub const fn unlikely(b: bool) -> bool { /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The public form of this instrinsic is [`bool::select_unpredictable`]. +/// This intrinsic does not have a stable counterpart. #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] @@ -1551,7 +1570,8 @@ pub fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { /// This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_assert_type", since = "1.59.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1563,7 +1583,8 @@ pub const fn assert_inhabited() { /// zero-initialization: This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_assert_type2", since = "1.75.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1574,7 +1595,8 @@ pub const fn assert_zero_valid() { /// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing. /// /// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_assert_type2", since = "1.75.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1590,7 +1612,8 @@ pub const fn assert_mem_uninitialized_valid() { /// any safety invariants. /// /// Consider using [`core::panic::Location::caller`] instead. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_caller_location", since = "1.79.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1607,7 +1630,8 @@ pub const fn caller_location() -> &'static crate::panic::Location<'static> { /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_intrinsic_forget", since = "1.83.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1901,11 +1925,7 @@ pub const fn forget(_: T) { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] -#[cfg_attr( - not(bootstrap), - rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" -)] +#[rustc_allowed_through_unstable_modules] #[rustc_const_stable(feature = "const_transmute", since = "1.56.0")] #[rustc_diagnostic_item = "transmute"] #[rustc_nounwind] @@ -1924,7 +1944,8 @@ pub const unsafe fn transmute(_src: Src) -> Dst { /// /// This is not expected to ever be exposed directly to users, rather it /// may eventually be exposed through some more-constrained API. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_transmute", since = "1.56.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1945,7 +1966,8 @@ pub const unsafe fn transmute_unchecked(_src: Src) -> Dst { /// any safety invariants. /// /// The stabilized version of this intrinsic is [`mem::needs_drop`](crate::mem::needs_drop). -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_needs_drop", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1970,7 +1992,8 @@ pub const fn needs_drop() -> bool { /// /// The stabilized version of this intrinsic is [`pointer::offset`]. #[must_use = "returns a new pointer rather than modifying its argument"] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -1992,7 +2015,8 @@ pub const unsafe fn offset(_dst: Ptr, _offset: Delta) -> Ptr { /// /// The stabilized version of this intrinsic is [`pointer::wrapping_offset`]. #[must_use = "returns a new pointer rather than modifying its argument"] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -2015,1062 +2039,709 @@ pub fn ptr_mask(_ptr: *const T, _mask: usize) -> *const T { unreachable!() } -/// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with -/// a size of `count` * `size_of::()` and an alignment of -/// `min_align_of::()` -/// -/// The volatile parameter is set to `true`, so it will not be optimized out -/// unless size is equal to zero. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn volatile_copy_nonoverlapping_memory(_dst: *mut T, _src: *const T, _count: usize) { - unreachable!() +extern "rust-intrinsic" { + /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with + /// a size of `count` * `size_of::()` and an alignment of + /// `min_align_of::()` + /// + /// The volatile parameter is set to `true`, so it will not be optimized out + /// unless size is equal to zero. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn volatile_copy_nonoverlapping_memory(dst: *mut T, src: *const T, count: usize); + /// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with + /// a size of `count * size_of::()` and an alignment of + /// `min_align_of::()` + /// + /// The volatile parameter is set to `true`, so it will not be optimized out + /// unless size is equal to zero. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn volatile_copy_memory(dst: *mut T, src: *const T, count: usize); + /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a + /// size of `count * size_of::()` and an alignment of + /// `min_align_of::()`. + /// + /// The volatile parameter is set to `true`, so it will not be optimized out + /// unless size is equal to zero. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn volatile_set_memory(dst: *mut T, val: u8, count: usize); + + /// Performs a volatile load from the `src` pointer. + /// + /// The stabilized version of this intrinsic is [`core::ptr::read_volatile`]. + #[rustc_nounwind] + pub fn volatile_load(src: *const T) -> T; + /// Performs a volatile store to the `dst` pointer. + /// + /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`]. + #[rustc_nounwind] + pub fn volatile_store(dst: *mut T, val: T); + + /// Performs a volatile load from the `src` pointer + /// The pointer is not required to be aligned. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_load"] + pub fn unaligned_volatile_load(src: *const T) -> T; + /// Performs a volatile store to the `dst` pointer. + /// The pointer is not required to be aligned. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] + pub fn unaligned_volatile_store(dst: *mut T, val: T); + + /// Returns the square root of an `f16` + /// + /// The stabilized version of this intrinsic is + /// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf16(x: f16) -> f16; + /// Returns the square root of an `f32` + /// + /// The stabilized version of this intrinsic is + /// [`f32::sqrt`](../../std/primitive.f32.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf32(x: f32) -> f32; + /// Returns the square root of an `f64` + /// + /// The stabilized version of this intrinsic is + /// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf64(x: f64) -> f64; + /// Returns the square root of an `f128` + /// + /// The stabilized version of this intrinsic is + /// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf128(x: f128) -> f128; + + /// Raises an `f16` to an integer power. + /// + /// The stabilized version of this intrinsic is + /// [`f16::powi`](../../std/primitive.f16.html#method.powi) + #[rustc_nounwind] + pub fn powif16(a: f16, x: i32) -> f16; + /// Raises an `f32` to an integer power. + /// + /// The stabilized version of this intrinsic is + /// [`f32::powi`](../../std/primitive.f32.html#method.powi) + #[rustc_nounwind] + pub fn powif32(a: f32, x: i32) -> f32; + /// Raises an `f64` to an integer power. + /// + /// The stabilized version of this intrinsic is + /// [`f64::powi`](../../std/primitive.f64.html#method.powi) + #[rustc_nounwind] + pub fn powif64(a: f64, x: i32) -> f64; + /// Raises an `f128` to an integer power. + /// + /// The stabilized version of this intrinsic is + /// [`f128::powi`](../../std/primitive.f128.html#method.powi) + #[rustc_nounwind] + pub fn powif128(a: f128, x: i32) -> f128; + + /// Returns the sine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::sin`](../../std/primitive.f16.html#method.sin) + #[rustc_nounwind] + pub fn sinf16(x: f16) -> f16; + /// Returns the sine of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::sin`](../../std/primitive.f32.html#method.sin) + #[rustc_nounwind] + pub fn sinf32(x: f32) -> f32; + /// Returns the sine of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::sin`](../../std/primitive.f64.html#method.sin) + #[rustc_nounwind] + pub fn sinf64(x: f64) -> f64; + /// Returns the sine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::sin`](../../std/primitive.f128.html#method.sin) + #[rustc_nounwind] + pub fn sinf128(x: f128) -> f128; + + /// Returns the cosine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::cos`](../../std/primitive.f16.html#method.cos) + #[rustc_nounwind] + pub fn cosf16(x: f16) -> f16; + /// Returns the cosine of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::cos`](../../std/primitive.f32.html#method.cos) + #[rustc_nounwind] + pub fn cosf32(x: f32) -> f32; + /// Returns the cosine of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::cos`](../../std/primitive.f64.html#method.cos) + #[rustc_nounwind] + pub fn cosf64(x: f64) -> f64; + /// Returns the cosine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::cos`](../../std/primitive.f128.html#method.cos) + #[rustc_nounwind] + pub fn cosf128(x: f128) -> f128; + + /// Raises an `f16` to an `f16` power. + /// + /// The stabilized version of this intrinsic is + /// [`f16::powf`](../../std/primitive.f16.html#method.powf) + #[rustc_nounwind] + pub fn powf16(a: f16, x: f16) -> f16; + /// Raises an `f32` to an `f32` power. + /// + /// The stabilized version of this intrinsic is + /// [`f32::powf`](../../std/primitive.f32.html#method.powf) + #[rustc_nounwind] + pub fn powf32(a: f32, x: f32) -> f32; + /// Raises an `f64` to an `f64` power. + /// + /// The stabilized version of this intrinsic is + /// [`f64::powf`](../../std/primitive.f64.html#method.powf) + #[rustc_nounwind] + pub fn powf64(a: f64, x: f64) -> f64; + /// Raises an `f128` to an `f128` power. + /// + /// The stabilized version of this intrinsic is + /// [`f128::powf`](../../std/primitive.f128.html#method.powf) + #[rustc_nounwind] + pub fn powf128(a: f128, x: f128) -> f128; + + /// Returns the exponential of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp`](../../std/primitive.f16.html#method.exp) + #[rustc_nounwind] + pub fn expf16(x: f16) -> f16; + /// Returns the exponential of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::exp`](../../std/primitive.f32.html#method.exp) + #[rustc_nounwind] + pub fn expf32(x: f32) -> f32; + /// Returns the exponential of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::exp`](../../std/primitive.f64.html#method.exp) + #[rustc_nounwind] + pub fn expf64(x: f64) -> f64; + /// Returns the exponential of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp`](../../std/primitive.f128.html#method.exp) + #[rustc_nounwind] + pub fn expf128(x: f128) -> f128; + + /// Returns 2 raised to the power of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f16(x: f16) -> f16; + /// Returns 2 raised to the power of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::exp2`](../../std/primitive.f32.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f32(x: f32) -> f32; + /// Returns 2 raised to the power of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f64(x: f64) -> f64; + /// Returns 2 raised to the power of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f128(x: f128) -> f128; + + /// Returns the natural logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ln`](../../std/primitive.f16.html#method.ln) + #[rustc_nounwind] + pub fn logf16(x: f16) -> f16; + /// Returns the natural logarithm of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::ln`](../../std/primitive.f32.html#method.ln) + #[rustc_nounwind] + pub fn logf32(x: f32) -> f32; + /// Returns the natural logarithm of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::ln`](../../std/primitive.f64.html#method.ln) + #[rustc_nounwind] + pub fn logf64(x: f64) -> f64; + /// Returns the natural logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ln`](../../std/primitive.f128.html#method.ln) + #[rustc_nounwind] + pub fn logf128(x: f128) -> f128; + + /// Returns the base 10 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log10`](../../std/primitive.f16.html#method.log10) + #[rustc_nounwind] + pub fn log10f16(x: f16) -> f16; + /// Returns the base 10 logarithm of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::log10`](../../std/primitive.f32.html#method.log10) + #[rustc_nounwind] + pub fn log10f32(x: f32) -> f32; + /// Returns the base 10 logarithm of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::log10`](../../std/primitive.f64.html#method.log10) + #[rustc_nounwind] + pub fn log10f64(x: f64) -> f64; + /// Returns the base 10 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log10`](../../std/primitive.f128.html#method.log10) + #[rustc_nounwind] + pub fn log10f128(x: f128) -> f128; + + /// Returns the base 2 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log2`](../../std/primitive.f16.html#method.log2) + #[rustc_nounwind] + pub fn log2f16(x: f16) -> f16; + /// Returns the base 2 logarithm of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::log2`](../../std/primitive.f32.html#method.log2) + #[rustc_nounwind] + pub fn log2f32(x: f32) -> f32; + /// Returns the base 2 logarithm of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::log2`](../../std/primitive.f64.html#method.log2) + #[rustc_nounwind] + pub fn log2f64(x: f64) -> f64; + /// Returns the base 2 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log2`](../../std/primitive.f128.html#method.log2) + #[rustc_nounwind] + pub fn log2f128(x: f128) -> f128; + + /// Returns `a * b + c` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf16(a: f16, b: f16, c: f16) -> f16; + /// Returns `a * b + c` for `f32` values. + /// + /// The stabilized version of this intrinsic is + /// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; + /// Returns `a * b + c` for `f64` values. + /// + /// The stabilized version of this intrinsic is + /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; + /// Returns `a * b + c` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; + + /// Returns `a * b + c` for `f16` values, 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. + #[rustc_nounwind] + pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; + /// Returns `a * b + c` for `f32` values, 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. + #[rustc_nounwind] + pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; + /// Returns `a * b + c` for `f64` values, 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. + #[rustc_nounwind] + pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; + /// Returns `a * b + c` for `f128` values, 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. + #[rustc_nounwind] + pub fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; + + /// Returns the largest integer less than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::floor`](../../std/primitive.f16.html#method.floor) + #[rustc_nounwind] + pub fn floorf16(x: f16) -> f16; + /// Returns the largest integer less than or equal to an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::floor`](../../std/primitive.f32.html#method.floor) + #[rustc_nounwind] + pub fn floorf32(x: f32) -> f32; + /// Returns the largest integer less than or equal to an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::floor`](../../std/primitive.f64.html#method.floor) + #[rustc_nounwind] + pub fn floorf64(x: f64) -> f64; + /// Returns the largest integer less than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::floor`](../../std/primitive.f128.html#method.floor) + #[rustc_nounwind] + pub fn floorf128(x: f128) -> f128; + + /// Returns the smallest integer greater than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf16(x: f16) -> f16; + /// Returns the smallest integer greater than or equal to an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::ceil`](../../std/primitive.f32.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf32(x: f32) -> f32; + /// Returns the smallest integer greater than or equal to an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf64(x: f64) -> f64; + /// Returns the smallest integer greater than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf128(x: f128) -> f128; + + /// Returns the integer part of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) + #[rustc_nounwind] + pub fn truncf16(x: f16) -> f16; + /// Returns the integer part of an `f32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::trunc`](../../std/primitive.f32.html#method.trunc) + #[rustc_nounwind] + pub fn truncf32(x: f32) -> f32; + /// Returns the integer part of an `f64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) + #[rustc_nounwind] + pub fn truncf64(x: f64) -> f64; + /// Returns the integer part of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) + #[rustc_nounwind] + pub fn truncf128(x: f128) -> f128; + + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf16(x: f16) -> f16; + /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf32` and `roundevenf32`. + /// + /// The stabilized version of this intrinsic is + /// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf32(x: f32) -> f32; + /// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf64` and `roundevenf64`. + /// + /// The stabilized version of this intrinsic is + /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf128(x: f128) -> f128; + + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf16(x: f16) -> f16; + /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf32(x: f32) -> f32; + /// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf128(x: f128) -> f128; + + /// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round`](../../std/primitive.f16.html#method.round) + #[rustc_nounwind] + pub fn roundf16(x: f16) -> f16; + /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f32::round`](../../std/primitive.f32.html#method.round) + #[rustc_nounwind] + pub fn roundf32(x: f32) -> f32; + /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f64::round`](../../std/primitive.f64.html#method.round) + #[rustc_nounwind] + pub fn roundf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round`](../../std/primitive.f128.html#method.round) + #[rustc_nounwind] + pub fn roundf128(x: f128) -> f128; + + /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf16(x: f16) -> f16; + /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf32(x: f32) -> f32; + /// Returns the nearest integer to an `f64`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf128(x: f128) -> f128; + + /// Float addition that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn fadd_fast(a: T, b: T) -> T; + + /// Float subtraction that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn fsub_fast(a: T, b: T) -> T; + + /// Float multiplication that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn fmul_fast(a: T, b: T) -> T; + + /// Float division that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn fdiv_fast(a: T, b: T) -> T; + + /// Float remainder that allows optimizations based on algebraic rules. + /// May assume inputs are finite. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn frem_fast(a: T, b: T) -> T; + + /// Converts with LLVM’s fptoui/fptosi, which may return undef for values out of range + /// () + /// + /// Stabilized as [`f32::to_int_unchecked`] and [`f64::to_int_unchecked`]. + #[rustc_nounwind] + pub fn float_to_int_unchecked(value: Float) -> Int; } -/// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with -/// a size of `count * size_of::()` and an alignment of -/// `min_align_of::()` -/// -/// The volatile parameter is set to `true`, so it will not be optimized out -/// unless size is equal to zero. + +/// Float addition that allows optimizations based on algebraic rules. /// /// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] #[rustc_nounwind] -pub unsafe fn volatile_copy_memory(_dst: *mut T, _src: *const T, _count: usize) { - unreachable!() -} -/// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a -/// size of `count * size_of::()` and an alignment of -/// `min_align_of::()`. -/// -/// The volatile parameter is set to `true`, so it will not be optimized out -/// unless size is equal to zero. -/// -/// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn volatile_set_memory(_dst: *mut T, _val: u8, _count: usize) { - unreachable!() +pub fn fadd_algebraic(_a: T, _b: T) -> T { + unimplemented!() } -/// Performs a volatile load from the `src` pointer. +/// Float subtraction that allows optimizations based on algebraic rules. /// -/// The stabilized version of this intrinsic is [`core::ptr::read_volatile`]. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] +/// This intrinsic does not have a stable counterpart. #[rustc_nounwind] -pub unsafe fn volatile_load(_src: *const T) -> T { - unreachable!() -} -/// Performs a volatile store to the `dst` pointer. -/// -/// The stabilized version of this intrinsic is [`core::ptr::write_volatile`]. #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn volatile_store(_dst: *mut T, _val: T) { - unreachable!() +pub fn fsub_algebraic(_a: T, _b: T) -> T { + unimplemented!() } -/// Performs a volatile load from the `src` pointer -/// The pointer is not required to be aligned. +/// Float multiplication that allows optimizations based on algebraic rules. /// /// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] #[rustc_nounwind] -#[rustc_diagnostic_item = "intrinsics_unaligned_volatile_load"] -pub unsafe fn unaligned_volatile_load(_src: *const T) -> T { - unreachable!() -} -/// Performs a volatile store to the `dst` pointer. -/// The pointer is not required to be aligned. -/// -/// This intrinsic does not have a stable counterpart. #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -#[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] -pub unsafe fn unaligned_volatile_store(_dst: *mut T, _val: T) { - unreachable!() +pub fn fmul_algebraic(_a: T, _b: T) -> T { + unimplemented!() } -/// Returns the square root of an `f16` +/// Float division that allows optimizations based on algebraic rules. /// -/// The stabilized version of this intrinsic is -/// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] +/// This intrinsic does not have a stable counterpart. #[rustc_nounwind] -pub unsafe fn sqrtf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the square root of an `f32` -/// -/// The stabilized version of this intrinsic is -/// [`f32::sqrt`](../../std/primitive.f32.html#method.sqrt) #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn sqrtf32(_x: f32) -> f32 { - unreachable!() +pub fn fdiv_algebraic(_a: T, _b: T) -> T { + unimplemented!() } -/// Returns the square root of an `f64` + +/// Float remainder that allows optimizations based on algebraic rules. /// -/// The stabilized version of this intrinsic is -/// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] +/// This intrinsic does not have a stable counterpart. #[rustc_nounwind] -pub unsafe fn sqrtf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the square root of an `f128` -/// -/// The stabilized version of this intrinsic is -/// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn sqrtf128(_x: f128) -> f128 { - unreachable!() +pub fn frem_algebraic(_a: T, _b: T) -> T { + unimplemented!() } -/// Raises an `f16` to an integer power. -/// -/// The stabilized version of this intrinsic is -/// [`f16::powi`](../../std/primitive.f16.html#method.powi) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn powif16(_a: f16, _x: i32) -> f16 { - unreachable!() -} -/// Raises an `f32` to an integer power. +/// Returns the number of bits set in an integer type `T` /// -/// The stabilized version of this intrinsic is -/// [`f32::powi`](../../std/primitive.f32.html#method.powi) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn powif32(_a: f32, _x: i32) -> f32 { - unreachable!() -} -/// Raises an `f64` to an integer power. +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f64::powi`](../../std/primitive.f64.html#method.powi) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] +/// The stabilized versions of this intrinsic are available on the integer +/// primitives via the `count_ones` method. For example, +/// [`u32::count_ones`] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ctpop", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] -pub unsafe fn powif64(_a: f64, _x: i32) -> f64 { - unreachable!() -} -/// Raises an `f128` to an integer power. -/// -/// The stabilized version of this intrinsic is -/// [`f128::powi`](../../std/primitive.f128.html#method.powi) #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn powif128(_a: f128, _x: i32) -> f128 { - unreachable!() +pub const fn ctpop(_x: T) -> u32 { + unimplemented!() } -/// Returns the sine of an `f16`. +/// Returns the number of leading unset bits (zeroes) in an integer type `T`. /// -/// The stabilized version of this intrinsic is -/// [`f16::sin`](../../std/primitive.f16.html#method.sin) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn sinf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the sine of an `f32`. +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f32::sin`](../../std/primitive.f32.html#method.sin) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn sinf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the sine of an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::sin`](../../std/primitive.f64.html#method.sin) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn sinf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the sine of an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::sin`](../../std/primitive.f128.html#method.sin) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn sinf128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the cosine of an `f16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::cos`](../../std/primitive.f16.html#method.cos) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn cosf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the cosine of an `f32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::cos`](../../std/primitive.f32.html#method.cos) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn cosf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the cosine of an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::cos`](../../std/primitive.f64.html#method.cos) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn cosf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the cosine of an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::cos`](../../std/primitive.f128.html#method.cos) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn cosf128(_x: f128) -> f128 { - unreachable!() -} - -/// Raises an `f16` to an `f16` power. -/// -/// The stabilized version of this intrinsic is -/// [`f16::powf`](../../std/primitive.f16.html#method.powf) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn powf16(_a: f16, _x: f16) -> f16 { - unreachable!() -} -/// Raises an `f32` to an `f32` power. -/// -/// The stabilized version of this intrinsic is -/// [`f32::powf`](../../std/primitive.f32.html#method.powf) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn powf32(_a: f32, _x: f32) -> f32 { - unreachable!() -} -/// Raises an `f64` to an `f64` power. -/// -/// The stabilized version of this intrinsic is -/// [`f64::powf`](../../std/primitive.f64.html#method.powf) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn powf64(_a: f64, _x: f64) -> f64 { - unreachable!() -} -/// Raises an `f128` to an `f128` power. -/// -/// The stabilized version of this intrinsic is -/// [`f128::powf`](../../std/primitive.f128.html#method.powf) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn powf128(_a: f128, _x: f128) -> f128 { - unreachable!() -} - -/// Returns the exponential of an `f16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::exp`](../../std/primitive.f16.html#method.exp) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn expf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the exponential of an `f32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::exp`](../../std/primitive.f32.html#method.exp) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn expf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the exponential of an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::exp`](../../std/primitive.f64.html#method.exp) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn expf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the exponential of an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::exp`](../../std/primitive.f128.html#method.exp) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn expf128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns 2 raised to the power of an `f16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn exp2f16(_x: f16) -> f16 { - unreachable!() -} -/// Returns 2 raised to the power of an `f32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::exp2`](../../std/primitive.f32.html#method.exp2) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn exp2f32(_x: f32) -> f32 { - unreachable!() -} -/// Returns 2 raised to the power of an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn exp2f64(_x: f64) -> f64 { - unreachable!() -} -/// Returns 2 raised to the power of an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn exp2f128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the natural logarithm of an `f16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::ln`](../../std/primitive.f16.html#method.ln) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn logf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the natural logarithm of an `f32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::ln`](../../std/primitive.f32.html#method.ln) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn logf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the natural logarithm of an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::ln`](../../std/primitive.f64.html#method.ln) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn logf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the natural logarithm of an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::ln`](../../std/primitive.f128.html#method.ln) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn logf128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the base 10 logarithm of an `f16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::log10`](../../std/primitive.f16.html#method.log10) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn log10f16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the base 10 logarithm of an `f32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::log10`](../../std/primitive.f32.html#method.log10) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn log10f32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the base 10 logarithm of an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::log10`](../../std/primitive.f64.html#method.log10) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn log10f64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the base 10 logarithm of an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::log10`](../../std/primitive.f128.html#method.log10) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn log10f128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the base 2 logarithm of an `f16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::log2`](../../std/primitive.f16.html#method.log2) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn log2f16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the base 2 logarithm of an `f32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::log2`](../../std/primitive.f32.html#method.log2) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn log2f32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the base 2 logarithm of an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::log2`](../../std/primitive.f64.html#method.log2) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn log2f64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the base 2 logarithm of an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::log2`](../../std/primitive.f128.html#method.log2) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn log2f128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns `a * b + c` for `f16` values. -/// -/// The stabilized version of this intrinsic is -/// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fmaf16(_a: f16, _b: f16, _c: f16) -> f16 { - unreachable!() -} -/// Returns `a * b + c` for `f32` values. -/// -/// The stabilized version of this intrinsic is -/// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fmaf32(_a: f32, _b: f32, _c: f32) -> f32 { - unreachable!() -} -/// Returns `a * b + c` for `f64` values. -/// -/// The stabilized version of this intrinsic is -/// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fmaf64(_a: f64, _b: f64, _c: f64) -> f64 { - unreachable!() -} -/// Returns `a * b + c` for `f128` values. -/// -/// The stabilized version of this intrinsic is -/// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fmaf128(_a: f128, _b: f128, _c: f128) -> f128 { - unreachable!() -} - -/// Returns `a * b + c` for `f16` values, 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. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fmuladdf16(_a: f16, _b: f16, _c: f16) -> f16 { - unreachable!() -} -/// Returns `a * b + c` for `f32` values, 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. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fmuladdf32(_a: f32, _b: f32, _c: f32) -> f32 { - unreachable!() -} -/// Returns `a * b + c` for `f64` values, 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. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fmuladdf64(_a: f64, _b: f64, _c: f64) -> f64 { - unreachable!() -} -/// Returns `a * b + c` for `f128` values, 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. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fmuladdf128(_a: f128, _b: f128, _c: f128) -> f128 { - unreachable!() -} - -/// Returns the largest integer less than or equal to an `f16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::floor`](../../std/primitive.f16.html#method.floor) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn floorf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the largest integer less than or equal to an `f32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::floor`](../../std/primitive.f32.html#method.floor) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn floorf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the largest integer less than or equal to an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::floor`](../../std/primitive.f64.html#method.floor) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn floorf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the largest integer less than or equal to an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::floor`](../../std/primitive.f128.html#method.floor) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn floorf128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the smallest integer greater than or equal to an `f16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn ceilf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the smallest integer greater than or equal to an `f32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::ceil`](../../std/primitive.f32.html#method.ceil) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn ceilf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the smallest integer greater than or equal to an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn ceilf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the smallest integer greater than or equal to an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn ceilf128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the integer part of an `f16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn truncf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the integer part of an `f32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::trunc`](../../std/primitive.f32.html#method.trunc) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn truncf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the integer part of an `f64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn truncf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the integer part of an `f128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn truncf128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, -/// so this rounds half-way cases to the number with an even least significant digit. -/// -/// May raise an inexact floating-point exception if the argument is not an integer. -/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions -/// cannot actually be utilized from Rust code. -/// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`. -/// -/// The stabilized version of this intrinsic is -/// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn rintf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, -/// so this rounds half-way cases to the number with an even least significant digit. -/// -/// May raise an inexact floating-point exception if the argument is not an integer. -/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions -/// cannot actually be utilized from Rust code. -/// In other words, this intrinsic is equivalent in behavior to `nearbyintf32` and `roundevenf32`. -/// -/// The stabilized version of this intrinsic is -/// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn rintf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, -/// so this rounds half-way cases to the number with an even least significant digit. -/// -/// May raise an inexact floating-point exception if the argument is not an integer. -/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions -/// cannot actually be utilized from Rust code. -/// In other words, this intrinsic is equivalent in behavior to `nearbyintf64` and `roundevenf64`. -/// -/// The stabilized version of this intrinsic is -/// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn rintf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, -/// so this rounds half-way cases to the number with an even least significant digit. -/// -/// May raise an inexact floating-point exception if the argument is not an integer. -/// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions -/// cannot actually be utilized from Rust code. -/// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`. -/// -/// The stabilized version of this intrinsic is -/// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn rintf128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, -/// so this rounds half-way cases to the number with an even least significant digit. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn nearbyintf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, -/// so this rounds half-way cases to the number with an even least significant digit. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn nearbyintf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the nearest integer to an `f64`. Changing the rounding mode is not possible in Rust, -/// so this rounds half-way cases to the number with an even least significant digit. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn nearbyintf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, -/// so this rounds half-way cases to the number with an even least significant digit. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn nearbyintf128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. -/// -/// The stabilized version of this intrinsic is -/// [`f16::round`](../../std/primitive.f16.html#method.round) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn roundf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. -/// -/// The stabilized version of this intrinsic is -/// [`f32::round`](../../std/primitive.f32.html#method.round) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn roundf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. -/// -/// The stabilized version of this intrinsic is -/// [`f64::round`](../../std/primitive.f64.html#method.round) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn roundf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. -/// -/// The stabilized version of this intrinsic is -/// [`f128::round`](../../std/primitive.f128.html#method.round) -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn roundf128(_x: f128) -> f128 { - unreachable!() -} - -/// Returns the nearest integer to an `f16`. Rounds half-way cases to the number -/// with an even least significant digit. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn roundevenf16(_x: f16) -> f16 { - unreachable!() -} -/// Returns the nearest integer to an `f32`. Rounds half-way cases to the number -/// with an even least significant digit. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn roundevenf32(_x: f32) -> f32 { - unreachable!() -} -/// Returns the nearest integer to an `f64`. Rounds half-way cases to the number -/// with an even least significant digit. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn roundevenf64(_x: f64) -> f64 { - unreachable!() -} -/// Returns the nearest integer to an `f128`. Rounds half-way cases to the number -/// with an even least significant digit. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn roundevenf128(_x: f128) -> f128 { - unreachable!() -} - -/// Float addition that allows optimizations based on algebraic rules. -/// May assume inputs are finite. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fadd_fast(_a: T, _b: T) -> T { - unreachable!() -} - -/// Float subtraction that allows optimizations based on algebraic rules. -/// May assume inputs are finite. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fsub_fast(_a: T, _b: T) -> T { - unreachable!() -} - -/// Float multiplication that allows optimizations based on algebraic rules. -/// May assume inputs are finite. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fmul_fast(_a: T, _b: T) -> T { - unreachable!() -} - -/// Float division that allows optimizations based on algebraic rules. -/// May assume inputs are finite. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn fdiv_fast(_a: T, _b: T) -> T { - unreachable!() -} - -/// Float remainder that allows optimizations based on algebraic rules. -/// May assume inputs are finite. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn frem_fast(_a: T, _b: T) -> T { - unreachable!() -} - -/// Converts with LLVM’s fptoui/fptosi, which may return undef for values out of range -/// () -/// -/// Stabilized as [`f32::to_int_unchecked`] and [`f64::to_int_unchecked`]. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn float_to_int_unchecked(_value: Float) -> Int { - unreachable!() -} - -/// Float addition that allows optimizations based on algebraic rules. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_nounwind] -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub fn fadd_algebraic(_a: T, _b: T) -> T { - unimplemented!() -} - -/// Float subtraction that allows optimizations based on algebraic rules. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_nounwind] -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub fn fsub_algebraic(_a: T, _b: T) -> T { - unimplemented!() -} - -/// Float multiplication that allows optimizations based on algebraic rules. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_nounwind] -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub fn fmul_algebraic(_a: T, _b: T) -> T { - unimplemented!() -} - -/// Float division that allows optimizations based on algebraic rules. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_nounwind] -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub fn fdiv_algebraic(_a: T, _b: T) -> T { - unimplemented!() -} - -/// Float remainder that allows optimizations based on algebraic rules. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_nounwind] -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub fn frem_algebraic(_a: T, _b: T) -> T { - unimplemented!() -} - -/// Returns the number of bits set in an integer type `T` -/// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. -/// -/// The stabilized versions of this intrinsic are available on the integer -/// primitives via the `count_ones` method. For example, -/// [`u32::count_ones`] -#[rustc_intrinsic_const_stable_indirect] -#[rustc_nounwind] -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub const fn ctpop(_x: T) -> u32 { - unimplemented!() -} - -/// Returns the number of leading unset bits (zeroes) in an integer type `T`. -/// -/// Note that, unlike most intrinsics, this is safe to call; -/// it does not require an `unsafe` block. -/// Therefore, implementations must not require the user to uphold -/// any safety invariants. -/// -/// The stabilized versions of this intrinsic are available on the integer -/// primitives via the `leading_zeros` method. For example, -/// [`u32::leading_zeros`] +/// The stabilized versions of this intrinsic are available on the integer +/// primitives via the `leading_zeros` method. For example, +/// [`u32::leading_zeros`] /// /// # Examples /// @@ -3097,7 +2768,8 @@ pub const fn ctpop(_x: T) -> u32 { /// let num_leading = ctlz(x); /// assert_eq!(num_leading, 16); /// ``` -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ctlz", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3122,7 +2794,8 @@ pub const fn ctlz(_x: T) -> u32 { /// let num_leading = unsafe { ctlz_nonzero(x) }; /// assert_eq!(num_leading, 3); /// ``` -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "constctlz", since = "1.50.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3166,7 +2839,8 @@ pub const unsafe fn ctlz_nonzero(_x: T) -> u32 { /// let num_trailing = cttz(x); /// assert_eq!(num_trailing, 16); /// ``` -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cttz", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3191,7 +2865,8 @@ pub const fn cttz(_x: T) -> u32 { /// let num_trailing = unsafe { cttz_nonzero(x) }; /// assert_eq!(num_trailing, 3); /// ``` -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cttz_nonzero", since = "1.53.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3209,7 +2884,8 @@ pub const unsafe fn cttz_nonzero(_x: T) -> u32 { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `swap_bytes` method. For example, /// [`u32::swap_bytes`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_bswap", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3227,7 +2903,8 @@ pub const fn bswap(_x: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `reverse_bits` method. For example, /// [`u32::reverse_bits`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_bitreverse", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3242,32 +2919,13 @@ pub const fn bitreverse(_x: T) -> T { /// large and difficult to optimize. /// /// The stabilized version of this intrinsic is [`Ord::cmp`]. +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_three_way_compare", issue = "none"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn three_way_compare(_lhs: T, _rhss: T) -> crate::cmp::Ordering { 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(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; @@ -3278,7 +2936,8 @@ pub const unsafe fn disjoint_bitor(a: T, b: T /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `overflowing_add` method. For example, /// [`u32::overflowing_add`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_overflow", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3296,7 +2955,8 @@ pub const fn add_with_overflow(_x: T, _y: T) -> (T, bool) { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `overflowing_sub` method. For example, /// [`u32::overflowing_sub`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_overflow", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3314,7 +2974,8 @@ pub const fn sub_with_overflow(_x: T, _y: T) -> (T, bool) { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `overflowing_mul` method. For example, /// [`u32::overflowing_mul`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_overflow", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3322,38 +2983,11 @@ pub const fn mul_with_overflow(_x: T, _y: T) -> (T, bool) { unimplemented!() } -/// Performs full-width multiplication and addition with a carry: -/// `multiplier * multiplicand + addend + carry`. -/// -/// This is possible without any overflow. For `uN`: -/// MAX * MAX + MAX + MAX -/// => (2ⁿ-1) × (2ⁿ-1) + (2ⁿ-1) + (2ⁿ-1) -/// => (2²ⁿ - 2ⁿ⁺¹ + 1) + (2ⁿ⁺¹ - 2) -/// => 2²ⁿ - 1 -/// -/// For `iN`, the upper bound is MIN * MIN + MAX + MAX => 2²ⁿ⁻² + 2ⁿ - 2, -/// and the lower bound is MAX * MIN + MIN + MIN => -2²ⁿ⁻² - 2ⁿ + 2ⁿ⁺¹. -/// -/// This currently supports unsigned integers *only*, no signed ones. -/// The stabilized versions of this intrinsic are available on integers. -#[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_const_unstable(feature = "const_carrying_mul_add", issue = "85532")] -#[rustc_nounwind] -#[rustc_intrinsic] -#[miri::intrinsic_fallback_is_spec] -pub const fn carrying_mul_add, U>( - multiplier: T, - multiplicand: T, - addend: T, - carry: T, -) -> (U, T) { - multiplier.carrying_mul_add(multiplicand, addend, carry) -} - /// Performs an exact division, resulting in undefined behavior where /// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1` /// /// This intrinsic does not have a stable counterpart. +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_exact_div", issue = "none"))] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3367,7 +3001,8 @@ pub const unsafe fn exact_div(_x: T, _y: T) -> T { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_div` method. For example, /// [`u32::checked_div`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_unchecked_div", since = "1.52.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3380,7 +3015,8 @@ pub const unsafe fn unchecked_div(_x: T, _y: T) -> T { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_rem` method. For example, /// [`u32::checked_rem`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_unchecked_rem", since = "1.52.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3394,7 +3030,8 @@ pub const unsafe fn unchecked_rem(_x: T, _y: T) -> T { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shl` method. For example, /// [`u32::checked_shl`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3407,7 +3044,8 @@ pub const unsafe fn unchecked_shl(_x: T, _y: U) -> T { /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shr` method. For example, /// [`u32::checked_shr`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3420,7 +3058,8 @@ pub const unsafe fn unchecked_shr(_x: T, _y: U) -> T { /// /// The stable counterpart of this intrinsic is `unchecked_add` on the various /// integer types, such as [`u16::unchecked_add`] and [`i64::unchecked_add`]. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "unchecked_math", since = "1.79.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3433,7 +3072,8 @@ pub const unsafe fn unchecked_add(_x: T, _y: T) -> T { /// /// The stable counterpart of this intrinsic is `unchecked_sub` on the various /// integer types, such as [`u16::unchecked_sub`] and [`i64::unchecked_sub`]. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "unchecked_math", since = "1.79.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3446,7 +3086,8 @@ pub const unsafe fn unchecked_sub(_x: T, _y: T) -> T { /// /// The stable counterpart of this intrinsic is `unchecked_mul` on the various /// integer types, such as [`u16::unchecked_mul`] and [`i64::unchecked_mul`]. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "unchecked_math", since = "1.79.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3464,7 +3105,8 @@ pub const unsafe fn unchecked_mul(_x: T, _y: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `rotate_left` method. For example, /// [`u32::rotate_left`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_rotate", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3482,7 +3124,8 @@ pub const fn rotate_left(_x: T, _shift: u32) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `rotate_right` method. For example, /// [`u32::rotate_right`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_rotate", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3500,7 +3143,8 @@ pub const fn rotate_right(_x: T, _shift: u32) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_add` method. For example, /// [`u32::wrapping_add`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3517,7 +3161,8 @@ pub const fn wrapping_add(_a: T, _b: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_sub` method. For example, /// [`u32::wrapping_sub`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3534,7 +3179,8 @@ pub const fn wrapping_sub(_a: T, _b: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `wrapping_mul` method. For example, /// [`u32::wrapping_mul`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3552,7 +3198,8 @@ pub const fn wrapping_mul(_a: T, _b: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_add` method. For example, /// [`u32::saturating_add`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_saturating", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3569,7 +3216,8 @@ pub const fn saturating_add(_a: T, _b: T) -> T { /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_sub` method. For example, /// [`u32::saturating_sub`] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_saturating", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3583,7 +3231,8 @@ pub const fn saturating_sub(_a: T, _b: T) -> T { /// This intrinsic can *only* be called where the pointer is a local without /// projections (`read_via_copy(ptr)`, not `read_via_copy(*ptr)`) so that it /// trivially obeys runtime-MIR rules about derefs in operands. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_read", since = "1.71.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3597,7 +3246,8 @@ pub const unsafe fn read_via_copy(_ptr: *const T) -> T { /// This intrinsic can *only* be called where the pointer is a local without /// projections (`write_via_move(ptr, x)`, not `write_via_move(*ptr, x)`) so /// that it trivially obeys runtime-MIR rules about derefs in operands. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_write", since = "1.83.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3614,7 +3264,8 @@ pub const unsafe fn write_via_move(_ptr: *mut T, _value: T) { /// any safety invariants. /// /// The stabilized version of this intrinsic is [`core::mem::discriminant`]. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_discriminant", since = "1.75.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3622,48 +3273,39 @@ pub const fn discriminant_value(_v: &T) -> ::Discrimin unimplemented!() } -/// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the -/// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs. -/// -/// `catch_fn` must not unwind. -/// -/// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign -/// unwinds). This function takes the data pointer and a pointer to the target- and -/// runtime-specific exception object that was caught. -/// -/// Note that in the case of a foreign unwinding operation, the exception object data may not be -/// safely usable from Rust, and should not be directly exposed via the standard library. To -/// prevent unsafe access, the library implementation may either abort the process or present an -/// opaque error type to the user. -/// -/// For more information, see the compiler's source, as well as the documentation for the stable -/// version of this intrinsic, `std::panic::catch_unwind`. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn catch_unwind( - _try_fn: fn(*mut u8), - _data: *mut u8, - _catch_fn: fn(*mut u8, *mut u8), -) -> i32 { - unreachable!() -} - -/// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held -/// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`. -/// -/// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT` -/// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered -/// in ways that are not allowed for regular writes). -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn nontemporal_store(_ptr: *mut T, _val: T) { - unreachable!() +extern "rust-intrinsic" { + /// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the + /// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs. + /// + /// `catch_fn` must not unwind. + /// + /// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign + /// unwinds). This function takes the data pointer and a pointer to the target- and + /// runtime-specific exception object that was caught. + /// + /// Note that in the case of a foreign unwinding operation, the exception object data may not be + /// safely usable from Rust, and should not be directly exposed via the standard library. To + /// prevent unsafe access, the library implementation may either abort the process or present an + /// opaque error type to the user. + /// + /// For more information, see the compiler's source, as well as the documentation for the stable + /// version of this intrinsic, `std::panic::catch_unwind`. + #[rustc_nounwind] + pub fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32; + + /// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held + /// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`. + /// + /// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT` + /// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered + /// in ways that are not allowed for regular writes). + #[rustc_nounwind] + pub fn nontemporal_store(ptr: *mut T, val: T); } /// See documentation of `<*const T>::offset_from` for details. -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_offset_from", since = "1.65.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3672,6 +3314,7 @@ pub const unsafe fn ptr_offset_from(_ptr: *const T, _base: *const T) -> isize } /// See documentation of `<*const T>::sub_ptr` for details. +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892"))] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3683,6 +3326,7 @@ pub const unsafe fn ptr_offset_from_unsigned(_ptr: *const T, _base: *const T) /// Returns `2` if the result is unknown. /// Returns `1` if the pointers are guaranteed equal. /// Returns `0` if the pointers are guaranteed inequal. +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020"))] #[rustc_intrinsic] #[rustc_nounwind] #[rustc_do_not_const_check] @@ -3715,6 +3359,7 @@ pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { /// /// (The implementation is allowed to branch on the results of comparisons, /// which is UB if any of their inputs are `undef`.) +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_intrinsic_raw_eq", issue = "none"))] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3736,6 +3381,10 @@ pub const unsafe fn raw_eq(_a: &T, _b: &T) -> bool { /// that differs. That allows optimizations that can read in large chunks. /// /// [valid]: crate::ptr#safety +#[cfg_attr( + bootstrap, + rustc_const_unstable(feature = "const_intrinsic_compare_bytes", issue = "none") +)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -3746,10 +3395,10 @@ pub const unsafe fn compare_bytes(_left: *const u8, _right: *const u8, _bytes: u /// See documentation of [`std::hint::black_box`] for details. /// /// [`std::hint::black_box`]: crate::hint::black_box +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_black_box", issue = "none"))] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[rustc_intrinsic_const_stable_indirect] pub const fn black_box(_dummy: T) -> T { unimplemented!() } @@ -3841,7 +3490,7 @@ where /// See [`const_eval_select()`] for the rules and requirements around that intrinsic. pub(crate) macro const_eval_select { ( - @capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : + @capture { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : if const $(#[$compiletime_attr:meta])* $compiletime:block else @@ -3849,7 +3498,7 @@ pub(crate) macro const_eval_select { ) => { // Use the `noinline` arm, after adding explicit `inline` attributes $crate::intrinsics::const_eval_select!( - @capture$([$($binders)*])? { $($arg : $ty = $val),* } $(-> $ret)? : + @capture { $($arg : $ty = $val),* } $(-> $ret)? : #[noinline] if const #[inline] // prevent codegen on this function @@ -3863,7 +3512,7 @@ pub(crate) macro const_eval_select { }, // With a leading #[noinline], we don't add inline attributes ( - @capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : + @capture { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : #[noinline] if const $(#[$compiletime_attr:meta])* $compiletime:block @@ -3871,12 +3520,12 @@ pub(crate) macro const_eval_select { $(#[$runtime_attr:meta])* $runtime:block ) => {{ $(#[$runtime_attr])* - fn runtime$(<$($binders)*>)?($($arg: $ty),*) $( -> $ret )? { + fn runtime($($arg: $ty),*) $( -> $ret )? { $runtime } $(#[$compiletime_attr])* - const fn compiletime$(<$($binders)*>)?($($arg: $ty),*) $( -> $ret )? { + const fn compiletime($($arg: $ty),*) $( -> $ret )? { // Don't warn if one of the arguments is unused. $(let _ = $arg;)* @@ -3888,14 +3537,14 @@ pub(crate) macro const_eval_select { // We support leaving away the `val` expressions for *all* arguments // (but not for *some* arguments, that's too tricky). ( - @capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty),* $(,)? } $( -> $ret:ty )? : + @capture { $($arg:ident : $ty:ty),* $(,)? } $( -> $ret:ty )? : if const $(#[$compiletime_attr:meta])* $compiletime:block else $(#[$runtime_attr:meta])* $runtime:block ) => { $crate::intrinsics::const_eval_select!( - @capture$([$($binders)*])? { $($arg : $ty = $arg),* } $(-> $ret)? : + @capture { $($arg : $ty = $arg),* } $(-> $ret)? : if const $(#[$compiletime_attr])* $compiletime else @@ -3978,7 +3627,11 @@ pub(crate) macro const_eval_select { /// # _ = foo(&5_i32); /// # _ = bar(&5_i32); /// ``` -#[rustc_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_is_val_statically_known", since = "CURRENT_RUSTC_VERSION") +)] +#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] @@ -3999,9 +3652,9 @@ pub const fn is_val_statically_known(_arg: T) -> bool { #[rustc_nounwind] #[inline] #[rustc_intrinsic] -#[rustc_intrinsic_const_stable_indirect] -#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic -pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { +// Const-unstable because `swap_nonoverlapping` is const-unstable. +#[rustc_const_unstable(feature = "const_typed_swap", issue = "none")] +pub const unsafe fn typed_swap(x: *mut T, y: *mut T) { // SAFETY: The caller provided single non-overlapping items behind // pointers, so swapping them with `count: 1` is fine. unsafe { ptr::swap_nonoverlapping(x, y, 1) }; @@ -4020,7 +3673,8 @@ pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { /// assertions are enabled whenever the *user crate* has UB checks enabled. However, if the /// user has UB checks disabled, the checks will still get optimized out. This intrinsic is /// primarily used by [`ub_checks::assert_unsafe_precondition`]. -#[rustc_intrinsic_const_stable_indirect] // just for UB checks +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_ub_checks", issue = "none"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] // just for UB checks #[inline(always)] #[rustc_intrinsic] pub const fn ub_checks() -> bool { @@ -4064,52 +3718,6 @@ 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 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 @@ -4149,7 +3757,8 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize { /// The stabilized version of this intrinsic is [`core::mem::size_of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_size_of", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn size_of() -> usize { @@ -4166,7 +3775,8 @@ pub const fn size_of() -> usize { /// The stabilized version of this intrinsic is [`core::mem::align_of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_min_align_of", since = "1.40.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn min_align_of() -> usize { @@ -4179,6 +3789,7 @@ pub const fn min_align_of() -> usize { /// It's "tracking issue" is [#91971](https://github.com/rust-lang/rust/issues/91971). #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_pref_align_of", issue = "91971"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn pref_align_of() -> usize { @@ -4196,6 +3807,7 @@ pub const unsafe fn pref_align_of() -> usize { /// The to-be-stabilized version of this intrinsic is [`crate::mem::variant_count`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "variant_count", issue = "73662"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn variant_count() -> usize { @@ -4211,9 +3823,9 @@ pub const fn variant_count() -> usize { /// See [`crate::mem::size_of_val_raw`] for safety conditions. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_size_of_val", issue = "46571"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[rustc_intrinsic_const_stable_indirect] pub const unsafe fn size_of_val(_ptr: *const T) -> usize { unreachable!() } @@ -4227,9 +3839,9 @@ pub const unsafe fn size_of_val(_ptr: *const T) -> usize { /// See [`crate::mem::align_of_val_raw`] for safety conditions. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_align_of_val", issue = "46571"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] -#[rustc_intrinsic_const_stable_indirect] pub const unsafe fn min_align_of_val(_ptr: *const T) -> usize { unreachable!() } @@ -4244,6 +3856,7 @@ pub const unsafe fn min_align_of_val(_ptr: *const T) -> usize { /// The stabilized version of this intrinsic is [`core::any::type_name`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_type_name", issue = "63084"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn type_name() -> &'static str { @@ -4262,6 +3875,7 @@ pub const fn type_name() -> &'static str { /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_type_id", issue = "77125"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn type_id() -> u128 { @@ -4275,7 +3889,8 @@ pub const fn type_id() -> u128 { /// change the possible layouts of pointers. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn aggregate_raw_ptr, D, M>(_data: D, _meta: M) -> P { @@ -4300,7 +3915,11 @@ impl AggregateRawPtr<*mut T> for *mut P { /// This is used to implement functions like `ptr::metadata`. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0")) +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn ptr_metadata + ?Sized, M>(_ptr: *const P) -> M { @@ -4400,17 +4019,14 @@ pub const fn ptr_metadata + ?Sized, M>(_ptr: *cons /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append #[doc(alias = "memcpy")] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] -#[cfg_attr( - not(bootstrap), - rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" -)] +#[rustc_allowed_through_unstable_modules] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_diagnostic_item = "ptr_copy_nonoverlapping"] pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { - #[rustc_intrinsic_const_stable_indirect] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0"))] + #[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -4460,11 +4076,13 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `src` must be [valid] for reads of `count * size_of::()` bytes. +/// * `src` must be [valid] for reads of `count * size_of::()` bytes, and must remain valid even +/// when `dst` is written for `count * size_of::()` bytes. (This means if the memory ranges +/// overlap, the two pointers must not be subject to aliasing restrictions relative to each +/// other.) /// /// * `dst` must be [valid] for writes of `count * size_of::()` bytes, and must remain valid even -/// when `src` is read for `count * size_of::()` bytes. (This means if the memory ranges -/// overlap, the `dst` pointer must not be invalidated by `src` reads.) +/// when `src` is read for `count * size_of::()` bytes. /// /// * Both `src` and `dst` must be properly aligned. /// @@ -4508,17 +4126,14 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// ``` #[doc(alias = "memmove")] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] -#[cfg_attr( - not(bootstrap), - rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" -)] +#[rustc_allowed_through_unstable_modules] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_diagnostic_item = "ptr_copy"] pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { - #[rustc_intrinsic_const_stable_indirect] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0"))] + #[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -4595,17 +4210,14 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { /// ``` #[doc(alias = "memset")] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] -#[cfg_attr( - not(bootstrap), - rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" -)] +#[rustc_allowed_through_unstable_modules] #[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_diagnostic_item = "ptr_write_bytes"] pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { - #[rustc_intrinsic_const_stable_indirect] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_write", since = "1.83.0"))] + #[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] @@ -4638,6 +4250,7 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { /// The stabilized version of this intrinsic is /// [`f16::min`] #[rustc_nounwind] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f16", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn minnumf16(_x: f16, _y: f16) -> f16 { @@ -4654,7 +4267,11 @@ pub const fn minnumf16(_x: f16, _y: f16) -> f16 { /// The stabilized version of this intrinsic is /// [`f32::min`] #[rustc_nounwind] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn minnumf32(_x: f32, _y: f32) -> f32 { @@ -4671,7 +4288,11 @@ pub const fn minnumf32(_x: f32, _y: f32) -> f32 { /// The stabilized version of this intrinsic is /// [`f64::min`] #[rustc_nounwind] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn minnumf64(_x: f64, _y: f64) -> f64 { @@ -4688,6 +4309,7 @@ pub const fn minnumf64(_x: f64, _y: f64) -> f64 { /// The stabilized version of this intrinsic is /// [`f128::min`] #[rustc_nounwind] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f128", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn minnumf128(_x: f128, _y: f128) -> f128 { @@ -4704,6 +4326,7 @@ pub const fn minnumf128(_x: f128, _y: f128) -> f128 { /// The stabilized version of this intrinsic is /// [`f16::max`] #[rustc_nounwind] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f16", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn maxnumf16(_x: f16, _y: f16) -> f16 { @@ -4720,7 +4343,11 @@ pub const fn maxnumf16(_x: f16, _y: f16) -> f16 { /// The stabilized version of this intrinsic is /// [`f32::max`] #[rustc_nounwind] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn maxnumf32(_x: f32, _y: f32) -> f32 { @@ -4737,7 +4364,11 @@ pub const fn maxnumf32(_x: f32, _y: f32) -> f32 { /// The stabilized version of this intrinsic is /// [`f64::max`] #[rustc_nounwind] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn maxnumf64(_x: f64, _y: f64) -> f64 { @@ -4754,6 +4385,7 @@ pub const fn maxnumf64(_x: f64, _y: f64) -> f64 { /// The stabilized version of this intrinsic is /// [`f128::max`] #[rustc_nounwind] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f128", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const fn maxnumf128(_x: f128, _y: f128) -> f128 { @@ -4765,6 +4397,7 @@ pub const fn maxnumf128(_x: f128, _y: f128) -> f128 { /// The stabilized version of this intrinsic is /// [`f16::abs`](../../std/primitive.f16.html#method.abs) #[rustc_nounwind] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f16", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn fabsf16(_x: f16) -> f16 { @@ -4776,7 +4409,11 @@ pub const unsafe fn fabsf16(_x: f16) -> f16 { /// The stabilized version of this intrinsic is /// [`f32::abs`](../../std/primitive.f32.html#method.abs) #[rustc_nounwind] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn fabsf32(_x: f32) -> f32 { @@ -4788,7 +4425,11 @@ pub const unsafe fn fabsf32(_x: f32) -> f32 { /// The stabilized version of this intrinsic is /// [`f64::abs`](../../std/primitive.f64.html#method.abs) #[rustc_nounwind] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn fabsf64(_x: f64) -> f64 { @@ -4800,6 +4441,7 @@ pub const unsafe fn fabsf64(_x: f64) -> f64 { /// The stabilized version of this intrinsic is /// [`f128::abs`](../../std/primitive.f128.html#method.abs) #[rustc_nounwind] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f128", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn fabsf128(_x: f128) -> f128 { @@ -4811,6 +4453,7 @@ pub const unsafe fn fabsf128(_x: f128) -> f128 { /// The stabilized version of this intrinsic is /// [`f16::copysign`](../../std/primitive.f16.html#method.copysign) #[rustc_nounwind] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f16", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn copysignf16(_x: f16, _y: f16) -> f16 { @@ -4822,7 +4465,11 @@ pub const unsafe fn copysignf16(_x: f16, _y: f16) -> f16 { /// The stabilized version of this intrinsic is /// [`f32::copysign`](../../std/primitive.f32.html#method.copysign) #[rustc_nounwind] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn copysignf32(_x: f32, _y: f32) -> f32 { @@ -4833,7 +4480,11 @@ pub const unsafe fn copysignf32(_x: f32, _y: f32) -> f32 { /// The stabilized version of this intrinsic is /// [`f64::copysign`](../../std/primitive.f64.html#method.copysign) #[rustc_nounwind] -#[rustc_intrinsic_const_stable_indirect] +#[cfg_attr( + bootstrap, + rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION") +)] +#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn copysignf64(_x: f64, _y: f64) -> f64 { @@ -4845,6 +4496,7 @@ pub const unsafe fn copysignf64(_x: f64, _y: f64) -> f64 { /// The stabilized version of this intrinsic is /// [`f128::copysign`](../../std/primitive.f128.html#method.copysign) #[rustc_nounwind] +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "f128", issue = "116909"))] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] pub const unsafe fn copysignf128(_x: f128, _y: f128) -> f128 { diff --git a/core/src/intrinsics/simd.rs b/core/src/intrinsics/simd.rs index e59d3aff37999..5ddca9c4dce88 100644 --- a/core/src/intrinsics/simd.rs +++ b/core/src/intrinsics/simd.rs @@ -2,939 +2,655 @@ //! //! In this module, a "vector" is any `repr(simd)` type. -/// 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(_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(_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(_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(_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(_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, `::MIN / -1` is undefined behavior. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn simd_div(_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, `::MIN / -1` is undefined behavior. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn simd_rem(_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 `::BITS`. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn simd_shl(_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 `::BITS`. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn simd_shr(_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(_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(_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(_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(_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(_x: T) -> U { - unreachable!() -} - -/// Negates a vector elementwise. -/// -/// `T` must be a vector of integer or floating-point primitive types. -/// -/// Rust panics for `-::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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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 `::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(_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 `::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(_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 `::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(_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 `::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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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: 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 `::wrapping_offset`. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -pub unsafe fn simd_arith_offset(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_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(_a: T) -> T { - unreachable!() +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(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(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(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(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(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, `::MIN / -1` is undefined behavior. + #[rustc_nounwind] + pub fn simd_div(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, `::MIN / -1` is undefined behavior. + #[rustc_nounwind] + pub fn simd_rem(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 `::BITS`. + #[rustc_nounwind] + pub fn simd_shl(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 `::BITS`. + #[rustc_nounwind] + pub fn simd_shr(lhs: T, rhs: T) -> T; + + /// "Ands" vectors elementwise. + /// + /// `T` must be a vector of integer primitive types. + #[rustc_nounwind] + pub fn simd_and(x: T, y: T) -> T; + + /// "Ors" vectors elementwise. + /// + /// `T` must be a vector of integer primitive types. + #[rustc_nounwind] + pub fn simd_or(x: T, y: T) -> T; + + /// "Exclusive ors" vectors elementwise. + /// + /// `T` must be a vector of integer primitive types. + #[rustc_nounwind] + pub fn simd_xor(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(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(x: T) -> U; + + /// Negates a vector elementwise. + /// + /// `T` must be a vector of integer or floating-point primitive types. + /// + /// Rust panics for `-::Min` due to overflow, but it is not UB with this intrinsic. + #[rustc_nounwind] + pub fn simd_neg(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(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(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(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(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(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(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(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(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(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(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 `::read` (e.g. aligned to the element + /// type). + /// + /// `mask` must only contain `0` or `!0` values. + #[rustc_nounwind] + pub fn simd_gather(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 `::write` (e.g. aligned to the element + /// type). + /// + /// `mask` must only contain `0` or `!0` values. + #[rustc_nounwind] + pub fn simd_scatter(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 `::read` (e.g. aligned to the element + /// type). + /// + /// `mask` must only contain `0` or `!0` values. + #[rustc_nounwind] + pub fn simd_masked_load(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 `::write` (e.g. aligned to the element + /// type). + /// + /// `mask` must only contain `0` or `!0` values. + #[rustc_nounwind] + pub fn simd_masked_store(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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: 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 `::wrapping_offset`. + #[rustc_nounwind] + pub fn simd_arith_offset(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(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(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(addr: T) -> U; + + /// Swaps bytes of each element. + /// + /// `T` must be a vector of integers. + #[rustc_nounwind] + pub fn simd_bswap(x: T) -> T; + + /// Reverses bits of each element. + /// + /// `T` must be a vector of integers. + #[rustc_nounwind] + pub fn simd_bitreverse(x: T) -> T; + + /// Counts the leading zeros of each element. + /// + /// `T` must be a vector of integers. + #[rustc_nounwind] + pub fn simd_ctlz(x: T) -> T; + + /// Counts the number of ones in each element. + /// + /// `T` must be a vector of integers. + #[rustc_nounwind] + pub fn simd_ctpop(x: T) -> T; + + /// Counts the trailing zeros of each element. + /// + /// `T` must be a vector of integers. + #[rustc_nounwind] + pub fn simd_cttz(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(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(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(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(x: T) -> T; + + /// Takes the square root of each element. + /// + /// `T` must be a vector of floats. + #[rustc_nounwind] + pub fn simd_fsqrt(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(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(a: T) -> T; + + // Computes the cosine of each element. + /// + /// `T` must be a vector of floats. + #[rustc_nounwind] + pub fn simd_fcos(a: T) -> T; + + // Computes the exponential function of each element. + /// + /// `T` must be a vector of floats. + #[rustc_nounwind] + pub fn simd_fexp(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(a: T) -> T; + + // Computes the base 10 logarithm of each element. + /// + /// `T` must be a vector of floats. + #[rustc_nounwind] + pub fn simd_flog10(a: T) -> T; + + // Computes the base 2 logarithm of each element. + /// + /// `T` must be a vector of floats. + #[rustc_nounwind] + pub fn simd_flog2(a: T) -> T; + + // Computes the natural logarithm of each element. + /// + /// `T` must be a vector of floats. + #[rustc_nounwind] + pub fn simd_flog(a: T) -> T; } diff --git a/core/src/io/borrowed_buf.rs b/core/src/io/borrowed_buf.rs index f86abf7f1e91c..4227e503ba7ba 100644 --- a/core/src/io/borrowed_buf.rs +++ b/core/src/io/borrowed_buf.rs @@ -94,7 +94,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked(..self.filled); - buf.assume_init_ref() + MaybeUninit::slice_assume_init_ref(buf) } } @@ -104,7 +104,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked_mut(..self.filled); - buf.assume_init_mut() + MaybeUninit::slice_assume_init_mut(buf) } } @@ -114,7 +114,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked(..self.filled); - buf.assume_init_ref() + MaybeUninit::slice_assume_init_ref(buf) } } @@ -124,7 +124,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked_mut(..self.filled); - buf.assume_init_mut() + MaybeUninit::slice_assume_init_mut(buf) } } @@ -233,7 +233,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { let buf = self.buf.buf.get_unchecked(self.buf.filled..self.buf.init); - buf.assume_init_ref() + MaybeUninit::slice_assume_init_ref(buf) } } @@ -243,7 +243,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init); - buf.assume_init_mut() + MaybeUninit::slice_assume_init_mut(buf) } } @@ -344,7 +344,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: we do not de-initialize any of the elements of the slice unsafe { - self.as_mut()[..buf.len()].write_copy_of_slice(buf); + MaybeUninit::copy_from_slice(&mut self.as_mut()[..buf.len()], buf); } // SAFETY: We just added the entire contents of buf to the filled section. diff --git a/core/src/iter/adapters/filter_map.rs b/core/src/iter/adapters/filter_map.rs index 24ec6b1741ce1..cc64ceb13f766 100644 --- a/core/src/iter/adapters/filter_map.rs +++ b/core/src/iter/adapters/filter_map.rs @@ -81,7 +81,9 @@ where if const { crate::mem::needs_drop::() } { // SAFETY: self.initialized is always <= N, which also is the length of the array. unsafe { - self.array.get_unchecked_mut(..self.initialized).assume_init_drop(); + core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( + self.array.get_unchecked_mut(..self.initialized), + )); } } } diff --git a/core/src/iter/adapters/flatten.rs b/core/src/iter/adapters/flatten.rs index 9b9353b800a98..0023b46031f12 100644 --- a/core/src/iter/adapters/flatten.rs +++ b/core/src/iter/adapters/flatten.rs @@ -1,7 +1,7 @@ use crate::iter::adapters::SourceIter; use crate::iter::{ - Cloned, Copied, Empty, Filter, FilterMap, Fuse, FusedIterator, Map, Once, OnceWith, - TrustedFused, TrustedLen, + Cloned, Copied, Empty, Filter, FilterMap, Fuse, FusedIterator, InPlaceIterable, Map, Once, + OnceWith, TrustedFused, TrustedLen, }; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; @@ -157,6 +157,21 @@ where { } +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for FlatMap +where + I: InPlaceIterable, + U: BoundedSize + IntoIterator, +{ + const EXPAND_BY: Option> = const { + match (I::EXPAND_BY, U::UPPER_BOUND) { + (Some(m), Some(n)) => m.checked_mul(n), + _ => None, + } + }; + const MERGE_BY: Option> = I::MERGE_BY; +} + #[unstable(issue = "none", feature = "inplace_iteration")] unsafe impl SourceIter for FlatMap where @@ -371,6 +386,21 @@ where { } +#[unstable(issue = "none", feature = "inplace_iteration")] +unsafe impl InPlaceIterable for Flatten +where + I: InPlaceIterable + Iterator, + ::Item: IntoIterator + BoundedSize, +{ + const EXPAND_BY: Option> = const { + match (I::EXPAND_BY, I::Item::UPPER_BOUND) { + (Some(m), Some(n)) => m.checked_mul(n), + _ => None, + } + }; + const MERGE_BY: Option> = I::MERGE_BY; +} + #[unstable(issue = "none", feature = "inplace_iteration")] unsafe impl SourceIter for Flatten where diff --git a/core/src/iter/sources/from_fn.rs b/core/src/iter/sources/from_fn.rs index 1c7e1b30a2f88..3cd3830471cfe 100644 --- a/core/src/iter/sources/from_fn.rs +++ b/core/src/iter/sources/from_fn.rs @@ -1,9 +1,7 @@ use crate::fmt; -/// Creates an iterator with the provided closure -/// `F: FnMut() -> Option` as its [`next`](Iterator::next) method. -/// -/// The iterator will yield the `T`s returned from the closure. +/// Creates a new iterator where each iteration calls the provided closure +/// `F: FnMut() -> Option`. /// /// This allows creating a custom iterator with any behavior /// without using the more verbose syntax of creating a dedicated type diff --git a/core/src/iter/sources/once.rs b/core/src/iter/sources/once.rs index c4a9860bdd76c..21be4377da1ca 100644 --- a/core/src/iter/sources/once.rs +++ b/core/src/iter/sources/once.rs @@ -34,7 +34,7 @@ use crate::iter::{FusedIterator, TrustedLen}; /// use std::fs; /// use std::path::PathBuf; /// -/// let dirs = fs::read_dir(".foo")?; +/// let dirs = fs::read_dir(".foo").unwrap(); /// /// // we need to convert from an iterator of DirEntry-s to an iterator of /// // PathBufs, so we use map @@ -50,7 +50,6 @@ use crate::iter::{FusedIterator, TrustedLen}; /// for f in files { /// println!("{f:?}"); /// } -/// # std::io::Result::Ok(()) /// ``` #[stable(feature = "iter_once", since = "1.2.0")] pub fn once(value: T) -> Once { diff --git a/core/src/iter/sources/successors.rs b/core/src/iter/sources/successors.rs index e14c9235e5562..36bc4035039e6 100644 --- a/core/src/iter/sources/successors.rs +++ b/core/src/iter/sources/successors.rs @@ -5,7 +5,6 @@ use crate::iter::FusedIterator; /// /// The iterator starts with the given first item (if any) /// and calls the given `FnMut(&T) -> Option` closure to compute each item’s successor. -/// The iterator will yield the `T`s returned from the closure. /// /// ``` /// use std::iter::successors; diff --git a/core/src/iter/traits/collect.rs b/core/src/iter/traits/collect.rs index 97bb21c8a36e8..2cf2ea58fd4ee 100644 --- a/core/src/iter/traits/collect.rs +++ b/core/src/iter/traits/collect.rs @@ -152,6 +152,39 @@ pub trait FromIterator
: Sized { fn from_iter>(iter: T) -> Self; } +/// This implementation turns an iterator of tuples into a tuple of types which implement +/// [`Default`] and [`Extend`]. +/// +/// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`] +/// implementations: +/// +/// ```rust +/// # fn main() -> Result<(), core::num::ParseIntError> { +/// let string = "1,2,123,4"; +/// +/// let (numbers, lengths): (Vec<_>, Vec<_>) = string +/// .split(',') +/// .map(|s| s.parse().map(|n: u32| (n, s.len()))) +/// .collect::>()?; +/// +/// assert_eq!(numbers, [1, 2, 123, 4]); +/// assert_eq!(lengths, [1, 1, 3, 1]); +/// # Ok(()) } +/// ``` +#[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] +impl FromIterator<(AE, BE)> for (A, B) +where + A: Default + Extend, + B: Default + Extend, +{ + fn from_iter>(iter: I) -> Self { + let mut res = <(A, B)>::default(); + res.extend(iter); + + res + } +} + /// Conversion into an [`Iterator`]. /// /// By implementing `IntoIterator` for a type, you define how it will be @@ -459,234 +492,131 @@ impl Extend<()> for () { fn extend_one(&mut self, _item: ()) {} } -macro_rules! spec_tuple_impl { - ( - ( - $ty_name:ident, $var_name:ident, $extend_ty_name: ident, - $trait_name:ident, $default_fn_name:ident, $cnt:tt - ), - ) => { - spec_tuple_impl!( - $trait_name, - $default_fn_name, - #[doc(fake_variadic)] - #[doc = "This trait is implemented for tuples up to twelve items long. The `impl`s for \ - 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in \ - 1.85.0."] - => ($ty_name, $var_name, $extend_ty_name, $cnt), - ); - }; - ( - ( - $ty_name:ident, $var_name:ident, $extend_ty_name: ident, - $trait_name:ident, $default_fn_name:ident, $cnt:tt - ), - $( - ( - $ty_names:ident, $var_names:ident, $extend_ty_names:ident, - $trait_names:ident, $default_fn_names:ident, $cnts:tt - ), - )* - ) => { - spec_tuple_impl!( - $( - ( - $ty_names, $var_names, $extend_ty_names, - $trait_names, $default_fn_names, $cnts - ), - )* - ); - spec_tuple_impl!( - $trait_name, - $default_fn_name, - #[doc(hidden)] - => ( - $ty_name, $var_name, $extend_ty_name, $cnt - ), - $( - ( - $ty_names, $var_names, $extend_ty_names, $cnts - ), - )* - ); - }; - ( - $trait_name:ident, $default_fn_name:ident, #[$meta:meta] - $(#[$doctext:meta])? => $( - ( - $ty_names:ident, $var_names:ident, $extend_ty_names:ident, $cnts:tt - ), - )* - ) => { - #[$meta] - $(#[$doctext])? - #[stable(feature = "extend_for_tuple", since = "1.56.0")] - impl<$($ty_names,)* $($extend_ty_names,)*> Extend<($($ty_names,)*)> for ($($extend_ty_names,)*) - where - $($extend_ty_names: Extend<$ty_names>,)* - { - /// Allows to `extend` a tuple of collections that also implement `Extend`. - /// - /// See also: [`Iterator::unzip`] - /// - /// # Examples - /// ``` - /// // Example given for a 2-tuple, but 1- through 12-tuples are supported - /// let mut tuple = (vec![0], vec![1]); - /// tuple.extend([(2, 3), (4, 5), (6, 7)]); - /// assert_eq!(tuple.0, [0, 2, 4, 6]); - /// assert_eq!(tuple.1, [1, 3, 5, 7]); - /// - /// // also allows for arbitrarily nested tuples as elements - /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); - /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); - /// - /// let (a, (b, c)) = nested_tuple; - /// assert_eq!(a, [1, 4, 7]); - /// assert_eq!(b, [2, 5, 8]); - /// assert_eq!(c, [3, 6, 9]); - /// ``` - fn extend>(&mut self, into_iter: T) { - let ($($var_names,)*) = self; - let iter = into_iter.into_iter(); - $trait_name::extend(iter, $($var_names,)*); - } +#[stable(feature = "extend_for_tuple", since = "1.56.0")] +impl Extend<(A, B)> for (ExtendA, ExtendB) +where + ExtendA: Extend, + ExtendB: Extend, +{ + /// Allows to `extend` a tuple of collections that also implement `Extend`. + /// + /// See also: [`Iterator::unzip`] + /// + /// # Examples + /// ``` + /// let mut tuple = (vec![0], vec![1]); + /// tuple.extend([(2, 3), (4, 5), (6, 7)]); + /// assert_eq!(tuple.0, [0, 2, 4, 6]); + /// assert_eq!(tuple.1, [1, 3, 5, 7]); + /// + /// // also allows for arbitrarily nested tuples as elements + /// let mut nested_tuple = (vec![1], (vec![2], vec![3])); + /// nested_tuple.extend([(4, (5, 6)), (7, (8, 9))]); + /// + /// let (a, (b, c)) = nested_tuple; + /// assert_eq!(a, [1, 4, 7]); + /// assert_eq!(b, [2, 5, 8]); + /// assert_eq!(c, [3, 6, 9]); + /// ``` + fn extend>(&mut self, into_iter: T) { + let (a, b) = self; + let iter = into_iter.into_iter(); + SpecTupleExtend::extend(iter, a, b); + } - fn extend_one(&mut self, item: ($($ty_names,)*)) { - $(self.$cnts.extend_one(item.$cnts);)* - } + fn extend_one(&mut self, item: (A, B)) { + self.0.extend_one(item.0); + self.1.extend_one(item.1); + } - fn extend_reserve(&mut self, additional: usize) { - $(self.$cnts.extend_reserve(additional);)* - } + fn extend_reserve(&mut self, additional: usize) { + self.0.extend_reserve(additional); + self.1.extend_reserve(additional); + } - unsafe fn extend_one_unchecked(&mut self, item: ($($ty_names,)*)) { - // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. - unsafe { - $(self.$cnts.extend_one_unchecked(item.$cnts);)* - } - } + unsafe fn extend_one_unchecked(&mut self, item: (A, B)) { + // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. + unsafe { + self.0.extend_one_unchecked(item.0); + self.1.extend_one_unchecked(item.1); } + } +} - trait $trait_name<$($ty_names),*> { - fn extend(self, $($var_names: &mut $ty_names,)*); +fn default_extend_tuple( + iter: impl Iterator, + a: &mut ExtendA, + b: &mut ExtendB, +) where + ExtendA: Extend, + ExtendB: Extend, +{ + fn extend<'a, A, B>( + a: &'a mut impl Extend, + b: &'a mut impl Extend, + ) -> impl FnMut((), (A, B)) + 'a { + move |(), (t, u)| { + a.extend_one(t); + b.extend_one(u); } + } - fn $default_fn_name<$($ty_names,)* $($extend_ty_names,)*>( - iter: impl Iterator, - $($var_names: &mut $extend_ty_names,)* - ) where - $($extend_ty_names: Extend<$ty_names>,)* - { - fn extend<'a, $($ty_names,)*>( - $($var_names: &'a mut impl Extend<$ty_names>,)* - ) -> impl FnMut((), ($($ty_names,)*)) + 'a { - #[allow(non_snake_case)] - move |(), ($($extend_ty_names,)*)| { - $($var_names.extend_one($extend_ty_names);)* - } - } + let (lower_bound, _) = iter.size_hint(); + if lower_bound > 0 { + a.extend_reserve(lower_bound); + b.extend_reserve(lower_bound); + } - let (lower_bound, _) = iter.size_hint(); - if lower_bound > 0 { - $($var_names.extend_reserve(lower_bound);)* - } + iter.fold((), extend(a, b)); +} - iter.fold((), extend($($var_names,)*)); - } +trait SpecTupleExtend { + fn extend(self, a: &mut A, b: &mut B); +} - impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter - where - $($extend_ty_names: Extend<$ty_names>,)* - Iter: Iterator, - { - default fn extend(self, $($var_names: &mut $extend_ty_names),*) { - $default_fn_name(self, $($var_names),*); +impl SpecTupleExtend for Iter +where + ExtendA: Extend, + ExtendB: Extend, + Iter: Iterator, +{ + default fn extend(self, a: &mut ExtendA, b: &mut ExtendB) { + default_extend_tuple(self, a, b); + } +} + +impl SpecTupleExtend for Iter +where + ExtendA: Extend, + ExtendB: Extend, + Iter: TrustedLen, +{ + fn extend(self, a: &mut ExtendA, b: &mut ExtendB) { + fn extend<'a, A, B>( + a: &'a mut impl Extend, + b: &'a mut impl Extend, + ) -> impl FnMut((), (A, B)) + 'a { + // SAFETY: We reserve enough space for the `size_hint`, and the iterator is `TrustedLen` + // so its `size_hint` is exact. + move |(), (t, u)| unsafe { + a.extend_one_unchecked(t); + b.extend_one_unchecked(u); } } - impl<$($ty_names,)* $($extend_ty_names,)* Iter> $trait_name<$($extend_ty_names),*> for Iter - where - $($extend_ty_names: Extend<$ty_names>,)* - Iter: TrustedLen, - { - fn extend(self, $($var_names: &mut $extend_ty_names,)*) { - fn extend<'a, $($ty_names,)*>( - $($var_names: &'a mut impl Extend<$ty_names>,)* - ) -> impl FnMut((), ($($ty_names,)*)) + 'a { - #[allow(non_snake_case)] - // SAFETY: We reserve enough space for the `size_hint`, and the iterator is - // `TrustedLen` so its `size_hint` is exact. - move |(), ($($extend_ty_names,)*)| unsafe { - $($var_names.extend_one_unchecked($extend_ty_names);)* - } - } - - let (lower_bound, upper_bound) = self.size_hint(); - - if upper_bound.is_none() { - // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. - $default_fn_name(self, $($var_names,)*); - return; - } - - if lower_bound > 0 { - $($var_names.extend_reserve(lower_bound);)* - } + let (lower_bound, upper_bound) = self.size_hint(); - self.fold((), extend($($var_names,)*)); - } + if upper_bound.is_none() { + // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. + default_extend_tuple(self, a, b); + return; } - /// This implementation turns an iterator of tuples into a tuple of types which implement - /// [`Default`] and [`Extend`]. - /// - /// This is similar to [`Iterator::unzip`], but is also composable with other [`FromIterator`] - /// implementations: - /// - /// ```rust - /// # fn main() -> Result<(), core::num::ParseIntError> { - /// let string = "1,2,123,4"; - /// - /// // Example given for a 2-tuple, but 1- through 12-tuples are supported - /// let (numbers, lengths): (Vec<_>, Vec<_>) = string - /// .split(',') - /// .map(|s| s.parse().map(|n: u32| (n, s.len()))) - /// .collect::>()?; - /// - /// assert_eq!(numbers, [1, 2, 123, 4]); - /// assert_eq!(lengths, [1, 1, 3, 1]); - /// # Ok(()) } - /// ``` - #[$meta] - $(#[$doctext])? - #[stable(feature = "from_iterator_for_tuple", since = "1.79.0")] - impl<$($ty_names,)* $($extend_ty_names,)*> FromIterator<($($extend_ty_names,)*)> for ($($ty_names,)*) - where - $($ty_names: Default + Extend<$extend_ty_names>,)* - { - fn from_iter>(iter: Iter) -> Self { - let mut res = <($($ty_names,)*)>::default(); - res.extend(iter); - - res - } + if lower_bound > 0 { + a.extend_reserve(lower_bound); + b.extend_reserve(lower_bound); } - }; + self.fold((), extend(a, b)); + } } - -spec_tuple_impl!( - (L, l, EL, TraitL, default_extend_tuple_l, 11), - (K, k, EK, TraitK, default_extend_tuple_k, 10), - (J, j, EJ, TraitJ, default_extend_tuple_j, 9), - (I, i, EI, TraitI, default_extend_tuple_i, 8), - (H, h, EH, TraitH, default_extend_tuple_h, 7), - (G, g, EG, TraitG, default_extend_tuple_g, 6), - (F, f, EF, TraitF, default_extend_tuple_f, 5), - (E, e, EE, TraitE, default_extend_tuple_e, 4), - (D, d, ED, TraitD, default_extend_tuple_d, 3), - (C, c, EC, TraitC, default_extend_tuple_c, 2), - (B, b, EB, TraitB, default_extend_tuple_b, 1), - (A, a, EA, TraitA, default_extend_tuple_a, 0), -); diff --git a/core/src/iter/traits/iterator.rs b/core/src/iter/traits/iterator.rs index 42886e90f997d..ffaf1bc56e942 100644 --- a/core/src/iter/traits/iterator.rs +++ b/core/src/iter/traits/iterator.rs @@ -1553,7 +1553,7 @@ pub trait Iterator { /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a + /// Panics if `N` is 0. This check will most probably get changed to a /// compile time error before this method gets stabilized. /// /// ```should_panic @@ -2564,7 +2564,7 @@ pub trait Iterator { /// # Example /// /// ``` - /// let reduced: i32 = (1..10).reduce(|acc, e| acc + e).unwrap_or(0); + /// let reduced: i32 = (1..10).reduce(|acc, e| acc + e).unwrap(); /// assert_eq!(reduced, 45); /// /// // Which is equivalent to doing it with `fold`: @@ -3051,7 +3051,6 @@ pub trait Iterator { /// /// // we can still use `iter`, as there are more elements. /// assert_eq!(iter.next(), Some(&-1)); - /// assert_eq!(iter.next_back(), Some(&3)); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -3088,7 +3087,7 @@ pub trait Iterator { /// [2.4, f32::NAN, 1.3] /// .into_iter() /// .reduce(f32::max) - /// .unwrap_or(0.), + /// .unwrap(), /// 2.4 /// ); /// ``` @@ -3124,7 +3123,7 @@ pub trait Iterator { /// [2.4, f32::NAN, 1.3] /// .into_iter() /// .reduce(f32::min) - /// .unwrap_or(0.), + /// .unwrap(), /// 1.3 /// ); /// ``` @@ -3455,7 +3454,7 @@ pub trait Iterator { /// /// # Panics /// - /// Panics if `N` is zero. + /// Panics if `N` is 0. /// /// # Examples /// @@ -3493,8 +3492,7 @@ pub trait Iterator { /// /// Takes each element, adds them together, and returns the result. /// - /// An empty iterator returns the *additive identity* ("zero") of the type, - /// which is `0` for integers and `-0.0` for floats. + /// An empty iterator returns the zero value of the type. /// /// `sum()` can be used to sum any type implementing [`Sum`][`core::iter::Sum`], /// including [`Option`][`Option::sum`] and [`Result`][`Result::sum`]. @@ -3512,10 +3510,6 @@ pub trait Iterator { /// let sum: i32 = a.iter().sum(); /// /// assert_eq!(sum, 6); - /// - /// let b: Vec = vec![]; - /// let sum: f32 = b.iter().sum(); - /// assert_eq!(sum, -0.0_f32); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] fn sum(self) -> S diff --git a/core/src/lib.rs b/core/src/lib.rs index 49ce1bbcf3980..a178d10125477 100644 --- a/core/src/lib.rs +++ b/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, coretests (library/coretests), to avoid bizarre issues. +// separate crate, libcoretest (library/core/tests), 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 @@ -101,24 +101,38 @@ #![warn(multiple_supertrait_upcastable)] #![allow(internal_features)] #![deny(ffi_unwind_calls)] -#![warn(unreachable_pub)] // Do not check link redundancy on bootstraping phase #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] // // Library features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(const_exact_div))] +#![cfg_attr(bootstrap, feature(const_fmt_arguments_new))] +#![cfg_attr(bootstrap, feature(const_ub_checks))] #![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_align_of_val)] +#![feature(const_align_of_val_raw)] +#![feature(const_alloc_layout)] +#![feature(const_black_box)] +#![feature(const_eq_ignore_ascii_case)] #![feature(const_eval_select)] +#![feature(const_heap)] +#![feature(const_nonnull_new)] +#![feature(const_ptr_sub_ptr)] +#![feature(const_raw_ptr_comparison)] +#![feature(const_size_of_val)] +#![feature(const_size_of_val_raw)] +#![feature(const_sockaddr_setters)] +#![feature(const_swap)] +#![feature(const_try)] +#![feature(const_type_id)] +#![feature(const_type_name)] +#![feature(const_typed_swap)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] -#![feature(disjoint_bitor)] +#![feature(do_not_recommend)] #![feature(internal_impls_macro)] #![feature(ip)] #![feature(is_ascii_octdigit)] @@ -130,7 +144,6 @@ #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] -#![feature(slice_as_array)] #![feature(slice_as_chunks)] #![feature(slice_ptr_get)] #![feature(str_internals)] @@ -145,6 +158,8 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] @@ -154,7 +169,10 @@ #![feature(cfg_target_has_atomic)] #![feature(cfg_target_has_atomic_equal_alignment)] #![feature(cfg_ub_checks)] +#![feature(const_for)] +#![feature(const_is_char_boundary)] #![feature(const_precise_live_drops)] +#![feature(const_str_split_at)] #![feature(const_trait_impl)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] @@ -191,7 +209,6 @@ #![feature(simd_ffi)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] -#![feature(strict_provenance_lints)] #![feature(target_feature_11)] #![feature(trait_alias)] #![feature(transparent_unions)] @@ -241,6 +258,7 @@ pub mod assert_matches { } // We don't export this through #[macro_export] for now, to avoid breakage. +#[cfg(not(bootstrap))] #[unstable(feature = "autodiff", issue = "124509")] /// Unstable module containing the unstable `autodiff` macro. pub mod autodiff { @@ -248,10 +266,6 @@ 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; @@ -344,8 +358,6 @@ 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; @@ -356,7 +368,7 @@ pub mod net; pub mod option; pub mod panic; pub mod panicking; -#[unstable(feature = "pattern_type_macro", issue = "123646")] +#[unstable(feature = "core_pattern_types", issue = "123646")] pub mod pat; pub mod pin; #[unstable(feature = "random", issue = "130703")] @@ -365,8 +377,6 @@ pub mod random; pub mod range; pub mod result; pub mod sync; -#[unstable(feature = "unsafe_binders", issue = "130516")] -pub mod unsafe_binder; pub mod fmt; pub mod hash; @@ -407,8 +417,7 @@ pub mod primitive; unused_imports, unsafe_op_in_unsafe_fn, ambiguous_glob_reexports, - deprecated_in_future, - unreachable_pub + deprecated_in_future )] #[allow(rustdoc::bare_urls)] mod core_arch; diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index 4c6fd196bd31c..771c2d31b60e0 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -224,7 +224,6 @@ pub macro assert_matches { /// } /// } /// ``` -#[cfg(bootstrap)] #[unstable(feature = "cfg_match", issue = "115585")] #[rustc_diagnostic_item = "cfg_match"] pub macro cfg_match { @@ -285,68 +284,6 @@ pub macro cfg_match { } } -/// A macro for defining `#[cfg]` match-like statements. -/// -/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of -/// `#[cfg]` cases, emitting the implementation which matches first. -/// -/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code -/// without having to rewrite each clause multiple times. -/// -/// Trailing `_` wildcard match arms are **optional** and they indicate a fallback branch when -/// all previous declarations do not evaluate to true. -/// -/// # Example -/// -/// ``` -/// #![feature(cfg_match)] -/// -/// cfg_match! { -/// unix => { -/// fn foo() { /* unix specific functionality */ } -/// } -/// target_pointer_width = "32" => { -/// fn foo() { /* non-unix, 32-bit functionality */ } -/// } -/// _ => { -/// fn foo() { /* fallback implementation */ } -/// } -/// } -/// ``` -/// -/// 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"] -pub macro cfg_match { - ({ $($tt:tt)* }) => {{ - cfg_match! { $($tt)* } - }}, - (_ => { $($output:tt)* }) => { - $($output)* - }, - ( - $cfg:meta => $output:tt - $($( $rest:tt )+)? - ) => { - #[cfg($cfg)] - cfg_match! { _ => $output } - $( - #[cfg(not($cfg))] - cfg_match! { $($rest)+ } - )? - }, -} - /// Asserts that a boolean expression is `true` at runtime. /// /// This will invoke the [`panic!`] macro if the provided expression cannot be @@ -1612,11 +1549,12 @@ pub(crate) mod builtin { /// NAME is a string that represents a valid function name. /// MODE is any of Forward, Reverse, ForwardFirst, ReverseFirst. /// INPUT_ACTIVITIES consists of one valid activity for each input parameter. - /// OUTPUT_ACTIVITY must not be set if we implicitly return nothing (or explicitly return + /// OUTPUT_ACTIVITY must not be set if we implicitely return nothing (or explicitely return /// `-> ()`). Otherwise it must be set to one of the allowed activities. #[unstable(feature = "autodiff", issue = "124509")] #[allow_internal_unstable(rustc_attrs)] #[rustc_builtin_macro] + #[cfg(not(bootstrap))] pub macro autodiff($item:item) { /* compiler built-in */ } @@ -1777,32 +1715,6 @@ 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). @@ -1857,4 +1769,32 @@ 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/core/src/marker.rs b/core/src/marker.rs index 1a8ef20dd7b9d..acbad07746bb9 100644 --- a/core/src/marker.rs +++ b/core/src/marker.rs @@ -6,13 +6,6 @@ #![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; @@ -148,8 +141,7 @@ unsafe impl Send for &T {} )] #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable #[rustc_specialization_trait] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_deny_explicit_impl(implement_via_object = false)] #[rustc_coinductive] pub trait Sized { // Empty. @@ -189,27 +181,31 @@ pub trait Sized { /// [^1]: Formerly known as *object safe*. #[unstable(feature = "unsize", issue = "18598")] #[lang = "unsize"] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_deny_explicit_impl(implement_via_object = false)] pub trait Unsize { // Empty. } /// Required trait for constants used in pattern matches. /// -/// Constants are only allowed as patterns if (a) their type implements -/// `PartialEq`, and (b) interpreting the value of the constant as a pattern -/// is equialent to calling `PartialEq`. This ensures that constants used as -/// patterns cannot expose implementation details in an unexpected way or -/// cause semver hazards. +/// Any type that derives `PartialEq` automatically implements this trait, +/// *regardless* of whether its type-parameters implement `PartialEq`. +/// +/// If a `const` item contains some type that does not implement this trait, +/// then that type either (1.) does not implement `PartialEq` (which means the +/// constant will not provide that comparison method, which code generation +/// assumes is available), or (2.) it implements *its own* version of +/// `PartialEq` (which we assume does not conform to a structural-equality +/// comparison). /// -/// This trait ensures point (b). -/// Any type that derives `PartialEq` automatically implements this trait. +/// In either of the two scenarios above, we reject usage of such a constant in +/// a pattern match. /// -/// Implementing this trait (which is unstable) is a way for type authors to explicitly allow -/// comparing const values of this type; that operation will recursively compare all fields -/// (including private fields), even if that behavior differs from `PartialEq`. This can make it -/// semver-breaking to add further private fields to a type. +/// See also the [structural match RFC][RFC1445], and [issue 63438] which +/// motivated migrating from an attribute-based design to this trait. +/// +/// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md +/// [issue 63438]: https://github.com/rust-lang/rust/issues/63438 #[unstable(feature = "structural_match", issue = "31434")] #[diagnostic::on_unimplemented(message = "the type `{Self}` does not `#[derive(PartialEq)]`")] #[lang = "structural_peq"] @@ -819,8 +815,7 @@ impl StructuralPartialEq for PhantomData {} reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" )] #[lang = "discriminant_kind"] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_deny_explicit_impl(implement_via_object = false)] pub trait DiscriminantKind { /// The type of the discriminant, which must satisfy the trait /// bounds required by `mem::Discriminant`. @@ -959,12 +954,9 @@ marker_impls! { /// This should be used for `~const` bounds, /// as non-const bounds will always hold for every type. #[unstable(feature = "const_destruct", issue = "133214")] -#[rustc_const_unstable(feature = "const_destruct", issue = "133214")] #[lang = "destruct"] #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] -#[const_trait] +#[rustc_deny_explicit_impl(implement_via_object = false)] pub trait Destruct {} /// A marker for tuple types. @@ -974,33 +966,25 @@ pub trait Destruct {} #[unstable(feature = "tuple_trait", issue = "none")] #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_deny_explicit_impl(implement_via_object = false)] pub trait Tuple {} /// A marker for pointer-like types. /// -/// This trait can only be implemented for types that are certain to have -/// the same size and alignment as a [`usize`] or [`*const ()`](pointer). -/// To ensure this, there are special requirements on implementations -/// of `PointerLike` (other than the already-provided implementations -/// for built-in types): -/// -/// * The type must have `#[repr(transparent)]`. -/// * The type’s sole non-zero-sized field must itself implement `PointerLike`. +/// All types that have the same size and alignment as a `usize` or +/// `*const ()` automatically implement this trait. #[unstable(feature = "pointer_like_trait", issue = "none")] #[lang = "pointer_like"] #[diagnostic::on_unimplemented( message = "`{Self}` needs to have the same ABI as a pointer", label = "`{Self}` needs to be a pointer-like type" )] -#[rustc_do_not_implement_via_object] pub trait PointerLike {} +#[cfg(not(bootstrap))] marker_impls! { #[unstable(feature = "pointer_like_trait", issue = "none")] PointerLike for - isize, usize, {T} &T, {T} &mut T, @@ -1077,215 +1061,24 @@ 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", reason = "internal trait for implementing various traits for all function pointers" )] #[lang = "fn_ptr_trait"] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_deny_explicit_impl(implement_via_object = false)] pub trait FnPtr: Copy + Clone { /// Returns the address of the function pointer. #[lang = "fn_ptr_addr"] fn addr(self) -> *const (); } -/// 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(Box); -/// -/// impl Deref for MySmartPointer { -/// type Target = T; -/// fn deref(&self) -> &T { -/// &self.0 -/// } -/// } -/// -/// trait MyTrait {} -/// -/// impl MyTrait for i32 {} -/// -/// fn main() { -/// let ptr: MySmartPointer = MySmartPointer(Box::new(4)); -/// -/// // This coercion would be an error without the derive. -/// let ptr: MySmartPointer = 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 = ptr; -/// | --------------------------- ^^^ expected `MySmartPointer`, found `MySmartPointer` -/// | | -/// | expected due to this -/// | -/// = note: expected struct `MySmartPointer` -/// found struct `MySmartPointer` -/// = help: `i32` implements `MyTrait` so you could box the found value and coerce it to the trait object `Box`, 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(Box); -/// -/// impl Deref for MySmartPointer { -/// 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); -/// } -/// -/// // But using `dyn MyTrait` requires #[derive(CoercePointee)]. -/// fn call_func(value: MySmartPointer) { -/// 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); -/// | -------------------- help: consider changing method `func`'s `self` parameter to be `&self`: `&Self` -/// ... -/// 21 | fn call_func(value: MySmartPointer) { -/// | ^^^^^^^^^^^ `MyTrait` is not dyn compatible -/// | -/// note: for a trait to be dyn compatible it needs to allow building a vtable -/// for more information, visit -/// --> src/lib.rs:17:19 -/// | -/// 16 | trait MyTrait { -/// | ------- this trait is not dyn compatible... -/// 17 | fn func(self: MySmartPointer); -/// | ^^^^^^^^^^^^^^^^^^^^ ...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, -/// _phantom: PhantomData, -/// } -/// ``` -/// 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 { -/// inner: NonNull>, -/// } -/// -/// struct RcInner { -/// refcount: usize, -/// value: T, -/// } -/// -/// impl Deref for Rc { -/// type Target = T; -/// fn deref(&self) -> &T { -/// let ptr = self.inner.as_ptr(); -/// unsafe { &(*ptr).value } -/// } -/// } -/// -/// impl Rc { -/// pub fn new(value: T) -> Self { -/// let inner = Box::new(RcInner { -/// refcount: 1, -/// value, -/// }); -/// Self { -/// inner: NonNull::from(Box::leak(inner)), -/// } -/// } -/// } -/// -/// impl Clone for Rc { -/// fn clone(&self) -> Self { -/// // A real implementation would handle overflow here. -/// unsafe { (*self.inner.as_ptr()).refcount += 1 }; -/// Self { inner: self.inner } -/// } -/// } -/// -/// impl Drop for Rc { -/// 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) }); -/// } -/// } -/// } -/// ``` +/// Derive macro generating impls of traits related to smart pointers. #[rustc_builtin_macro(CoercePointee, attributes(pointee))] #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] #[unstable(feature = "derive_coerce_pointee", issue = "123430")] +#[cfg(not(bootstrap))] pub macro CoercePointee($item:item) { /* compiler built-in */ } diff --git a/core/src/marker/variance.rs b/core/src/marker/variance.rs deleted file mode 100644 index 23334e6575ddf..0000000000000 --- a/core/src/marker/variance.rs +++ /dev/null @@ -1,260 +0,0 @@ -#![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 $name - where T: ?Sized - { - /// Constructs a new instance of the variance marker. - pub const fn new() -> Self { - Self(PhantomData) - } - } - - impl self::sealed::Sealed for $name where T: ?Sized { - const VALUE: Self = Self::new(); - } - impl Variance for $name where T: ?Sized {} - - impl Default for $name - where T: ?Sized - { - fn default() -> Self { - Self(PhantomData) - } - } - - impl fmt::Debug for $name - where T: ?Sized - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}<{}>", stringify!($name), type_name::()) - } - } - - impl Clone for $name - where T: ?Sized - { - fn clone(&self) -> Self { - *self - } - } - - impl Copy for $name where T: ?Sized {} - - impl PartialEq for $name - where T: ?Sized - { - fn eq(&self, _: &Self) -> bool { - true - } - } - - impl Eq for $name where T: ?Sized {} - - impl PartialOrd for $name - where T: ?Sized - { - fn partial_cmp(&self, _: &Self) -> Option { - Some(Ordering::Equal) - } - } - - impl Ord for $name - where T: ?Sized - { - fn cmp(&self, _: &Self) -> Ordering { - Ordering::Equal - } - } - - impl Hash for $name - where T: ?Sized - { - fn hash(&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::>() == 0` - /// * `align_of::>() == 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::>() == 0` - /// * `align_of::>() == 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::>() == 0` - /// * `align_of::>() == 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::>() == 0` - /// * `align_of::>() == 1` - pub struct PhantomCovariant(PhantomData 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::>() == 0` - /// * `align_of::>() == 1` - pub struct PhantomContravariant(PhantomData); - /// 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::>() == 0` - /// * `align_of::>() == 1` - pub struct PhantomInvariant(PhantomData 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 -/// where -/// F: Fn(P) -> R, -/// { -/// function: F, -/// parameter: P, -/// return_value: PhantomCovariant, -/// } -/// -/// let bound_fn = BoundFn { -/// function: core::convert::identity, -/// parameter: 5u8, -/// return_value: variance(), -/// }; -/// ``` -pub const fn variance() -> T -where - T: Variance, -{ - T::VALUE -} diff --git a/core/src/mem/maybe_uninit.rs b/core/src/mem/maybe_uninit.rs index 0d8c3ef906bf0..58315cb74f0a1 100644 --- a/core/src/mem/maybe_uninit.rs +++ b/core/src/mem/maybe_uninit.rs @@ -1,5 +1,5 @@ use crate::any::type_name; -use crate::mem::ManuallyDrop; +use crate::mem::{self, ManuallyDrop}; use crate::{fmt, intrinsics, ptr, slice}; /// A wrapper type to construct uninitialized instances of `T`. @@ -232,26 +232,6 @@ use crate::{fmt, intrinsics, ptr, slice}; /// remain `#[repr(transparent)]`. That said, `MaybeUninit` will *always* guarantee that it has /// the same size, alignment, and ABI as `T`; it's just that the way `MaybeUninit` implements that /// guarantee may evolve. -/// -/// Note that even though `T` and `MaybeUninit` are ABI compatible it is still unsound to -/// transmute `&mut T` to `&mut MaybeUninit` and expose that to safe code because it would allow -/// safe code to access uninitialized memory: -/// -/// ```rust,no_run -/// use core::mem::MaybeUninit; -/// -/// fn unsound_transmute(val: &mut T) -> &mut MaybeUninit { -/// unsafe { core::mem::transmute(val) } -/// } -/// -/// fn main() { -/// let mut code = 0; -/// let code = &mut code; -/// let code2 = unsound_transmute(code); -/// *code2 = MaybeUninit::uninit(); -/// std::process::exit(*code); // UB! Accessing uninitialized memory. -/// } -/// ``` #[stable(feature = "maybe_uninit", since = "1.36.0")] // Lang item so we can wrap other types in it. This is useful for coroutines. #[lang = "maybe_uninit"] @@ -275,10 +255,7 @@ impl Clone for MaybeUninit { #[stable(feature = "maybe_uninit_debug", since = "1.41.0")] impl fmt::Debug for MaybeUninit { 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<{..}>"). - let full_name = type_name::(); - let prefix_len = full_name.find("MaybeUninit").unwrap(); - f.pad(&full_name[prefix_len..]) + f.pad(type_name::()) } } @@ -353,7 +330,7 @@ impl MaybeUninit { /// fn read(buf: &mut [MaybeUninit]) -> &[u8] { /// unsafe { /// let len = read_into_buffer(buf.as_mut_ptr() as *mut u8, buf.len()); - /// buf[..len].assume_init_ref() + /// MaybeUninit::slice_assume_init_ref(&buf[..len]) /// } /// } /// @@ -504,9 +481,9 @@ impl MaybeUninit { /// } /// } /// ``` - #[inline(always)] #[stable(feature = "maybe_uninit_write", since = "1.55.0")] - #[rustc_const_stable(feature = "const_maybe_uninit_write", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_maybe_uninit_write", issue = "63567")] + #[inline(always)] pub const fn write(&mut self, val: T) -> &mut T { *self = MaybeUninit::new(val); // SAFETY: We just initialized this value. @@ -548,7 +525,7 @@ impl MaybeUninit { /// until they are, it is advisable to avoid them.) #[stable(feature = "maybe_uninit", since = "1.36.0")] #[rustc_const_stable(feature = "const_maybe_uninit_as_ptr", since = "1.59.0")] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[inline(always)] pub const fn as_ptr(&self) -> *const T { // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer. @@ -590,7 +567,7 @@ impl MaybeUninit { /// until they are, it is advisable to avoid them.) #[stable(feature = "maybe_uninit", since = "1.36.0")] #[rustc_const_stable(feature = "const_maybe_uninit_as_mut_ptr", since = "1.83.0")] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[inline(always)] pub const fn as_mut_ptr(&mut self) -> *mut T { // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer. @@ -739,7 +716,7 @@ impl MaybeUninit { /// /// On top of that, all additional invariants of the type `T` must be /// satisfied, as the `Drop` implementation of `T` (or its members) may - /// rely on this. For example, setting a `Vec` to an invalid but + /// rely on this. For example, setting a [`Vec`] to an invalid but /// non-null address makes it initialized (under the current implementation; /// this does not constitute a stable guarantee), because the only /// requirement the compiler knows about it is that the data pointer must be @@ -747,6 +724,7 @@ impl MaybeUninit { /// behavior. /// /// [`assume_init`]: MaybeUninit::assume_init + /// [`Vec`]: ../../std/vec/struct.Vec.html #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized and @@ -929,7 +907,10 @@ impl MaybeUninit { /// }; /// ``` #[stable(feature = "maybe_uninit_ref", since = "1.55.0")] - #[rustc_const_stable(feature = "const_maybe_uninit_assume_init", since = "1.84.0")] + #[rustc_const_stable( + feature = "const_maybe_uninit_assume_init", + since = "CURRENT_RUSTC_VERSION" + )] #[inline(always)] pub const unsafe fn assume_init_mut(&mut self) -> &mut T { // SAFETY: the caller must guarantee that `self` is initialized. @@ -980,87 +961,44 @@ impl MaybeUninit { } } - /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. + /// Assuming all the elements are initialized, get a slice to them. /// - /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still - /// contain padding bytes which are left uninitialized. + /// # Safety /// - /// # Examples + /// It is up to the caller to guarantee that the `MaybeUninit` elements + /// really are in an initialized state. + /// Calling this when the content is not yet fully initialized causes undefined behavior. /// - /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] - /// use std::mem::MaybeUninit; + /// See [`assume_init_ref`] for more details and examples. /// - /// let val = 0x12345678_i32; - /// let uninit = MaybeUninit::new(val); - /// let uninit_bytes = uninit.as_bytes(); - /// let bytes = unsafe { uninit_bytes.assume_init_ref() }; - /// assert_eq!(bytes, val.to_ne_bytes()); - /// ``` - #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub const fn as_bytes(&self) -> &[MaybeUninit] { - // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { - slice::from_raw_parts(self.as_ptr().cast::>(), super::size_of::()) - } + /// [`assume_init_ref`]: MaybeUninit::assume_init_ref + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub const unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { + // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that + // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. + // The pointer obtained is valid since it refers to memory owned by `slice` which is a + // reference and thus guaranteed to be valid for reads. + unsafe { &*(slice as *const [Self] as *const [T]) } } - /// Returns the contents of this `MaybeUninit` as a mutable slice of potentially uninitialized - /// bytes. + /// Assuming all the elements are initialized, get a mutable slice to them. /// - /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still - /// contain padding bytes which are left uninitialized. + /// # Safety /// - /// # Examples + /// It is up to the caller to guarantee that the `MaybeUninit` elements + /// really are in an initialized state. + /// Calling this when the content is not yet fully initialized causes undefined behavior. /// - /// ``` - /// #![feature(maybe_uninit_as_bytes)] - /// use std::mem::MaybeUninit; + /// See [`assume_init_mut`] for more details and examples. /// - /// let val = 0x12345678_i32; - /// let mut uninit = MaybeUninit::new(val); - /// let uninit_bytes = uninit.as_bytes_mut(); - /// if cfg!(target_endian = "little") { - /// uninit_bytes[0].write(0xcd); - /// } else { - /// uninit_bytes[3].write(0xcd); - /// } - /// let val2 = unsafe { uninit.assume_init() }; - /// assert_eq!(val2, 0x123456cd); - /// ``` - #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { - // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { - slice::from_raw_parts_mut( - self.as_mut_ptr().cast::>(), - super::size_of::(), - ) - } - } - - /// Deprecated version of [`slice::assume_init_ref`]. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[deprecated( - note = "replaced by inherent assume_init_ref method; will eventually be removed", - since = "1.83.0" - )] - pub const unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { - // SAFETY: Same for both methods. - unsafe { slice.assume_init_ref() } - } - - /// Deprecated version of [`slice::assume_init_mut`]. + /// [`assume_init_mut`]: MaybeUninit::assume_init_mut #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[deprecated( - note = "replaced by inherent assume_init_mut method; will eventually be removed", - since = "1.83.0" - )] + #[inline(always)] pub const unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T] { - // SAFETY: Same for both methods. - unsafe { slice.assume_init_mut() } + // SAFETY: similar to safety notes for `slice_get_ref`, but we have a + // mutable reference which is also guaranteed to be valid for writes. + unsafe { &mut *(slice as *mut [Self] as *mut [T]) } } /// Gets a pointer to the first element of the array. @@ -1077,34 +1015,142 @@ impl MaybeUninit { this.as_mut_ptr() as *mut T } - /// Deprecated version of [`slice::write_copy_of_slice`]. + /// Copies the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`. + /// + /// If `T` does not implement `Copy`, use [`clone_from_slice`] + /// + /// This is similar to [`slice::copy_from_slice`]. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// use std::mem::MaybeUninit; + /// + /// let mut dst = [MaybeUninit::uninit(); 32]; + /// let src = [0; 32]; + /// + /// let init = MaybeUninit::copy_from_slice(&mut dst, &src); + /// + /// assert_eq!(init, src); + /// ``` + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// use std::mem::MaybeUninit; + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = [0; 16]; + /// + /// MaybeUninit::copy_from_slice(&mut vec.spare_capacity_mut()[..src.len()], &src); + /// + /// // SAFETY: we have just copied all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); + /// } + /// + /// assert_eq!(vec, src); + /// ``` + /// + /// [`clone_from_slice`]: MaybeUninit::clone_from_slice #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] - #[deprecated( - note = "replaced by inherent write_copy_of_slice method; will eventually be removed", - since = "1.83.0" - )] pub fn copy_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] where T: Copy, { - this.write_copy_of_slice(src) + // SAFETY: &[T] and &[MaybeUninit] have the same layout + let uninit_src: &[MaybeUninit] = unsafe { super::transmute(src) }; + + this.copy_from_slice(uninit_src); + + // SAFETY: Valid elements have just been copied into `this` so it is initialized + unsafe { MaybeUninit::slice_assume_init_mut(this) } } - /// Deprecated version of [`slice::write_clone_of_slice`]. + /// Clones the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`. + /// Any already initialized elements will not be dropped. + /// + /// If `T` implements `Copy`, use [`copy_from_slice`] + /// + /// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics. + /// + /// If there is a panic, the already cloned elements will be dropped. + /// + /// # Examples + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// use std::mem::MaybeUninit; + /// + /// let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; + /// let src = ["wibbly".to_string(), "wobbly".to_string(), "timey".to_string(), "wimey".to_string(), "stuff".to_string()]; + /// + /// let init = MaybeUninit::clone_from_slice(&mut dst, &src); + /// + /// assert_eq!(init, src); + /// # // Prevent leaks for Miri + /// # unsafe { std::ptr::drop_in_place(init); } + /// ``` + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// use std::mem::MaybeUninit; + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = ["rust", "is", "a", "pretty", "cool", "language"]; + /// + /// MaybeUninit::clone_from_slice(&mut vec.spare_capacity_mut()[..src.len()], &src); + /// + /// // SAFETY: we have just cloned all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); + /// } + /// + /// assert_eq!(vec, src); + /// ``` + /// + /// [`copy_from_slice`]: MaybeUninit::copy_from_slice #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] - #[deprecated( - note = "replaced by inherent write_clone_of_slice method; will eventually be removed", - since = "1.83.0" - )] pub fn clone_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] where T: Clone, { - this.write_clone_of_slice(src) + // unlike copy_from_slice this does not call clone_from_slice on the slice + // this is because `MaybeUninit` does not implement Clone. + + assert_eq!(this.len(), src.len(), "destination and source slices have different lengths"); + // NOTE: We need to explicitly slice them to the same length + // for bounds checking to be elided, and the optimizer will + // generate memcpy for simple cases (for example T = u8). + let len = this.len(); + let src = &src[..len]; + + // guard is needed b/c panic might happen during a clone + let mut guard = Guard { slice: this, initialized: 0 }; + + for i in 0..len { + guard.slice[i].write(src[i].clone()); + guard.initialized += 1; + } + + super::forget(guard); + + // SAFETY: Valid elements have just been written into `this` so it is initialized + unsafe { MaybeUninit::slice_assume_init_mut(this) } } - /// Fills a slice with elements by cloning `value`, returning a mutable reference to the now - /// initialized contents of the slice. + /// Fills `this` with elements by cloning `value`, returning a mutable reference to the now + /// initialized contents of `this`. /// Any previously initialized elements will not be dropped. /// /// This is similar to [`slice::fill`]. @@ -1118,26 +1164,27 @@ impl MaybeUninit { /// /// # Examples /// + /// Fill an uninit vec with 1. /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = [const { MaybeUninit::uninit() }; 10]; - /// let initialized = MaybeUninit::fill(&mut buf, 1); + /// let mut buf = vec![MaybeUninit::uninit(); 10]; + /// let initialized = MaybeUninit::fill(buf.as_mut_slice(), 1); /// assert_eq!(initialized, &mut [1; 10]); /// ``` #[doc(alias = "memset")] #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill(this: &mut [MaybeUninit], value: T) -> &mut [T] + pub fn fill<'a>(this: &'a mut [MaybeUninit], value: T) -> &'a mut [T] where T: Clone, { SpecFill::spec_fill(this, value); // SAFETY: Valid elements have just been filled into `this` so it is initialized - unsafe { this.assume_init_mut() } + unsafe { MaybeUninit::slice_assume_init_mut(this) } } - /// Fills a slice with elements returned by calling a closure repeatedly. + /// Fills `this` with elements returned by calling a closure repeatedly. /// /// This method uses a closure to create new values. If you'd rather `Clone` a given value, use /// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can @@ -1152,16 +1199,17 @@ impl MaybeUninit { /// /// # Examples /// + /// Fill an uninit vec with the default value. /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = [const { MaybeUninit::::uninit() }; 10]; - /// let initialized = MaybeUninit::fill_with(&mut buf, Default::default); + /// let mut buf = vec![MaybeUninit::::uninit(); 10]; + /// let initialized = MaybeUninit::fill_with(buf.as_mut_slice(), Default::default); /// assert_eq!(initialized, &mut [0; 10]); /// ``` #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_with(this: &mut [MaybeUninit], mut f: F) -> &mut [T] + pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit], mut f: F) -> &'a mut [T] where F: FnMut() -> T, { @@ -1175,13 +1223,13 @@ impl MaybeUninit { super::forget(guard); // SAFETY: Valid elements have just been written into `this` so it is initialized - unsafe { this.assume_init_mut() } + unsafe { MaybeUninit::slice_assume_init_mut(this) } } - /// Fills a slice with elements yielded by an iterator until either all elements have been + /// Fills `this` with elements yielded by an iterator until either all elements have been /// initialized or the iterator is empty. /// - /// Returns two slices. The first slice contains the initialized portion of the original slice. + /// Returns two slices. The first slice contains the initialized portion of the original slice. /// The second slice is the still-uninitialized remainder of the original slice. /// /// # Panics @@ -1193,51 +1241,37 @@ impl MaybeUninit { /// /// # Examples /// - /// Completely filling the slice: - /// + /// Fill an uninit vec with a cycling iterator. /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = [const { MaybeUninit::uninit() }; 5]; + /// let mut buf = vec![MaybeUninit::uninit(); 5]; /// /// let iter = [1, 2, 3].into_iter().cycle(); /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); /// /// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]); - /// assert_eq!(remainder.len(), 0); + /// assert_eq!(0, remainder.len()); /// ``` /// - /// Partially filling the slice: - /// + /// Fill an uninit vec, but not completely. /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = [const { MaybeUninit::uninit() }; 5]; + /// let mut buf = vec![MaybeUninit::uninit(); 5]; /// let iter = [1, 2]; /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); /// /// assert_eq!(initialized, &mut [1, 2]); /// assert_eq!(remainder.len(), 3); /// ``` - /// - /// Checking an iterator after filling a slice: - /// - /// ``` - /// #![feature(maybe_uninit_fill)] - /// use std::mem::MaybeUninit; - /// - /// let mut buf = [const { MaybeUninit::uninit() }; 3]; - /// let mut iter = [1, 2, 3, 4, 5].into_iter(); - /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter.by_ref()); - /// - /// assert_eq!(initialized, &mut [1, 2, 3]); - /// assert_eq!(remainder.len(), 0); - /// assert_eq!(iter.as_slice(), &[4, 5]); - /// ``` #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_from(this: &mut [MaybeUninit], it: I) -> (&mut [T], &mut [MaybeUninit]) + pub fn fill_from<'a, I>( + this: &'a mut [MaybeUninit], + it: I, + ) -> (&'a mut [T], &'a mut [MaybeUninit]) where I: IntoIterator, { @@ -1257,169 +1291,70 @@ impl MaybeUninit { // SAFETY: Valid elements have just been written into `init`, so that portion // of `this` is initialized. - (unsafe { initted.assume_init_mut() }, remainder) + (unsafe { MaybeUninit::slice_assume_init_mut(initted) }, remainder) } - /// Deprecated version of [`slice::as_bytes`]. - #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - #[deprecated( - note = "replaced by inherent as_bytes method; will eventually be removed", - since = "1.83.0" - )] - pub fn slice_as_bytes(this: &[MaybeUninit]) -> &[MaybeUninit] { - this.as_bytes() - } - - /// Deprecated version of [`slice::as_bytes_mut`]. - #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - #[deprecated( - note = "replaced by inherent as_bytes_mut method; will eventually be removed", - since = "1.83.0" - )] - pub fn slice_as_bytes_mut(this: &mut [MaybeUninit]) -> &mut [MaybeUninit] { - this.as_bytes_mut() - } -} - -impl [MaybeUninit] { - /// Copies the elements from `src` to `self`, - /// returning a mutable reference to the now initialized contents of `self`. - /// - /// If `T` does not implement `Copy`, use [`write_clone_of_slice`] instead. - /// - /// This is similar to [`slice::copy_from_slice`]. - /// - /// # Panics + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// - /// This function will panic if the two slices have different lengths. + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. /// /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] /// use std::mem::MaybeUninit; /// - /// let mut dst = [MaybeUninit::uninit(); 32]; - /// let src = [0; 32]; - /// - /// let init = dst.write_copy_of_slice(&src); - /// - /// assert_eq!(init, src); - /// ``` - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// - /// let mut vec = Vec::with_capacity(32); - /// let src = [0; 16]; - /// - /// vec.spare_capacity_mut()[..src.len()].write_copy_of_slice(&src); - /// - /// // SAFETY: we have just copied all the elements of len into the spare capacity - /// // the first src.len() elements of the vec are valid now. - /// unsafe { - /// vec.set_len(src.len()); - /// } - /// - /// assert_eq!(vec, src); + /// let val = 0x12345678_i32; + /// let uninit = MaybeUninit::new(val); + /// let uninit_bytes = uninit.as_bytes(); + /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(uninit_bytes) }; + /// assert_eq!(bytes, val.to_ne_bytes()); /// ``` - /// - /// [`write_clone_of_slice`]: slice::write_clone_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] - #[rustc_const_unstable(feature = "maybe_uninit_write_slice", issue = "79995")] - pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T] - where - T: Copy, - { - // SAFETY: &[T] and &[MaybeUninit] have the same layout - let uninit_src: &[MaybeUninit] = unsafe { super::transmute(src) }; - - self.copy_from_slice(uninit_src); - - // SAFETY: Valid elements have just been copied into `self` so it is initialized - unsafe { self.assume_init_mut() } + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub fn as_bytes(&self) -> &[MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts(self.as_ptr() as *const MaybeUninit, mem::size_of::()) + } } - /// Clones the elements from `src` to `self`, - /// returning a mutable reference to the now initialized contents of `self`. - /// Any already initialized elements will not be dropped. - /// - /// If `T` implements `Copy`, use [`write_copy_of_slice`] instead. - /// - /// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics. + /// Returns the contents of this `MaybeUninit` as a mutable slice of potentially uninitialized + /// bytes. /// - /// If there is a panic, the already cloned elements will be dropped. + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. /// /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] + /// #![feature(maybe_uninit_as_bytes)] /// use std::mem::MaybeUninit; /// - /// let mut dst = [const { MaybeUninit::uninit() }; 5]; - /// let src = ["wibbly", "wobbly", "timey", "wimey", "stuff"].map(|s| s.to_string()); - /// - /// let init = dst.write_clone_of_slice(&src); - /// - /// assert_eq!(init, src); - /// - /// # // Prevent leaks for Miri - /// # unsafe { std::ptr::drop_in_place(init); } - /// ``` - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// - /// let mut vec = Vec::with_capacity(32); - /// let src = ["rust", "is", "a", "pretty", "cool", "language"].map(|s| s.to_string()); - /// - /// vec.spare_capacity_mut()[..src.len()].write_clone_of_slice(&src); - /// - /// // SAFETY: we have just cloned all the elements of len into the spare capacity - /// // the first src.len() elements of the vec are valid now. - /// unsafe { - /// vec.set_len(src.len()); + /// let val = 0x12345678_i32; + /// let mut uninit = MaybeUninit::new(val); + /// let uninit_bytes = uninit.as_bytes_mut(); + /// if cfg!(target_endian = "little") { + /// uninit_bytes[0].write(0xcd); + /// } else { + /// uninit_bytes[3].write(0xcd); /// } - /// - /// assert_eq!(vec, src); + /// let val2 = unsafe { uninit.assume_init() }; + /// assert_eq!(val2, 0x123456cd); /// ``` - /// - /// [`write_copy_of_slice`]: slice::write_copy_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] - pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T] - where - T: Clone, - { - // unlike copy_from_slice this does not call clone_from_slice on the slice - // this is because `MaybeUninit` does not implement Clone. - - assert_eq!(self.len(), src.len(), "destination and source slices have different lengths"); - - // NOTE: We need to explicitly slice them to the same length - // for bounds checking to be elided, and the optimizer will - // generate memcpy for simple cases (for example T = u8). - let len = self.len(); - let src = &src[..len]; - - // guard is needed b/c panic might happen during a clone - let mut guard = Guard { slice: self, initialized: 0 }; - - for i in 0..len { - guard.slice[i].write(src[i].clone()); - guard.initialized += 1; + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts_mut( + self.as_mut_ptr() as *mut MaybeUninit, + mem::size_of::(), + ) } - - super::forget(guard); - - // SAFETY: Valid elements have just been written into `self` so it is initialized - unsafe { self.assume_init_mut() } } - /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. + /// Returns the contents of this slice of `MaybeUninit` as a slice of potentially uninitialized + /// bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still /// contain padding bytes which are left uninitialized. @@ -1431,22 +1366,21 @@ impl [MaybeUninit] { /// use std::mem::MaybeUninit; /// /// let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)]; - /// let uninit_bytes = uninit.as_bytes(); - /// let bytes = unsafe { uninit_bytes.assume_init_ref() }; + /// let uninit_bytes = MaybeUninit::slice_as_bytes(&uninit); + /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(&uninit_bytes) }; /// let val1 = u16::from_ne_bytes(bytes[0..2].try_into().unwrap()); /// let val2 = u16::from_ne_bytes(bytes[2..4].try_into().unwrap()); /// assert_eq!(&[val1, val2], &[0x1234u16, 0x5678u16]); /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub const fn as_bytes(&self) -> &[MaybeUninit] { + pub fn slice_as_bytes(this: &[MaybeUninit]) -> &[MaybeUninit] { + let bytes = mem::size_of_val(this); // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { - slice::from_raw_parts(self.as_ptr().cast::>(), super::size_of_val(self)) - } + unsafe { slice::from_raw_parts(this.as_ptr() as *const MaybeUninit, bytes) } } - /// Returns the contents of this `MaybeUninit` slice as a mutable slice of potentially - /// uninitialized bytes. + /// Returns the contents of this mutable slice of `MaybeUninit` as a mutable slice of + /// potentially uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still /// contain padding bytes which are left uninitialized. @@ -1459,8 +1393,8 @@ impl [MaybeUninit] { /// /// let mut uninit = [MaybeUninit::::uninit(), MaybeUninit::::uninit()]; /// let uninit_bytes = MaybeUninit::slice_as_bytes_mut(&mut uninit); - /// uninit_bytes.write_copy_of_slice(&[0x12, 0x34, 0x56, 0x78]); - /// let vals = unsafe { uninit.assume_init_ref() }; + /// MaybeUninit::copy_from_slice(uninit_bytes, &[0x12, 0x34, 0x56, 0x78]); + /// let vals = unsafe { MaybeUninit::slice_assume_init_ref(&uninit) }; /// if cfg!(target_endian = "little") { /// assert_eq!(vals, &[0x3412u16, 0x7856u16]); /// } else { @@ -1468,74 +1402,10 @@ impl [MaybeUninit] { /// } /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { + pub fn slice_as_bytes_mut(this: &mut [MaybeUninit]) -> &mut [MaybeUninit] { + let bytes = mem::size_of_val(this); // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { - slice::from_raw_parts_mut( - self.as_mut_ptr() as *mut MaybeUninit, - super::size_of_val(self), - ) - } - } - - /// Drops the contained values in place. - /// - /// # Safety - /// - /// It is up to the caller to guarantee that every `MaybeUninit` in the slice - /// really is in an initialized state. Calling this when the content is not yet - /// fully initialized causes undefined behavior. - /// - /// On top of that, all additional invariants of the type `T` must be - /// satisfied, as the `Drop` implementation of `T` (or its members) may - /// rely on this. For example, setting a `Vec` to an invalid but - /// non-null address makes it initialized (under the current implementation; - /// this does not constitute a stable guarantee), because the only - /// requirement the compiler knows about it is that the data pointer must be - /// non-null. Dropping such a `Vec` however will cause undefined - /// behaviour. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub unsafe fn assume_init_drop(&mut self) { - if !self.is_empty() { - // SAFETY: the caller must guarantee that every element of `self` - // is initialized and satisfies all invariants of `T`. - // Dropping the value in place is safe if that is the case. - unsafe { ptr::drop_in_place(self as *mut [MaybeUninit] as *mut [T]) } - } - } - - /// Gets a shared reference to the contained value. - /// - /// # Safety - /// - /// Calling this when the content is not yet fully initialized causes undefined - /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in - /// the slice really is in an initialized state. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const unsafe fn assume_init_ref(&self) -> &[T] { - // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that - // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. - // The pointer obtained is valid since it refers to memory owned by `slice` which is a - // reference and thus guaranteed to be valid for reads. - unsafe { &*(self as *const Self as *const [T]) } - } - - /// Gets a mutable (unique) reference to the contained value. - /// - /// # Safety - /// - /// Calling this when the content is not yet fully initialized causes undefined - /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in the - /// slice really is in an initialized state. For instance, `.assume_init_mut()` cannot - /// be used to initialize a `MaybeUninit` slice. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const unsafe fn assume_init_mut(&mut self) -> &mut [T] { - // SAFETY: similar to safety notes for `slice_get_ref`, but we have a - // mutable reference which is also guaranteed to be valid for writes. - unsafe { &mut *(self as *mut Self as *mut [T]) } + unsafe { slice::from_raw_parts_mut(this.as_mut_ptr() as *mut MaybeUninit, bytes) } } } @@ -1588,7 +1458,7 @@ impl<'a, T> Drop for Guard<'a, T> { let initialized_part = &mut self.slice[..self.initialized]; // SAFETY: this raw sub-slice will contain only initialized objects. unsafe { - initialized_part.assume_init_drop(); + crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); } } } diff --git a/core/src/mem/mod.rs b/core/src/mem/mod.rs index b9bb6d6a13f7f..4cf52042a57f6 100644 --- a/core/src/mem/mod.rs +++ b/core/src/mem/mod.rs @@ -333,7 +333,7 @@ pub const fn size_of() -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_size_of_val", since = "1.85.0")] +#[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] #[cfg_attr(not(test), rustc_diagnostic_item = "mem_size_of_val")] pub const fn size_of_val(val: &T) -> usize { // SAFETY: `val` is a reference, so it's a valid raw pointer @@ -390,6 +390,7 @@ pub const fn size_of_val(val: &T) -> usize { #[inline] #[must_use] #[unstable(feature = "layout_for_ptr", issue = "69835")] +#[rustc_const_unstable(feature = "const_size_of_val_raw", issue = "46571")] pub const unsafe fn size_of_val_raw(val: *const T) -> usize { // SAFETY: the caller must provide a valid raw pointer unsafe { intrinsics::size_of_val(val) } @@ -484,7 +485,7 @@ pub const fn align_of() -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_align_of_val", since = "1.85.0")] +#[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] #[allow(deprecated)] pub const fn align_of_val(val: &T) -> usize { // SAFETY: val is a reference, so it's a valid raw pointer @@ -533,6 +534,7 @@ pub const fn align_of_val(val: &T) -> usize { #[inline] #[must_use] #[unstable(feature = "layout_for_ptr", issue = "69835")] +#[rustc_const_unstable(feature = "const_align_of_val_raw", issue = "46571")] pub const unsafe fn align_of_val_raw(val: *const T) -> usize { // SAFETY: the caller must provide a valid raw pointer unsafe { intrinsics::min_align_of_val(val) } @@ -725,12 +727,12 @@ pub unsafe fn uninitialized() -> T { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_swap", since = "1.85.0")] +#[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[rustc_diagnostic_item = "mem_swap"] pub const fn swap(x: &mut T, y: &mut T) { // SAFETY: `&mut` guarantees these are typed readable and writable // as well as non-overlapping. - unsafe { intrinsics::typed_swap_nonoverlapping(x, y) } + unsafe { intrinsics::typed_swap(x, y) } } /// Replaces `dest` with the default value of `T`, returning the previous `dest` value. @@ -1241,17 +1243,6 @@ pub trait SizedTypeProperties: Sized { #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] const LAYOUT: Layout = Layout::new::(); - - /// The largest safe length for a `[Self]`. - /// - /// Anything larger than this would make `size_of_val` overflow `isize::MAX`, - /// which is never allowed for a single object. - #[doc(hidden)] - #[unstable(feature = "sized_type_properties", issue = "none")] - const MAX_SLICE_LEN: usize = match size_of::() { - 0 => usize::MAX, - n => (isize::MAX as usize) / n, - }; } #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] diff --git a/core/src/mem/transmutability.rs b/core/src/mem/transmutability.rs index 6a4f84c849cb1..7fa3c33439170 100644 --- a/core/src/mem/transmutability.rs +++ b/core/src/mem/transmutability.rs @@ -84,8 +84,7 @@ use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; /// `usize` is stable, but not portable. #[unstable(feature = "transmutability", issue = "99571")] #[lang = "transmute_trait"] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_deny_explicit_impl(implement_via_object = false)] #[rustc_coinductive] pub unsafe trait TransmuteFrom where diff --git a/core/src/net/display_buffer.rs b/core/src/net/display_buffer.rs index 625ad5401f5c0..bab84a97308b3 100644 --- a/core/src/net/display_buffer.rs +++ b/core/src/net/display_buffer.rs @@ -2,23 +2,23 @@ use crate::mem::MaybeUninit; use crate::{fmt, str}; /// Used for slow path in `Display` implementations when alignment is required. -pub(super) struct DisplayBuffer { +pub struct DisplayBuffer { buf: [MaybeUninit; SIZE], len: usize, } impl DisplayBuffer { #[inline] - pub(super) const fn new() -> Self { + pub const fn new() -> Self { Self { buf: [MaybeUninit::uninit(); SIZE], len: 0 } } #[inline] - pub(super) fn as_str(&self) -> &str { + pub fn as_str(&self) -> &str { // SAFETY: `buf` is only written to by the `fmt::Write::write_str` implementation // which writes a valid UTF-8 string to `buf` and correctly sets `len`. unsafe { - let s = self.buf[..self.len].assume_init_ref(); + let s = MaybeUninit::slice_assume_init_ref(&self.buf[..self.len]); str::from_utf8_unchecked(s) } } @@ -29,7 +29,7 @@ impl fmt::Write for DisplayBuffer { let bytes = s.as_bytes(); if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) { - buf.write_copy_of_slice(bytes); + MaybeUninit::copy_from_slice(buf, bytes); self.len += bytes.len(); Ok(()) } else { diff --git a/core/src/net/ip_addr.rs b/core/src/net/ip_addr.rs index b11ba05685352..6746f0b2b316b 100644 --- a/core/src/net/ip_addr.rs +++ b/core/src/net/ip_addr.rs @@ -527,7 +527,7 @@ impl Ipv4Addr { /// ``` /// use std::net::Ipv4Addr; /// - /// let addr = Ipv4Addr::from_bits(0x12345678); + /// let addr = Ipv4Addr::from(0x12345678); /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr); /// ``` #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] @@ -1294,7 +1294,7 @@ impl Ipv6Addr { /// 0x1020, 0x3040, 0x5060, 0x7080, /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, /// ); - /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, addr.to_bits()); + /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr)); /// ``` /// /// ``` @@ -1330,7 +1330,7 @@ impl Ipv6Addr { /// ``` /// use std::net::Ipv6Addr; /// - /// let addr = Ipv6Addr::from_bits(0x102030405060708090A0B0C0D0E0F00D_u128); + /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128); /// assert_eq!( /// Ipv6Addr::new( /// 0x1020, 0x3040, 0x5060, 0x7080, @@ -1539,9 +1539,8 @@ impl Ipv6Addr { /// // Addresses reserved for benchmarking (`2001:2::/48`) /// assert_eq!(Ipv6Addr::new(0x2001, 2, 0, 0, 0, 0, 0, 1,).is_global(), false); /// - /// // Addresses reserved for documentation (`2001:db8::/32` and `3fff::/20`) + /// // Addresses reserved for documentation (`2001:db8::/32`) /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1).is_global(), false); - /// assert_eq!(Ipv6Addr::new(0x3fff, 0, 0, 0, 0, 0, 0, 0).is_global(), false); /// /// // Unique local addresses (`fc00::/7`) /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 1).is_global(), false); @@ -1602,8 +1601,8 @@ impl Ipv6Addr { /// ``` #[must_use] #[inline] - #[stable(feature = "ipv6_is_unique_local", since = "1.84.0")] - #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "1.84.0")] + #[stable(feature = "ipv6_is_unique_local", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "CURRENT_RUSTC_VERSION")] pub const fn is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } @@ -1680,19 +1679,18 @@ impl Ipv6Addr { /// ``` #[must_use] #[inline] - #[stable(feature = "ipv6_is_unique_local", since = "1.84.0")] - #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "1.84.0")] + #[stable(feature = "ipv6_is_unique_local", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ipv6_is_unique_local", since = "CURRENT_RUSTC_VERSION")] pub const fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32` and `3fff::/20`). + /// (`2001:db8::/32`). /// - /// This property is defined by [IETF RFC 3849] and [IETF RFC 9637]. + /// This property is defined in [IETF RFC 3849]. /// /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - /// [IETF RFC 9637]: https://tools.ietf.org/html/rfc9637 /// /// # Examples /// @@ -1703,13 +1701,12 @@ impl Ipv6Addr { /// /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); - /// assert_eq!(Ipv6Addr::new(0x3fff, 0, 0, 0, 0, 0, 0, 0).is_documentation(), true); /// ``` #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] pub const fn is_documentation(&self) -> bool { - matches!(self.segments(), [0x2001, 0xdb8, ..] | [0x3fff, 0..=0x0fff, ..]) + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } /// Returns [`true`] if this is an address reserved for benchmarking (`2001:2::/48`). diff --git a/core/src/num/dec2flt/decimal.rs b/core/src/num/dec2flt/decimal.rs index b37724ba62d5e..be9c0eccd5eb8 100644 --- a/core/src/num/dec2flt/decimal.rs +++ b/core/src/num/dec2flt/decimal.rs @@ -12,7 +12,7 @@ use crate::num::dec2flt::common::{ByteSlice, is_8digits}; #[derive(Clone)] -pub(super) struct Decimal { +pub struct Decimal { /// The number of significant digits in the decimal. pub num_digits: usize, /// The offset of the decimal point in the significant digits. @@ -55,13 +55,13 @@ impl Decimal { /// /// In Python: /// `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))` - pub(super) const MAX_DIGITS: usize = 768; + pub const MAX_DIGITS: usize = 768; /// The max digits that can be exactly represented in a 64-bit integer. - pub(super) const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19; - pub(super) const DECIMAL_POINT_RANGE: i32 = 2047; + pub const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19; + pub const DECIMAL_POINT_RANGE: i32 = 2047; /// Append a digit to the buffer. - pub(super) fn try_add_digit(&mut self, digit: u8) { + pub fn try_add_digit(&mut self, digit: u8) { if self.num_digits < Self::MAX_DIGITS { self.digits[self.num_digits] = digit; } @@ -69,7 +69,7 @@ impl Decimal { } /// Trim trailing zeros from the buffer. - pub(super) fn trim(&mut self) { + pub fn trim(&mut self) { // All of the following calls to `Decimal::trim` can't panic because: // // 1. `parse_decimal` sets `num_digits` to a max of `Decimal::MAX_DIGITS`. @@ -83,7 +83,7 @@ impl Decimal { } } - pub(super) fn round(&self) -> u64 { + pub fn round(&self) -> u64 { if self.num_digits == 0 || self.decimal_point < 0 { return 0; } else if self.decimal_point > 18 { @@ -111,7 +111,7 @@ impl Decimal { } /// Computes decimal * 2^shift. - pub(super) fn left_shift(&mut self, shift: usize) { + pub fn left_shift(&mut self, shift: usize) { if self.num_digits == 0 { return; } @@ -152,7 +152,7 @@ impl Decimal { } /// Computes decimal * 2^-shift. - pub(super) fn right_shift(&mut self, shift: usize) { + pub fn right_shift(&mut self, shift: usize) { let mut read_index = 0; let mut write_index = 0; let mut n = 0_u64; @@ -202,7 +202,7 @@ impl Decimal { } /// Parse a big integer representation of the float as a decimal. -pub(super) fn parse_decimal(mut s: &[u8]) -> Decimal { +pub fn parse_decimal(mut s: &[u8]) -> Decimal { let mut d = Decimal::default(); let start = s; diff --git a/core/src/num/dec2flt/fpu.rs b/core/src/num/dec2flt/fpu.rs index daeee1755b0b5..8d62684f8d383 100644 --- a/core/src/num/dec2flt/fpu.rs +++ b/core/src/num/dec2flt/fpu.rs @@ -1,7 +1,7 @@ //! Platform-specific, assembly instructions to avoid //! intermediate rounding on architectures with FPUs. -pub(super) use fpu_precision::set_precision; +pub use fpu_precision::set_precision; // On x86, the x87 FPU is used for float operations if the SSE/SSE2 extensions are not available. // The x87 FPU operates with 80 bits of precision by default, which means that operations will @@ -42,7 +42,7 @@ mod fpu_precision { /// - 0b10, double precision i.e., 64-bits /// - 0b11, double extended precision i.e., 80-bits (default state) /// The 0b01 value is reserved and should not be used. - pub(crate) struct FPUControlWord(u16); + pub struct FPUControlWord(u16); fn set_cw(cw: u16) { // SAFETY: the `fldcw` instruction has been audited to be able to work correctly with @@ -57,7 +57,7 @@ mod fpu_precision { } /// Sets the precision field of the FPU to `T` and returns a `FPUControlWord`. - pub(crate) fn set_precision() -> FPUControlWord { + pub fn set_precision() -> FPUControlWord { let mut cw = 0_u16; // Compute the value for the Precision Control field that is appropriate for `T`. @@ -97,5 +97,5 @@ mod fpu_precision { // precision of the computation is determined on a per-operation basis. #[cfg(any(not(target_arch = "x86"), target_feature = "sse2"))] mod fpu_precision { - pub(crate) fn set_precision() {} + pub fn set_precision() {} } diff --git a/core/src/num/dec2flt/table.rs b/core/src/num/dec2flt/table.rs index 942c2eacfd276..4856074a62bd0 100644 --- a/core/src/num/dec2flt/table.rs +++ b/core/src/num/dec2flt/table.rs @@ -6,17 +6,16 @@ //! //! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py` -pub(super) const SMALLEST_POWER_OF_FIVE: i32 = -342; -pub(super) const LARGEST_POWER_OF_FIVE: i32 = 308; -pub(super) const N_POWERS_OF_FIVE: usize = - (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize; +pub const SMALLEST_POWER_OF_FIVE: i32 = -342; +pub const LARGEST_POWER_OF_FIVE: i32 = 308; +pub const N_POWERS_OF_FIVE: usize = (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize; // Use static to avoid long compile times: Rust compiler errors // can have the entire table compiled multiple times, and then // emit code multiple times, even if it's stripped out in // the final binary. #[rustfmt::skip] -pub(super) static POWER_OF_FIVE_128: [(u64, u64); N_POWERS_OF_FIVE] = [ +pub static POWER_OF_FIVE_128: [(u64, u64); N_POWERS_OF_FIVE] = [ (0xeef453d6923bd65a, 0x113faa2906a13b3f), // 5^-342 (0x9558b4661b6565f8, 0x4ac7ca59a424c507), // 5^-341 (0xbaaee17fa23ebf76, 0x5d79bcf00d2df649), // 5^-340 diff --git a/core/src/num/f128.rs b/core/src/num/f128.rs index 5e45974b3d422..abeccb7eea248 100644 --- a/core/src/num/f128.rs +++ b/core/src/num/f128.rs @@ -504,6 +504,7 @@ impl f128 { /// /// ```rust /// #![feature(f128)] + /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): remove when `eqtf2` is available /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -515,15 +516,13 @@ impl f128 { /// # } /// ``` /// - /// This operation corresponds to IEEE-754 `nextUp`. - /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[doc(alias = "nextUp")] #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -559,6 +558,7 @@ impl f128 { /// /// ```rust /// #![feature(f128)] + /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): remove when `eqtf2` is available /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -570,15 +570,13 @@ impl f128 { /// # } /// ``` /// - /// This operation corresponds to IEEE-754 `nextDown`. - /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[doc(alias = "nextDown")] #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -670,8 +668,7 @@ 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. 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. + /// This also matches the behavior of libm’s fmax. /// /// ``` /// #![feature(f128)] @@ -697,8 +694,7 @@ 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. 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. + /// This also matches the behavior of libm’s fmin. /// /// ``` /// #![feature(f128)] @@ -811,6 +807,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] + /// #![feature(num_midpoint)] /// # // Using aarch64 because `reliable_f128_math` is needed /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { /// @@ -820,8 +817,8 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] - #[rustc_const_unstable(feature = "f128", issue = "116909")] - pub const fn midpoint(self, other: f128) -> f128 { + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f128) -> f128 { const LO: f128 = f128::MIN_POSITIVE * 2.; const HI: f128 = f128::MAX / 2.; diff --git a/core/src/num/f16.rs b/core/src/num/f16.rs index e3176cd168852..0d3e92695707c 100644 --- a/core/src/num/f16.rs +++ b/core/src/num/f16.rs @@ -497,6 +497,7 @@ impl f16 { /// /// ```rust /// #![feature(f16)] + /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): ABI issues on MSVC /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -508,15 +509,13 @@ impl f16 { /// # } /// ``` /// - /// This operation corresponds to IEEE-754 `nextUp`. - /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[doc(alias = "nextUp")] #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -552,6 +551,7 @@ impl f16 { /// /// ```rust /// #![feature(f16)] + /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): ABI issues on MSVC /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -563,15 +563,13 @@ impl f16 { /// # } /// ``` /// - /// This operation corresponds to IEEE-754 `nextDown`. - /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[doc(alias = "nextDown")] #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -662,8 +660,7 @@ 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. 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. + /// This also matches the behavior of libm’s fmax. /// /// ``` /// #![feature(f16)] @@ -688,8 +685,7 @@ 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. 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. + /// This also matches the behavior of libm’s fmin. /// /// ``` /// #![feature(f16)] @@ -799,6 +795,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] + /// #![feature(num_midpoint)] /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 /// /// assert_eq!(1f16.midpoint(4.0), 2.5); @@ -807,8 +804,8 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "f16", issue = "116909")] - #[rustc_const_unstable(feature = "f16", issue = "116909")] - pub const fn midpoint(self, other: f16) -> f16 { + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f16) -> f16 { const LO: f16 = f16::MIN_POSITIVE * 2.; const HI: f16 = f16::MAX / 2.; diff --git a/core/src/num/f32.rs b/core/src/num/f32.rs index 4d42997369ffb..47dfce7530fb7 100644 --- a/core/src/num/f32.rs +++ b/core/src/num/f32.rs @@ -726,6 +726,7 @@ impl f32 { /// is finite `x == x.next_up().next_down()` also holds. /// /// ```rust + /// #![feature(float_next_up_down)] /// // f32::EPSILON is the difference between 1.0 and the next number up. /// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON); /// // But not for most numbers. @@ -733,16 +734,12 @@ impl f32 { /// assert_eq!(16777216f32.next_up(), 16777218.0); /// ``` /// - /// This operation corresponds to IEEE-754 `nextUp`. - /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[doc(alias = "nextUp")] - #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -777,6 +774,7 @@ impl f32 { /// is finite `x == x.next_down().next_up()` also holds. /// /// ```rust + /// #![feature(float_next_up_down)] /// let x = 1.0f32; /// // Clamp value into range [0, 1). /// let clamped = x.clamp(0.0, 1.0f32.next_down()); @@ -784,16 +782,12 @@ impl f32 { /// assert_eq!(clamped.next_up(), 1.0); /// ``` /// - /// This operation corresponds to IEEE-754 `nextDown`. - /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[doc(alias = "nextDown")] - #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -824,7 +818,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn recip(self) -> f32 { 1.0 / self @@ -842,7 +836,7 @@ impl f32 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn to_degrees(self) -> f32 { // Use a constant for better precision. @@ -862,7 +856,7 @@ impl f32 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn to_radians(self) -> f32 { const RADS_PER_DEG: f32 = consts::PI / 180.0; @@ -874,8 +868,7 @@ 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. 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. + /// This also matches the behavior of libm’s fmax. /// /// ``` /// let x = 1.0f32; @@ -885,7 +878,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn max(self, other: f32) -> f32 { intrinsics::maxnumf32(self, other) @@ -896,8 +889,7 @@ 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. 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. + /// This also matches the behavior of libm’s fmin. /// /// ``` /// let x = 1.0f32; @@ -907,7 +899,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn min(self, other: f32) -> f32 { intrinsics::minnumf32(self, other) @@ -992,27 +984,27 @@ impl f32 { /// # Examples /// /// ``` + /// #![feature(num_midpoint)] /// assert_eq!(1f32.midpoint(4.0), 2.5); /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` #[inline] - #[stable(feature = "num_midpoint", since = "1.85.0")] - #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] - pub const fn midpoint(self, other: f32) -> f32 { + #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f32) -> f32 { cfg_if! { - // Allow faster implementation that have known good 64-bit float - // implementations. Falling back to the branchy code on targets that don't - // have 64-bit hardware floats or buggy implementations. - // https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114 if #[cfg(any( target_arch = "x86_64", target_arch = "aarch64", - all(any(target_arch = "riscv32", target_arch = "riscv64"), target_feature = "d"), - all(target_arch = "arm", target_feature = "vfp2"), + all(any(target_arch="riscv32", target_arch= "riscv64"), target_feature="d"), + all(target_arch = "arm", target_feature="vfp2"), target_arch = "wasm32", target_arch = "wasm64", ))] { - ((self as f64 + other as f64) / 2.0) as f32 + // whitelist the faster implementation to targets that have known good 64-bit float + // implementations. Falling back to the branchy code on targets that don't have + // 64-bit hardware floats or buggy implementations. + // see: https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114 + ((f64::from(self) + f64::from(other)) / 2.0) as f32 } else { const LO: f32 = f32::MIN_POSITIVE * 2.; const HI: f32 = f32::MAX / 2.; @@ -1404,7 +1396,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn clamp(mut self, min: f32, max: f32) -> f32 { const_assert!( @@ -1441,7 +1433,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn abs(self) -> f32 { // SAFETY: this is actually a safe intrinsic @@ -1466,7 +1458,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn signum(self) -> f32 { if self.is_nan() { Self::NAN } else { 1.0_f32.copysign(self) } @@ -1501,7 +1493,7 @@ impl f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[inline] #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] pub const fn copysign(self, sign: f32) -> f32 { // SAFETY: this is actually a safe intrinsic unsafe { intrinsics::copysignf32(self, sign) } diff --git a/core/src/num/f64.rs b/core/src/num/f64.rs index 907971d303ffc..c89023c1ae490 100644 --- a/core/src/num/f64.rs +++ b/core/src/num/f64.rs @@ -743,6 +743,7 @@ impl f64 { /// is finite `x == x.next_up().next_down()` also holds. /// /// ```rust + /// #![feature(float_next_up_down)] /// // f64::EPSILON is the difference between 1.0 and the next number up. /// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON); /// // But not for most numbers. @@ -750,16 +751,12 @@ impl f64 { /// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0); /// ``` /// - /// This operation corresponds to IEEE-754 `nextUp`. - /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[doc(alias = "nextUp")] - #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -794,6 +791,7 @@ impl f64 { /// is finite `x == x.next_down().next_up()` also holds. /// /// ```rust + /// #![feature(float_next_up_down)] /// let x = 1.0f64; /// // Clamp value into range [0, 1). /// let clamped = x.clamp(0.0, 1.0f64.next_down()); @@ -801,16 +799,12 @@ impl f64 { /// assert_eq!(clamped.next_up(), 1.0); /// ``` /// - /// This operation corresponds to IEEE-754 `nextDown`. - /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[doc(alias = "nextDown")] - #[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -841,7 +835,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn recip(self) -> f64 { 1.0 / self @@ -859,7 +853,7 @@ impl f64 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn to_degrees(self) -> f64 { // The division here is correctly rounded with respect to the true @@ -880,7 +874,7 @@ impl f64 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn to_radians(self) -> f64 { const RADS_PER_DEG: f64 = consts::PI / 180.0; @@ -892,8 +886,7 @@ 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. 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. + /// This also matches the behavior of libm’s fmax. /// /// ``` /// let x = 1.0_f64; @@ -903,7 +896,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn max(self, other: f64) -> f64 { intrinsics::maxnumf64(self, other) @@ -914,8 +907,7 @@ 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. 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. + /// This also matches the behavior of libm’s fmin. /// /// ``` /// let x = 1.0_f64; @@ -925,7 +917,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn min(self, other: f64) -> f64 { intrinsics::minnumf64(self, other) @@ -1010,13 +1002,13 @@ impl f64 { /// # Examples /// /// ``` + /// #![feature(num_midpoint)] /// assert_eq!(1f64.midpoint(4.0), 2.5); /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` #[inline] - #[stable(feature = "num_midpoint", since = "1.85.0")] - #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] - pub const fn midpoint(self, other: f64) -> f64 { + #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f64) -> f64 { const LO: f64 = f64::MIN_POSITIVE * 2.; const HI: f64 = f64::MAX / 2.; @@ -1404,7 +1396,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn clamp(mut self, min: f64, max: f64) -> f64 { const_assert!( @@ -1441,7 +1433,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn abs(self) -> f64 { // SAFETY: this is actually a safe intrinsic @@ -1466,7 +1458,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn signum(self) -> f64 { if self.is_nan() { Self::NAN } else { 1.0_f64.copysign(self) } @@ -1500,7 +1492,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] + #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn copysign(self, sign: f64) -> f64 { // SAFETY: this is actually a safe intrinsic diff --git a/core/src/num/flt2dec/mod.rs b/core/src/num/flt2dec/mod.rs index 7601e3e2c58a2..d6413fadc3381 100644 --- a/core/src/num/flt2dec/mod.rs +++ b/core/src/num/flt2dec/mod.rs @@ -210,10 +210,10 @@ fn digits_to_dec_str<'a>( if frac_digits > buf.len() && frac_digits - buf.len() > minus_exp { parts[3] = MaybeUninit::new(Part::Zero((frac_digits - buf.len()) - minus_exp)); // SAFETY: we just initialized the elements `..4`. - unsafe { parts[..4].assume_init_ref() } + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } } else { // SAFETY: we just initialized the elements `..3`. - unsafe { parts[..3].assume_init_ref() } + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } } } else { let exp = exp as usize; @@ -225,10 +225,10 @@ fn digits_to_dec_str<'a>( if frac_digits > buf.len() - exp { parts[3] = MaybeUninit::new(Part::Zero(frac_digits - (buf.len() - exp))); // SAFETY: we just initialized the elements `..4`. - unsafe { parts[..4].assume_init_ref() } + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } } else { // SAFETY: we just initialized the elements `..3`. - unsafe { parts[..3].assume_init_ref() } + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } } } else { // the decimal point is after rendered digits: [1234][____0000] or [1234][__][.][__]. @@ -238,10 +238,10 @@ fn digits_to_dec_str<'a>( parts[2] = MaybeUninit::new(Part::Copy(b".")); parts[3] = MaybeUninit::new(Part::Zero(frac_digits)); // SAFETY: we just initialized the elements `..4`. - unsafe { parts[..4].assume_init_ref() } + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } } else { // SAFETY: we just initialized the elements `..2`. - unsafe { parts[..2].assume_init_ref() } + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) } } } } @@ -292,7 +292,7 @@ fn digits_to_exp_str<'a>( parts[n + 1] = MaybeUninit::new(Part::Num(exp as u16)); } // SAFETY: we just initialized the elements `..n + 2`. - unsafe { parts[..n + 2].assume_init_ref() } + unsafe { MaybeUninit::slice_assume_init_ref(&parts[..n + 2]) } } /// Sign formatting options. @@ -366,12 +366,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } } FullDecoded::Zero => { if frac_digits > 0 { @@ -381,14 +381,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { parts[..2].assume_init_ref() }, + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { parts[..1].assume_init_ref() }, + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, } } } @@ -442,12 +442,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } } FullDecoded::Zero => { parts[0] = if dec_bounds.0 <= 0 && 0 < dec_bounds.1 { @@ -456,7 +456,7 @@ where MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })) }; // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } } FullDecoded::Finite(ref decoded) => { let (buf, exp) = format_shortest(decoded, buf); @@ -533,12 +533,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } } FullDecoded::Zero => { if ndigits > 1 { @@ -549,14 +549,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..3`. - parts: unsafe { parts[..3].assume_init_ref() }, + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) }, } } else { parts[0] = MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { parts[..1].assume_init_ref() }, + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, } } } @@ -607,12 +607,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } + Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } } FullDecoded::Zero => { if frac_digits > 0 { @@ -622,14 +622,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { parts[..2].assume_init_ref() }, + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { parts[..1].assume_init_ref() }, + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, } } } @@ -654,14 +654,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { parts[..2].assume_init_ref() }, + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { parts[..1].assume_init_ref() }, + parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, } } } else { diff --git a/core/src/num/flt2dec/strategy/dragon.rs b/core/src/num/flt2dec/strategy/dragon.rs index dd73e4b4846d5..e801f07b3bc0e 100644 --- a/core/src/num/flt2dec/strategy/dragon.rs +++ b/core/src/num/flt2dec/strategy/dragon.rs @@ -247,7 +247,7 @@ pub fn format_shortest<'a>( // it seems that this condition is very hard to satisfy (possibly impossible), // but we are just being safe and consistent here. // SAFETY: we initialized that memory above. - if let Some(c) = round_up(unsafe { buf[..i].assume_init_mut() }) { + if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }) { buf[i] = MaybeUninit::new(c); i += 1; k += 1; @@ -255,7 +255,7 @@ pub fn format_shortest<'a>( } // SAFETY: we initialized that memory above. - (unsafe { buf[..i].assume_init_ref() }, k) + (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..i]) }, k) } /// The exact and fixed mode implementation for Dragon. @@ -333,7 +333,7 @@ pub fn format_exact<'a>( *c = MaybeUninit::new(b'0'); } // SAFETY: we initialized that memory above. - return (unsafe { buf[..len].assume_init_ref() }, k); + return (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k); } let mut d = 0; @@ -372,7 +372,7 @@ pub fn format_exact<'a>( // if rounding up changes the length, the exponent should also change. // but we've been requested a fixed number of digits, so do not alter the buffer... // SAFETY: we initialized that memory above. - if let Some(c) = round_up(unsafe { buf[..len].assume_init_mut() }) { + if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) { // ...unless we've been requested the fixed precision instead. // we also need to check that, if the original buffer was empty, // the additional digit can only be added when `k == limit` (edge case). @@ -385,5 +385,5 @@ pub fn format_exact<'a>( } // SAFETY: we initialized that memory above. - (unsafe { buf[..len].assume_init_ref() }, k) + (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k) } diff --git a/core/src/num/flt2dec/strategy/grisu.rs b/core/src/num/flt2dec/strategy/grisu.rs index 2816de4c63339..bdf544a4133bb 100644 --- a/core/src/num/flt2dec/strategy/grisu.rs +++ b/core/src/num/flt2dec/strategy/grisu.rs @@ -275,7 +275,7 @@ pub fn format_shortest_opt<'a>( let ten_kappa = (ten_kappa as u64) << e; // scale 10^kappa back to the shared exponent return round_and_weed( // SAFETY: we initialized that memory above. - unsafe { buf[..i].assume_init_mut() }, + unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, exp, plus1rem, delta1, @@ -324,7 +324,7 @@ pub fn format_shortest_opt<'a>( let ten_kappa = 1 << e; // implicit divisor return round_and_weed( // SAFETY: we initialized that memory above. - unsafe { buf[..i].assume_init_mut() }, + unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, exp, r, threshold, @@ -713,7 +713,7 @@ pub fn format_exact_opt<'a>( // `10^kappa` did not overflow after all, the second check is fine. if ten_kappa - remainder > remainder && ten_kappa - 2 * remainder >= 2 * ulp { // SAFETY: our caller initialized that memory. - return Some((unsafe { buf[..len].assume_init_ref() }, exp)); + return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); } // :<------- remainder ------>| : @@ -736,7 +736,7 @@ pub fn format_exact_opt<'a>( if remainder > ulp && ten_kappa - (remainder - ulp) <= remainder - ulp { if let Some(c) = // SAFETY: our caller must have initialized that memory. - round_up(unsafe { buf[..len].assume_init_mut() }) + round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) { // only add an additional digit when we've been requested the fixed precision. // we also need to check that, if the original buffer was empty, @@ -748,7 +748,7 @@ pub fn format_exact_opt<'a>( } } // SAFETY: we and our caller initialized that memory. - return Some((unsafe { buf[..len].assume_init_ref() }, exp)); + return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); } // otherwise we are doomed (i.e., some values between `v - 1 ulp` and `v + 1 ulp` are diff --git a/core/src/num/int_log10.rs b/core/src/num/int_log10.rs index 28a3f5d880ad7..0ce31b40a3845 100644 --- a/core/src/num/int_log10.rs +++ b/core/src/num/int_log10.rs @@ -3,7 +3,7 @@ // 0 < val <= u8::MAX #[inline] -pub(super) const fn u8(val: u8) -> u32 { +pub const fn u8(val: u8) -> u32 { let val = val as u32; // For better performance, avoid branches by assembling the solution @@ -45,13 +45,13 @@ const fn less_than_5(val: u32) -> u32 { // 0 < val <= u16::MAX #[inline] -pub(super) const fn u16(val: u16) -> u32 { +pub const fn u16(val: u16) -> u32 { less_than_5(val as u32) } // 0 < val <= u32::MAX #[inline] -pub(super) const fn u32(mut val: u32) -> u32 { +pub const fn u32(mut val: u32) -> u32 { let mut log = 0; if val >= 100_000 { val /= 100_000; @@ -62,7 +62,7 @@ pub(super) const fn u32(mut val: u32) -> u32 { // 0 < val <= u64::MAX #[inline] -pub(super) const fn u64(mut val: u64) -> u32 { +pub const fn u64(mut val: u64) -> u32 { let mut log = 0; if val >= 10_000_000_000 { val /= 10_000_000_000; @@ -77,7 +77,7 @@ pub(super) const fn u64(mut val: u64) -> u32 { // 0 < val <= u128::MAX #[inline] -pub(super) const fn u128(mut val: u128) -> u32 { +pub const fn u128(mut val: u128) -> u32 { let mut log = 0; if val >= 100_000_000_000_000_000_000_000_000_000_000 { val /= 100_000_000_000_000_000_000_000_000_000_000; @@ -93,49 +93,49 @@ pub(super) const fn u128(mut val: u128) -> u32 { #[cfg(target_pointer_width = "16")] #[inline] -pub(super) const fn usize(val: usize) -> u32 { +pub const fn usize(val: usize) -> u32 { u16(val as _) } #[cfg(target_pointer_width = "32")] #[inline] -pub(super) const fn usize(val: usize) -> u32 { +pub const fn usize(val: usize) -> u32 { u32(val as _) } #[cfg(target_pointer_width = "64")] #[inline] -pub(super) const fn usize(val: usize) -> u32 { +pub const fn usize(val: usize) -> u32 { u64(val as _) } // 0 < val <= i8::MAX #[inline] -pub(super) const fn i8(val: i8) -> u32 { +pub const fn i8(val: i8) -> u32 { u8(val as u8) } // 0 < val <= i16::MAX #[inline] -pub(super) const fn i16(val: i16) -> u32 { +pub const fn i16(val: i16) -> u32 { u16(val as u16) } // 0 < val <= i32::MAX #[inline] -pub(super) const fn i32(val: i32) -> u32 { +pub const fn i32(val: i32) -> u32 { u32(val as u32) } // 0 < val <= i64::MAX #[inline] -pub(super) const fn i64(val: i64) -> u32 { +pub const fn i64(val: i64) -> u32 { u64(val as u64) } // 0 < val <= i128::MAX #[inline] -pub(super) const fn i128(val: i128) -> u32 { +pub const fn i128(val: i128) -> u32 { u128(val as u128) } @@ -143,6 +143,6 @@ pub(super) const fn i128(val: i128) -> u32 { /// on every single primitive type. #[cold] #[track_caller] -pub(super) const fn panic_for_nonpositive_argument() -> ! { +pub const fn panic_for_nonpositive_argument() -> ! { panic!("argument of integer logarithm must be positive") } diff --git a/core/src/num/int_macros.rs b/core/src/num/int_macros.rs index 96a290ad5a09d..64dcb4c91e628 100644 --- a/core/src/num/int_macros.rs +++ b/core/src/num/int_macros.rs @@ -1152,6 +1152,7 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_neg", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_neg(self) -> Self { @@ -1216,6 +1217,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1280,6 +1282,7 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_shifts", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { @@ -1337,6 +1340,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1401,6 +1405,7 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_shifts", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { @@ -1608,8 +1613,8 @@ macro_rules! int_impl { /// ``` #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".checked_isqrt(), Some(3));")] /// ``` - #[stable(feature = "isqrt", since = "1.84.0")] - #[rustc_const_stable(feature = "isqrt", since = "1.84.0")] + #[stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1823,7 +1828,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -1981,7 +1986,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2009,7 +2014,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2037,7 +2042,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2064,7 +2069,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2129,6 +2134,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] pub const fn wrapping_shl(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -2158,6 +2164,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] pub const fn wrapping_shr(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -2512,114 +2519,6 @@ macro_rules! int_impl { (a as Self, b) } - /// Calculates the complete product `self * rhs` without the possibility to overflow. - /// - /// This returns the low-order (wrapping) bits and the high-order (overflow) bits - /// of the result as two separate values, in that order. - /// - /// If you also need to add a carry to the wide result, then you want - /// [`Self::carrying_mul`] instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `i32` is used here. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!(5i32.widening_mul(-2), (4294967286, -1)); - /// assert_eq!(1_000_000_000i32.widening_mul(-10), (2884901888, -3)); - /// ``` - #[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 widening_mul(self, rhs: Self) -> ($UnsignedT, Self) { - Self::carrying_mul_add(self, rhs, 0, 0) - } - - /// Calculates the "full multiplication" `self * rhs + carry` - /// without the possibility to overflow. - /// - /// This returns the low-order (wrapping) bits and the high-order (overflow) bits - /// of the result as two separate values, in that order. - /// - /// Performs "long multiplication" which takes in an extra amount to add, and may return an - /// additional amount of overflow. This allows for chaining together multiple - /// multiplications to create "big integers" which represent larger values. - /// - /// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `i32` is used here. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!(5i32.carrying_mul(-2, 0), (4294967286, -1)); - /// assert_eq!(5i32.carrying_mul(-2, 10), (0, 0)); - /// assert_eq!(1_000_000_000i32.carrying_mul(-10, 0), (2884901888, -3)); - /// assert_eq!(1_000_000_000i32.carrying_mul(-10, 10), (2884901898, -3)); - #[doc = concat!("assert_eq!(", - stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", - "(", stringify!($SelfT), "::MAX.unsigned_abs() + 1, ", stringify!($SelfT), "::MAX / 2));" - )] - /// ``` - #[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_mul(self, rhs: Self, carry: Self) -> ($UnsignedT, Self) { - Self::carrying_mul_add(self, rhs, carry, 0) - } - - /// Calculates the "full multiplication" `self * rhs + carry1 + carry2` - /// without the possibility to overflow. - /// - /// This returns the low-order (wrapping) bits and the high-order (overflow) bits - /// of the result as two separate values, in that order. - /// - /// Performs "long multiplication" which takes in an extra amount to add, and may return an - /// additional amount of overflow. This allows for chaining together multiple - /// multiplications to create "big integers" which represent larger values. - /// - /// If you don't need either `carry`, then you can use [`Self::widening_mul`] instead, - /// and if you only need one `carry`, then you can use [`Self::carrying_mul`] instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `i32` is used here. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!(5i32.carrying_mul_add(-2, 0, 0), (4294967286, -1)); - /// assert_eq!(5i32.carrying_mul_add(-2, 10, 10), (10, 0)); - /// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 0, 0), (2884901888, -3)); - /// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 10, 10), (2884901908, -3)); - #[doc = concat!("assert_eq!(", - stringify!($SelfT), "::MAX.carrying_mul_add(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", - "(", stringify!($UnsignedT), "::MAX, ", stringify!($SelfT), "::MAX / 2));" - )] - /// ``` - #[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_mul_add(self, rhs: Self, carry: Self, add: Self) -> ($UnsignedT, Self) { - intrinsics::carrying_mul_add(self, rhs, carry, add) - } - /// Calculates the divisor when `self` is divided by `rhs`. /// /// Returns a tuple of the divisor along with a boolean indicating whether an arithmetic overflow would @@ -2627,7 +2526,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2658,7 +2557,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2689,7 +2588,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2720,7 +2619,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2961,8 +2860,8 @@ macro_rules! int_impl { /// ``` #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".isqrt(), 3);")] /// ``` - #[stable(feature = "isqrt", since = "1.84.0")] - #[rustc_const_stable(feature = "isqrt", since = "1.84.0")] + #[stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2988,7 +2887,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3027,7 +2926,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` and + /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` and /// `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3076,7 +2975,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3120,7 +3019,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples diff --git a/core/src/num/int_sqrt.rs b/core/src/num/int_sqrt.rs index c7a322c08c139..601e81f69930f 100644 --- a/core/src/num/int_sqrt.rs +++ b/core/src/num/int_sqrt.rs @@ -37,7 +37,7 @@ const U8_ISQRT_WITH_REMAINDER: [(u8, u8); 256] = { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] -pub(super) const fn u8(n: u8) -> u8 { +pub const fn u8(n: u8) -> u8 { U8_ISQRT_WITH_REMAINDER[n as usize].0 } @@ -58,7 +58,7 @@ macro_rules! signed_fn { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub(super) const unsafe fn $SignedT(n: $SignedT) -> $SignedT { + pub const unsafe fn $SignedT(n: $SignedT) -> $SignedT { debug_assert!(n >= 0, "Negative input inside `isqrt`."); $UnsignedT(n as $UnsignedT) as $SignedT } @@ -83,7 +83,7 @@ macro_rules! unsigned_fn { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub(super) const fn $UnsignedT(mut n: $UnsignedT) -> $UnsignedT { + pub const fn $UnsignedT(mut n: $UnsignedT) -> $UnsignedT { if n <= <$HalfBitsT>::MAX as $UnsignedT { $HalfBitsT(n as $HalfBitsT) as $UnsignedT } else { @@ -311,6 +311,6 @@ unsigned_fn!(u128, u64, u128_stages); /// on every single primitive type. #[cold] #[track_caller] -pub(super) const fn panic_for_negative_argument() -> ! { +pub const fn panic_for_negative_argument() -> ! { panic!("argument of integer square root cannot be negative") } diff --git a/core/src/num/mod.rs b/core/src/num/mod.rs index 55f4ccd958e77..9d9897b9cf05e 100644 --- a/core/src/num/mod.rs +++ b/core/src/num/mod.rs @@ -51,10 +51,6 @@ mod overflow_panic; mod saturating; mod wrapping; -/// 100% perma-unstable -#[doc(hidden)] -pub mod niche_types; - #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(no_fp_fmt_parse))] pub use dec2flt::ParseFloatError; @@ -81,31 +77,6 @@ pub use saturating::Saturating; #[stable(feature = "rust1", since = "1.0.0")] pub use wrapping::Wrapping; -macro_rules! u8_xe_bytes_doc { - () => { - " - -**Note**: This function is meaningless on `u8`. Byte order does not exist as a -concept for byte-sized integers. This function is only provided in symmetry -with larger integer types. - -" - }; -} - -macro_rules! i8_xe_bytes_doc { - () => { - " - -**Note**: This function is meaningless on `i8`. Byte order does not exist as a -concept for byte-sized integers. This function is only provided in symmetry -with larger integer types. You can cast from and to `u8` using `as i8` and `as -u8`. - -" - }; -} - macro_rules! usize_isize_to_xe_bytes_doc { () => { " @@ -132,18 +103,18 @@ macro_rules! midpoint_impl { ($SelfT:ty, unsigned) => { /// Calculates the middle point of `self` and `rhs`. /// - /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a - /// sufficiently-large unsigned integral type. This implies that the result is - /// always rounded towards zero and that no overflow will ever occur. + /// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a + /// sufficiently-large signed integral type. This implies that the result is + /// always rounded towards negative infinity and that no overflow will ever occur. /// /// # Examples /// /// ``` + /// #![feature(num_midpoint)] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")] /// ``` - #[stable(feature = "num_midpoint", since = "1.85.0")] - #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] + #[unstable(feature = "num_midpoint", issue = "110840")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -163,14 +134,14 @@ macro_rules! midpoint_impl { /// # Examples /// /// ``` - /// #![feature(num_midpoint_signed)] + /// #![feature(num_midpoint)] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").midpoint(2), 0);")] #[doc = concat!("assert_eq!((-7", stringify!($SelfT), ").midpoint(0), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-7), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(7), 3);")] /// ``` - #[unstable(feature = "num_midpoint_signed", issue = "110840")] + #[unstable(feature = "num_midpoint", issue = "110840")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -186,18 +157,18 @@ macro_rules! midpoint_impl { ($SelfT:ty, $WideT:ty, unsigned) => { /// Calculates the middle point of `self` and `rhs`. /// - /// `midpoint(a, b)` is `(a + b) / 2` as if it were performed in a - /// sufficiently-large unsigned integral type. This implies that the result is - /// always rounded towards zero and that no overflow will ever occur. + /// `midpoint(a, b)` is `(a + b) >> 1` as if it were performed in a + /// sufficiently-large signed integral type. This implies that the result is + /// always rounded towards negative infinity and that no overflow will ever occur. /// /// # Examples /// /// ``` + /// #![feature(num_midpoint)] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")] /// ``` - #[stable(feature = "num_midpoint", since = "1.85.0")] - #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] + #[unstable(feature = "num_midpoint", issue = "110840")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -215,14 +186,14 @@ macro_rules! midpoint_impl { /// # Examples /// /// ``` - /// #![feature(num_midpoint_signed)] + /// #![feature(num_midpoint)] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").midpoint(2), 0);")] #[doc = concat!("assert_eq!((-7", stringify!($SelfT), ").midpoint(0), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(-7), -3);")] #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(7), 3);")] /// ``` - #[unstable(feature = "num_midpoint_signed", issue = "110840")] + #[unstable(feature = "num_midpoint", issue = "110840")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -232,6 +203,134 @@ macro_rules! midpoint_impl { }; } +macro_rules! widening_impl { + ($SelfT:ty, $WideT:ty, $BITS:literal, unsigned) => { + /// Calculates the complete product `self * rhs` without the possibility to overflow. + /// + /// This returns the low-order (wrapping) bits and the high-order (overflow) bits + /// of the result as two separate values, in that order. + /// + /// If you also need to add a carry to the wide result, then you want + /// [`Self::carrying_mul`] instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `u32` is used here. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!(5u32.widening_mul(2), (10, 0)); + /// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2)); + /// ``` + #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn widening_mul(self, rhs: Self) -> (Self, Self) { + // note: longer-term this should be done via an intrinsic, + // but for now we can deal without an impl for u128/i128 + // SAFETY: overflow will be contained within the wider types + let wide = unsafe { (self as $WideT).unchecked_mul(rhs as $WideT) }; + (wide as $SelfT, (wide >> $BITS) as $SelfT) + } + + /// Calculates the "full multiplication" `self * rhs + carry` + /// without the possibility to overflow. + /// + /// This returns the low-order (wrapping) bits and the high-order (overflow) bits + /// of the result as two separate values, in that order. + /// + /// Performs "long multiplication" which takes in an extra amount to add, and may return an + /// additional amount of overflow. This allows for chaining together multiple + /// multiplications to create "big integers" which represent larger values. + /// + /// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// Please note that this example is shared between integer types. + /// Which explains why `u32` is used here. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!(5u32.carrying_mul(2, 0), (10, 0)); + /// assert_eq!(5u32.carrying_mul(2, 10), (20, 0)); + /// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2)); + /// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2)); + #[doc = concat!("assert_eq!(", + stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", + "(0, ", stringify!($SelfT), "::MAX));" + )] + /// ``` + /// + /// This is the core operation needed for scalar multiplication when + /// implementing it for wider-than-native types. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// fn scalar_mul_eq(little_endian_digits: &mut Vec, multiplicand: u16) { + /// let mut carry = 0; + /// for d in little_endian_digits.iter_mut() { + /// (*d, carry) = d.carrying_mul(multiplicand, carry); + /// } + /// if carry != 0 { + /// little_endian_digits.push(carry); + /// } + /// } + /// + /// let mut v = vec![10, 20]; + /// scalar_mul_eq(&mut v, 3); + /// assert_eq!(v, [30, 60]); + /// + /// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D); + /// let mut v = vec![0x4321, 0x8765]; + /// scalar_mul_eq(&mut v, 0xFEED); + /// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]); + /// ``` + /// + /// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul), + /// except that it gives the value of the overflow instead of just whether one happened: + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// let r = u8::carrying_mul(7, 13, 0); + /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13)); + /// let r = u8::carrying_mul(13, 42, 0); + /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42)); + /// ``` + /// + /// The value of the first field in the returned tuple matches what you'd get + /// by combining the [`wrapping_mul`](Self::wrapping_mul) and + /// [`wrapping_add`](Self::wrapping_add) methods: + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// assert_eq!( + /// 789_u16.carrying_mul(456, 123).0, + /// 789_u16.wrapping_mul(456).wrapping_add(123), + /// ); + /// ``` + #[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_mul(self, rhs: Self, carry: Self) -> (Self, Self) { + // note: longer-term this should be done via an intrinsic, + // but for now we can deal without an impl for u128/i128 + // SAFETY: overflow will be contained within the wider types + let wide = unsafe { + (self as $WideT).unchecked_mul(rhs as $WideT).unchecked_add(carry as $WideT) + }; + (wide as $SelfT, (wide >> $BITS) as $SelfT) + } + }; +} + impl i8 { int_impl! { Self = i8, @@ -249,8 +348,8 @@ impl i8 { reversed = "0x48", le_bytes = "[0x12]", be_bytes = "[0x12]", - to_xe_bytes_doc = i8_xe_bytes_doc!(), - from_xe_bytes_doc = i8_xe_bytes_doc!(), + to_xe_bytes_doc = "", + from_xe_bytes_doc = "", bound_condition = "", } midpoint_impl! { i8, i16, signed } @@ -448,10 +547,11 @@ impl u8 { reversed = "0x48", le_bytes = "[0x12]", be_bytes = "[0x12]", - to_xe_bytes_doc = u8_xe_bytes_doc!(), - from_xe_bytes_doc = u8_xe_bytes_doc!(), + to_xe_bytes_doc = "", + from_xe_bytes_doc = "", bound_condition = "", } + widening_impl! { u8, u16, 8, unsigned } midpoint_impl! { u8, u16, unsigned } /// Checks if the value is within the ASCII range. @@ -577,7 +677,7 @@ impl u8 { /// /// [`to_ascii_uppercase`]: Self::to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); @@ -603,7 +703,7 @@ impl u8 { /// /// [`to_ascii_lowercase`]: Self::to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); @@ -1067,6 +1167,7 @@ impl u16 { from_xe_bytes_doc = "", bound_condition = "", } + widening_impl! { u16, u32, 16, unsigned } midpoint_impl! { u16, u32, unsigned } /// Checks if the value is a Unicode surrogate code point, which are disallowed values for [`char`]. @@ -1114,6 +1215,7 @@ impl u32 { from_xe_bytes_doc = "", bound_condition = "", } + widening_impl! { u32, u64, 32, unsigned } midpoint_impl! { u32, u64, unsigned } } @@ -1137,6 +1239,7 @@ impl u64 { from_xe_bytes_doc = "", bound_condition = "", } + widening_impl! { u64, u128, 64, unsigned } midpoint_impl! { u64, u128, unsigned } } @@ -1186,6 +1289,7 @@ impl usize { from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(), bound_condition = " on 16-bit targets", } + widening_impl! { usize, u32, 16, unsigned } midpoint_impl! { usize, u32, unsigned } } @@ -1210,6 +1314,7 @@ impl usize { from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(), bound_condition = " on 32-bit targets", } + widening_impl! { usize, u64, 32, unsigned } midpoint_impl! { usize, u64, unsigned } } @@ -1234,6 +1339,7 @@ impl usize { from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(), bound_condition = " on 64-bit targets", } + widening_impl! { usize, u128, 64, unsigned } midpoint_impl! { usize, u128, unsigned } } @@ -1322,6 +1428,20 @@ 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 { + <$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 @@ -1329,6 +1449,7 @@ pub enum FpCategory { #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_from_str", since = "1.82.0"))] pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } @@ -1337,58 +1458,18 @@ pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -const fn from_ascii_radix_panic(radix: u32) -> ! { +const fn from_str_radix_panic(radix: u32) -> ! { const_panic!( - "from_ascii_radix: radix must lie in the range `[2, 36]`", - "from_ascii_radix: radix must lie in the range `[2, 36]` - found {radix}", + "from_str_radix_int: must lie in the range `[2, 36]`", + "from_str_radix_int: must lie in the range `[2, 36]` - found {radix}", radix: u32 = radix, ) } -macro_rules! from_str_int_impl { +macro_rules! from_str_radix { ($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 { - /// Parses an integer from a string slice with digits in a given base. + /// Converts a string slice in a given base to an integer. /// /// The string is expected to be an optional #[doc = sign_dependent_expr!{ @@ -1401,7 +1482,7 @@ macro_rules! from_str_int_impl { } }] /// 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`: @@ -1427,92 +1508,11 @@ macro_rules! from_str_int_impl { #[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_ascii_radix_panic(radix); + from_str_radix_panic(radix); } if src.is_empty() { @@ -1522,6 +1522,12 @@ macro_rules! from_str_int_impl { #[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 }); @@ -1597,8 +1603,67 @@ macro_rules! from_str_int_impl { 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), + } + } + })*} } -from_str_int_impl! { signed isize i8 i16 i32 i64 i128 } -from_str_int_impl! { unsigned usize u8 u16 u32 u64 u128 } +#[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 } diff --git a/core/src/num/niche_types.rs b/core/src/num/niche_types.rs deleted file mode 100644 index 096713c318f8d..0000000000000 --- a/core/src/num/niche_types.rs +++ /dev/null @@ -1,168 +0,0 @@ -#![unstable( - feature = "temporary_niche_types", - issue = "none", - reason = "for core, alloc, and std internals until pattern types are further along" -)] - -use crate::cmp::Ordering; -use crate::fmt; -use crate::hash::{Hash, Hasher}; -use crate::marker::StructuralPartialEq; - -macro_rules! define_valid_range_type { - ($( - $(#[$m:meta])* - $vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal); - )+) => {$( - #[derive(Clone, Copy, Eq)] - #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start($low)] - #[rustc_layout_scalar_valid_range_end($high)] - $(#[$m])* - $vis struct $name($int); - - const _: () = { - // With the `valid_range` attributes, it's always specified as unsigned - assert!(<$uint>::MIN == 0); - let ulow: $uint = $low; - let uhigh: $uint = $high; - assert!(ulow <= uhigh); - - assert!(size_of::<$int>() == size_of::<$uint>()); - }; - - impl $name { - /// Constructs an instance of this type from the underlying integer - /// primitive without checking whether its zero. - /// - /// # Safety - /// Immediate language UB if `val == 0`, as it violates the validity - /// invariant of this type. - #[inline] - pub const unsafe fn new_unchecked(val: $int) -> Self { - // SAFETY: Caller promised that `val` is non-zero. - unsafe { $name(val) } - } - - #[inline] - pub const fn as_inner(self) -> $int { - // SAFETY: This is a transparent wrapper, so unwrapping it is sound - // (Not using `.0` due to MCP#807.) - unsafe { crate::mem::transmute(self) } - } - } - - // This is required to allow matching a constant. We don't get it from a derive - // because the derived `PartialEq` would do a field projection, which is banned - // by . - impl StructuralPartialEq for $name {} - - impl PartialEq for $name { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.as_inner() == other.as_inner() - } - } - - impl Ord for $name { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - Ord::cmp(&self.as_inner(), &other.as_inner()) - } - } - - impl PartialOrd for $name { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(Ord::cmp(self, other)) - } - } - - impl Hash for $name { - // Required method - fn hash(&self, state: &mut H) { - Hash::hash(&self.as_inner(), state); - } - } - - impl fmt::Debug for $name { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - <$int as fmt::Debug>::fmt(&self.as_inner(), f) - } - } - )+}; -} - -define_valid_range_type! { - pub struct Nanoseconds(u32 as u32 in 0..=999_999_999); -} - -impl Nanoseconds { - // SAFETY: 0 is within the valid range - pub const ZERO: Self = unsafe { Nanoseconds::new_unchecked(0) }; -} - -impl Default for Nanoseconds { - #[inline] - fn default() -> Self { - Self::ZERO - } -} - -define_valid_range_type! { - pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff); - pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff); - pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff); - pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff); - pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); - - pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff); - pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff); - pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff); - pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff); - pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); -} - -#[cfg(target_pointer_width = "16")] -define_valid_range_type! { - pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff); - pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff); - pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff); -} -#[cfg(target_pointer_width = "32")] -define_valid_range_type! { - pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff); - pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff); - pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff); -} -#[cfg(target_pointer_width = "64")] -define_valid_range_type! { - pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff); - pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff); - pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff); -} - -define_valid_range_type! { - pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe); - pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe); - - pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe); - pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe); -} - -pub trait NotAllOnesHelper { - type Type; -} -pub type NotAllOnes = ::Type; -impl NotAllOnesHelper for u32 { - type Type = U32NotAllOnes; -} -impl NotAllOnesHelper for i32 { - type Type = I32NotAllOnes; -} -impl NotAllOnesHelper for u64 { - type Type = U64NotAllOnes; -} -impl NotAllOnesHelper for i64 { - type Type = I64NotAllOnes; -} diff --git a/core/src/num/nonzero.rs b/core/src/num/nonzero.rs index a115acf42b126..b883a0c2ec7f9 100644 --- a/core/src/num/nonzero.rs +++ b/core/src/num/nonzero.rs @@ -43,6 +43,19 @@ macro_rules! impl_zeroable_primitive { issue = "none" )] pub trait Sealed {} + + $( + #[derive(Debug, Clone, Copy, PartialEq)] + #[repr(transparent)] + #[rustc_layout_scalar_valid_range_start(1)] + #[rustc_nonnull_optimization_guaranteed] + #[unstable( + feature = "nonzero_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" + )] + pub struct $NonZeroInner($primitive); + )+ } $( @@ -59,7 +72,7 @@ macro_rules! impl_zeroable_primitive { issue = "none" )] unsafe impl ZeroablePrimitive for $primitive { - type NonZeroInner = super::niche_types::$NonZeroInner; + type NonZeroInner = private::$NonZeroInner; } )+ }; @@ -90,26 +103,6 @@ impl_zeroable_primitive!( /// /// assert_eq!(size_of::>>(), size_of::()); /// ``` -/// -/// # Layout -/// -/// `NonZero` is guaranteed to have the same layout and bit validity as `T` -/// with the exception that the all-zero bit pattern is invalid. -/// `Option>` is guaranteed to be compatible with `T`, including in -/// FFI. -/// -/// Thanks to the [null pointer optimization], `NonZero` and -/// `Option>` are guaranteed to have the same size and alignment: -/// -/// ``` -/// # use std::mem::{size_of, align_of}; -/// use std::num::NonZero; -/// -/// assert_eq!(size_of::>(), size_of::>>()); -/// assert_eq!(align_of::>(), align_of::>>()); -/// ``` -/// -/// [null pointer optimization]: crate::option#representation #[stable(feature = "generic_nonzero", since = "1.79.0")] #[repr(transparent)] #[rustc_nonnull_optimization_guaranteed] @@ -146,9 +139,9 @@ impl_nonzero_fmt! { LowerHex #[stable(feature = "nonzero", since = "1.28.0")] UpperHex - #[stable(feature = "nonzero_fmt_exp", since = "1.84.0")] + #[stable(feature = "nonzero_fmt_exp", since = "CURRENT_RUSTC_VERSION")] LowerExp - #[stable(feature = "nonzero_fmt_exp", since = "1.84.0")] + #[stable(feature = "nonzero_fmt_exp", since = "CURRENT_RUSTC_VERSION")] UpperExp } @@ -179,7 +172,7 @@ where { #[inline] fn clone(&self) -> Self { - *self + Self(self.0) } } @@ -447,21 +440,15 @@ where #[rustc_const_stable(feature = "const_nonzero_get", since = "1.34.0")] #[inline] pub const fn get(self) -> T { + // FIXME: This can be changed to simply `self.0` once LLVM supports `!range` metadata + // for function arguments: https://github.com/llvm/llvm-project/issues/76628 + // // Rustc can set range metadata only if it loads `self` from // memory somewhere. If the value of `self` was from by-value argument // of some not-inlined function, LLVM don't have range metadata // to understand that the value cannot be zero. // - // Using the transmute `assume`s the range at runtime. - // - // Even once LLVM supports `!range` metadata for function arguments - // (see ), this can't - // be `.0` because MCP#807 bans field-projecting into `scalar_valid_range` - // types, and it arguably wouldn't want to be anyway because if this is - // MIR-inlined, there's no opportunity to put that argument metadata anywhere. - // - // The good answer here will eventually be pattern types, which will hopefully - // allow it to go back to `.0`, maybe with a cast of some sort. + // For now, using the transmute `assume`s the range at runtime. // // SAFETY: `ZeroablePrimitive` guarantees that the size and bit validity // of `.0` is such that this transmute is sound. @@ -474,7 +461,6 @@ macro_rules! nonzero_integer { #[$stability:meta] Self = $Ty:ident, Primitive = $signedness:ident $Int:ident, - SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, // Used in doc comments. @@ -628,6 +614,7 @@ macro_rules! nonzero_integer { /// ``` /// #[unstable(feature = "non_zero_count_ones", issue = "120287")] + #[rustc_const_unstable(feature = "non_zero_count_ones", issue = "120287")] #[doc(alias = "popcount")] #[doc(alias = "popcnt")] #[must_use = "this returns the result of the operation, \ @@ -906,7 +893,6 @@ macro_rules! nonzero_integer { nonzero_integer_signedness_dependent_methods! { Primitive = $signedness $Int, - SignedPrimitive = $Sint, UnsignedPrimitive = $Uint, } @@ -1130,7 +1116,6 @@ 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, @@ -1143,7 +1128,6 @@ 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, @@ -1158,7 +1142,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, @@ -1170,8 +1154,7 @@ macro_rules! nonzero_integer { #[stable(feature = "signed_nonzero", since = "1.34.0")] Self = $Ty, Primitive = signed $Int, - SignedPrimitive = $Int, - UnsignedPrimitive = $Uint, + UnsignedPrimitive = $UInt, rot = $rot, rot_op = $rot_op, rot_result = $rot_result, @@ -1190,12 +1173,8 @@ macro_rules! nonzero_integer_signedness_dependent_impls { impl Div> 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, @@ -1206,9 +1185,6 @@ macro_rules! nonzero_integer_signedness_dependent_impls { #[stable(feature = "nonzero_div_assign", since = "1.79.0")] impl DivAssign> 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] @@ -1291,7 +1267,6 @@ 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 @@ -1534,6 +1509,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Examples /// /// ``` + /// #![feature(num_midpoint)] + /// /// # use std::num::NonZero; /// # /// # fn main() { test().unwrap(); } @@ -1547,8 +1524,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Some(()) /// # } /// ``` - #[stable(feature = "num_midpoint", since = "1.85.0")] - #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] + #[unstable(feature = "num_midpoint", issue = "110840")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1611,8 +1587,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Some(()) /// # } /// ``` - #[stable(feature = "isqrt", since = "1.84.0")] - #[rustc_const_stable(feature = "isqrt", since = "1.84.0")] + #[stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1626,35 +1602,11 @@ 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 @@ -2065,37 +2017,12 @@ 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", @@ -2107,7 +2034,6 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU16, Primitive = unsigned u16, - SignedPrimitive = i16, rot = 4, rot_op = "0xa003", rot_result = "0x3a", @@ -2119,7 +2045,6 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU32, Primitive = unsigned u32, - SignedPrimitive = i32, rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", @@ -2131,7 +2056,6 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU64, Primitive = unsigned u64, - SignedPrimitive = i64, rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", @@ -2143,7 +2067,6 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU128, Primitive = unsigned u128, - SignedPrimitive = i128, rot = 16, rot_op = "0x13f40000000000000000000000004f76", rot_result = "0x4f7613f4", @@ -2156,7 +2079,6 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, - SignedPrimitive = isize, rot = 4, rot_op = "0xa003", rot_result = "0x3a", @@ -2169,7 +2091,6 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, - SignedPrimitive = isize, rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", @@ -2182,7 +2103,6 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, - SignedPrimitive = isize, rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", diff --git a/core/src/num/overflow_panic.rs b/core/src/num/overflow_panic.rs index e30573dd3f392..203037ffb43ea 100644 --- a/core/src/num/overflow_panic.rs +++ b/core/src/num/overflow_panic.rs @@ -4,48 +4,48 @@ #[cold] #[track_caller] -pub(super) const fn add() -> ! { +pub const fn add() -> ! { panic!("attempt to add with overflow") } #[cold] #[track_caller] -pub(super) const fn sub() -> ! { +pub const fn sub() -> ! { panic!("attempt to subtract with overflow") } #[cold] #[track_caller] -pub(super) const fn mul() -> ! { +pub const fn mul() -> ! { panic!("attempt to multiply with overflow") } #[cold] #[track_caller] -pub(super) const fn div() -> ! { +pub const fn div() -> ! { panic!("attempt to divide with overflow") } #[cold] #[track_caller] -pub(super) const fn rem() -> ! { +pub const fn rem() -> ! { panic!("attempt to calculate the remainder with overflow") } #[cold] #[track_caller] -pub(super) const fn neg() -> ! { +pub const fn neg() -> ! { panic!("attempt to negate with overflow") } #[cold] #[track_caller] -pub(super) const fn shr() -> ! { +pub const fn shr() -> ! { panic!("attempt to shift right with overflow") } #[cold] #[track_caller] -pub(super) const fn shl() -> ! { +pub const fn shl() -> ! { panic!("attempt to shift left with overflow") } diff --git a/core/src/num/uint_macros.rs b/core/src/num/uint_macros.rs index 29f6791ee6ad2..0383c13fa082d 100644 --- a/core/src/num/uint_macros.rs +++ b/core/src/num/uint_macros.rs @@ -4,7 +4,7 @@ macro_rules! uint_impl { ActualT = $ActualT:ident, SignedT = $SignedT:ident, - // These are all for use *only* in doc comments. + // There are all for use *only* in doc comments. // As such, they're all passed as literals -- passing them as a string // literal is fine if they need to be multiple code tokens. // In non-comments, use the associated constants rather than these. @@ -1187,50 +1187,6 @@ 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. /// @@ -1478,6 +1434,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1542,6 +1499,7 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_shifts", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { @@ -1599,6 +1557,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1663,6 +1622,7 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "unchecked_shifts", issue = "85122"))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { @@ -1917,7 +1877,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2074,7 +2034,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2103,7 +2063,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2131,7 +2091,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2161,7 +2121,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2233,6 +2193,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] pub const fn wrapping_shl(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -2265,6 +2226,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_shifts))] pub const fn wrapping_shr(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -2390,22 +2352,15 @@ 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, 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 - // 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) }) + let (a, b) = self.overflowing_add(rhs); + let (c, d) = a.overflowing_add(carry as $SelfT); + (c, b || d) } /// Calculates `self` + `rhs` with a signed `rhs`. @@ -2496,7 +2451,7 @@ macro_rules! uint_impl { // to generate optimal code for now, and LLVM doesn't have an equivalent intrinsic let (a, b) = self.overflowing_sub(rhs); let (c, d) = a.overflowing_sub(borrow as $SelfT); - (c, b | d) + (c, b || d) } /// Calculates `self` - `rhs` with a signed `rhs` @@ -2581,191 +2536,6 @@ macro_rules! uint_impl { (a as Self, b) } - /// Calculates the complete product `self * rhs` without the possibility to overflow. - /// - /// This returns the low-order (wrapping) bits and the high-order (overflow) bits - /// of the result as two separate values, in that order. - /// - /// If you also need to add a carry to the wide result, then you want - /// [`Self::carrying_mul`] instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!(5u32.widening_mul(2), (10, 0)); - /// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2)); - /// ``` - #[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 widening_mul(self, rhs: Self) -> (Self, Self) { - Self::carrying_mul_add(self, rhs, 0, 0) - } - - /// Calculates the "full multiplication" `self * rhs + carry` - /// without the possibility to overflow. - /// - /// This returns the low-order (wrapping) bits and the high-order (overflow) bits - /// of the result as two separate values, in that order. - /// - /// Performs "long multiplication" which takes in an extra amount to add, and may return an - /// additional amount of overflow. This allows for chaining together multiple - /// multiplications to create "big integers" which represent larger values. - /// - /// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!(5u32.carrying_mul(2, 0), (10, 0)); - /// assert_eq!(5u32.carrying_mul(2, 10), (20, 0)); - /// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2)); - /// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2)); - #[doc = concat!("assert_eq!(", - stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", - "(0, ", stringify!($SelfT), "::MAX));" - )] - /// ``` - /// - /// This is the core operation needed for scalar multiplication when - /// implementing it for wider-than-native types. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// fn scalar_mul_eq(little_endian_digits: &mut Vec, multiplicand: u16) { - /// let mut carry = 0; - /// for d in little_endian_digits.iter_mut() { - /// (*d, carry) = d.carrying_mul(multiplicand, carry); - /// } - /// if carry != 0 { - /// little_endian_digits.push(carry); - /// } - /// } - /// - /// let mut v = vec![10, 20]; - /// scalar_mul_eq(&mut v, 3); - /// assert_eq!(v, [30, 60]); - /// - /// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D); - /// let mut v = vec![0x4321, 0x8765]; - /// scalar_mul_eq(&mut v, 0xFEED); - /// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]); - /// ``` - /// - /// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul), - /// except that it gives the value of the overflow instead of just whether one happened: - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// let r = u8::carrying_mul(7, 13, 0); - /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13)); - /// let r = u8::carrying_mul(13, 42, 0); - /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42)); - /// ``` - /// - /// The value of the first field in the returned tuple matches what you'd get - /// by combining the [`wrapping_mul`](Self::wrapping_mul) and - /// [`wrapping_add`](Self::wrapping_add) methods: - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!( - /// 789_u16.carrying_mul(456, 123).0, - /// 789_u16.wrapping_mul(456).wrapping_add(123), - /// ); - /// ``` - #[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_mul(self, rhs: Self, carry: Self) -> (Self, Self) { - Self::carrying_mul_add(self, rhs, carry, 0) - } - - /// Calculates the "full multiplication" `self * rhs + carry1 + carry2` - /// without the possibility to overflow. - /// - /// This returns the low-order (wrapping) bits and the high-order (overflow) bits - /// of the result as two separate values, in that order. - /// - /// Performs "long multiplication" which takes in an extra amount to add, and may return an - /// additional amount of overflow. This allows for chaining together multiple - /// multiplications to create "big integers" which represent larger values. - /// - /// If you don't need either `carry`, then you can use [`Self::widening_mul`] instead, - /// and if you only need one `carry`, then you can use [`Self::carrying_mul`] instead. - /// - /// # Examples - /// - /// Basic usage: - /// - /// Please note that this example is shared between integer types, - /// which explains why `u32` is used here. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// assert_eq!(5u32.carrying_mul_add(2, 0, 0), (10, 0)); - /// assert_eq!(5u32.carrying_mul_add(2, 10, 10), (30, 0)); - /// assert_eq!(1_000_000_000u32.carrying_mul_add(10, 0, 0), (1410065408, 2)); - /// assert_eq!(1_000_000_000u32.carrying_mul_add(10, 10, 10), (1410065428, 2)); - #[doc = concat!("assert_eq!(", - stringify!($SelfT), "::MAX.carrying_mul_add(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ", - "(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX));" - )] - /// ``` - /// - /// This is the core per-digit operation for "grade school" O(n²) multiplication. - /// - /// Please note that this example is shared between integer types, - /// using `u8` for simplicity of the demonstration. - /// - /// ``` - /// #![feature(bigint_helper_methods)] - /// - /// fn quadratic_mul(a: [u8; N], b: [u8; N]) -> [u8; N] { - /// let mut out = [0; N]; - /// for j in 0..N { - /// let mut carry = 0; - /// for i in 0..(N - j) { - /// (out[j + i], carry) = u8::carrying_mul_add(a[i], b[j], out[j + i], carry); - /// } - /// } - /// out - /// } - /// - /// // -1 * -1 == 1 - /// assert_eq!(quadratic_mul([0xFF; 3], [0xFF; 3]), [1, 0, 0]); - /// - /// assert_eq!(u32::wrapping_mul(0x9e3779b9, 0x7f4a7c15), 0xCFFC982D); - /// assert_eq!( - /// quadratic_mul(u32::to_le_bytes(0x9e3779b9), u32::to_le_bytes(0x7f4a7c15)), - /// u32::to_le_bytes(0xCFFC982D) - /// ); - /// ``` - #[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_mul_add(self, rhs: Self, carry: Self, add: Self) -> (Self, Self) { - intrinsics::carrying_mul_add(self, rhs, carry, add) - } - /// Calculates the divisor when `self` is divided by `rhs`. /// /// Returns a tuple of the divisor along with a boolean indicating @@ -2775,7 +2545,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2806,7 +2576,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2834,7 +2604,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -2865,7 +2635,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -3068,8 +2838,8 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".isqrt(), 3);")] /// ``` - #[stable(feature = "isqrt", since = "1.84.0")] - #[rustc_const_stable(feature = "isqrt", since = "1.84.0")] + #[stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "isqrt", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3102,7 +2872,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -3130,7 +2900,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero. + /// This function will panic if `rhs` is 0. /// /// # Examples /// @@ -3321,6 +3091,7 @@ macro_rules! uint_impl { // overflow cases it instead ends up returning the maximum value // of the type, and can return 0 for 0. #[inline] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_int_pow", since = "1.50.0"))] const fn one_less_than_next_power_of_two(self) -> Self { if self <= 1 { return 0; } @@ -3398,6 +3169,7 @@ macro_rules! uint_impl { #[inline] #[unstable(feature = "wrapping_next_power_of_two", issue = "32463", reason = "needs decision on wrapping behavior")] + #[rustc_const_unstable(feature = "wrapping_next_power_of_two", issue = "32463")] #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn wrapping_next_power_of_two(self) -> Self { diff --git a/core/src/num/wrapping.rs b/core/src/num/wrapping.rs index 55fa91d0b9f49..1156b389e2867 100644 --- a/core/src/num/wrapping.rs +++ b/core/src/num/wrapping.rs @@ -1058,33 +1058,33 @@ mod shift_max { #[cfg(target_pointer_width = "16")] mod platform { - pub(crate) const usize: u32 = super::u16; - pub(crate) const isize: u32 = super::i16; + pub const usize: u32 = super::u16; + pub const isize: u32 = super::i16; } #[cfg(target_pointer_width = "32")] mod platform { - pub(crate) const usize: u32 = super::u32; - pub(crate) const isize: u32 = super::i32; + pub const usize: u32 = super::u32; + pub const isize: u32 = super::i32; } #[cfg(target_pointer_width = "64")] mod platform { - pub(crate) const usize: u32 = super::u64; - pub(crate) const isize: u32 = super::i64; + pub const usize: u32 = super::u64; + pub const isize: u32 = super::i64; } - pub(super) const i8: u32 = (1 << 3) - 1; - pub(super) const i16: u32 = (1 << 4) - 1; - pub(super) const i32: u32 = (1 << 5) - 1; - pub(super) const i64: u32 = (1 << 6) - 1; - pub(super) const i128: u32 = (1 << 7) - 1; - pub(super) use self::platform::isize; - - pub(super) const u8: u32 = i8; - pub(super) const u16: u32 = i16; - pub(super) const u32: u32 = i32; - pub(super) const u64: u32 = i64; - pub(super) const u128: u32 = i128; - pub(super) use self::platform::usize; + pub const i8: u32 = (1 << 3) - 1; + pub const i16: u32 = (1 << 4) - 1; + pub const i32: u32 = (1 << 5) - 1; + pub const i64: u32 = (1 << 6) - 1; + pub const i128: u32 = (1 << 7) - 1; + pub use self::platform::isize; + + pub const u8: u32 = i8; + pub const u16: u32 = i16; + pub const u32: u32 = i32; + pub const u64: u32 = i64; + pub const u128: u32 = i128; + pub use self::platform::usize; } diff --git a/core/src/ops/arith.rs b/core/src/ops/arith.rs index fe7ff2d9ede6a..565bccf589826 100644 --- a/core/src/ops/arith.rs +++ b/core/src/ops/arith.rs @@ -65,7 +65,6 @@ /// ``` #[lang = "add"] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_ops", issue = "90080")] #[rustc_on_unimplemented( on(all(_Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",), on(all(_Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",), @@ -74,7 +73,7 @@ append_const_msg )] #[doc(alias = "+")] -#[const_trait] +#[cfg_attr(not(bootstrap), const_trait)] pub trait Add { /// The resulting type after applying the `+` operator. #[stable(feature = "rust1", since = "1.0.0")] @@ -96,6 +95,18 @@ pub trait Add { macro_rules! add_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(bootstrap)] + impl Add for $t { + type Output = $t; + + #[inline] + #[track_caller] + #[rustc_inherit_overflow_checks] + fn add(self, other: $t) -> $t { self + other } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[cfg(not(bootstrap))] impl const Add for $t { type Output = $t; diff --git a/core/src/ops/async_function.rs b/core/src/ops/async_function.rs index 6be42ca7d32fe..4b230b15a1e6f 100644 --- a/core/src/ops/async_function.rs +++ b/core/src/ops/async_function.rs @@ -4,8 +4,9 @@ use crate::marker::Tuple; /// An async-aware version of the [`Fn`](crate::ops::Fn) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[stable(feature = "async_closure", since = "1.85.0")] +#[unstable(feature = "async_closure", issue = "62290")] #[rustc_paren_sugar] +#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn"] pub trait AsyncFn: AsyncFnMut { @@ -17,8 +18,9 @@ pub trait AsyncFn: AsyncFnMut { /// An async-aware version of the [`FnMut`](crate::ops::FnMut) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[stable(feature = "async_closure", since = "1.85.0")] +#[unstable(feature = "async_closure", issue = "62290")] #[rustc_paren_sugar] +#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn_mut"] pub trait AsyncFnMut: AsyncFnOnce { @@ -37,8 +39,9 @@ pub trait AsyncFnMut: AsyncFnOnce { /// An async-aware version of the [`FnOnce`](crate::ops::FnOnce) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[stable(feature = "async_closure", since = "1.85.0")] +#[unstable(feature = "async_closure", issue = "62290")] #[rustc_paren_sugar] +#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn_once"] pub trait AsyncFnOnce { @@ -61,7 +64,7 @@ mod impls { use super::{AsyncFn, AsyncFnMut, AsyncFnOnce}; use crate::marker::Tuple; - #[stable(feature = "async_closure", since = "1.85.0")] + #[unstable(feature = "async_fn_traits", issue = "none")] impl AsyncFn for &F where F: AsyncFn, @@ -71,7 +74,7 @@ mod impls { } } - #[stable(feature = "async_closure", since = "1.85.0")] + #[unstable(feature = "async_fn_traits", issue = "none")] impl AsyncFnMut for &F where F: AsyncFn, @@ -86,7 +89,7 @@ mod impls { } } - #[stable(feature = "async_closure", since = "1.85.0")] + #[unstable(feature = "async_fn_traits", issue = "none")] impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce for &'a F where F: AsyncFn, @@ -99,7 +102,7 @@ mod impls { } } - #[stable(feature = "async_closure", since = "1.85.0")] + #[unstable(feature = "async_fn_traits", issue = "none")] impl AsyncFnMut for &mut F where F: AsyncFnMut, @@ -114,7 +117,7 @@ mod impls { } } - #[stable(feature = "async_closure", since = "1.85.0")] + #[unstable(feature = "async_fn_traits", issue = "none")] impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce for &'a mut F where F: AsyncFnMut, diff --git a/core/src/ops/control_flow.rs b/core/src/ops/control_flow.rs index c8fcee5c140f5..55deabbee8fb5 100644 --- a/core/src/ops/control_flow.rs +++ b/core/src/ops/control_flow.rs @@ -79,7 +79,6 @@ use crate::{convert, ops}; /// [`Break`]: ControlFlow::Break /// [`Continue`]: ControlFlow::Continue #[stable(feature = "control_flow_enum_type", since = "1.55.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "ControlFlow")] // ControlFlow should not implement PartialOrd or Ord, per RFC 3058: // https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -141,8 +140,8 @@ impl ControlFlow { /// ``` /// use std::ops::ControlFlow; /// - /// assert!(ControlFlow::<&str, i32>::Break("Stop right there!").is_break()); - /// assert!(!ControlFlow::<&str, i32>::Continue(3).is_break()); + /// assert!(ControlFlow::::Break(3).is_break()); + /// assert!(!ControlFlow::::Continue(3).is_break()); /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] @@ -157,8 +156,8 @@ impl ControlFlow { /// ``` /// use std::ops::ControlFlow; /// - /// assert!(!ControlFlow::<&str, i32>::Break("Stop right there!").is_continue()); - /// assert!(ControlFlow::<&str, i32>::Continue(3).is_continue()); + /// assert!(!ControlFlow::::Break(3).is_continue()); + /// assert!(ControlFlow::::Continue(3).is_continue()); /// ``` #[inline] #[stable(feature = "control_flow_enum_is", since = "1.59.0")] @@ -174,8 +173,8 @@ impl ControlFlow { /// ``` /// use std::ops::ControlFlow; /// - /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").break_value(), Some("Stop right there!")); - /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).break_value(), None); + /// assert_eq!(ControlFlow::::Break(3).break_value(), Some(3)); + /// assert_eq!(ControlFlow::::Continue(3).break_value(), None); /// ``` #[inline] #[stable(feature = "control_flow_enum", since = "1.83.0")] @@ -205,8 +204,8 @@ impl ControlFlow { /// ``` /// use std::ops::ControlFlow; /// - /// assert_eq!(ControlFlow::<&str, i32>::Break("Stop right there!").continue_value(), None); - /// assert_eq!(ControlFlow::<&str, i32>::Continue(3).continue_value(), Some(3)); + /// assert_eq!(ControlFlow::::Break(3).continue_value(), None); + /// assert_eq!(ControlFlow::::Continue(3).continue_value(), Some(3)); /// ``` #[inline] #[stable(feature = "control_flow_enum", since = "1.83.0")] diff --git a/core/src/ops/deref.rs b/core/src/ops/deref.rs index 11490ea2bfcb4..e9bb40d0fdd17 100644 --- a/core/src/ops/deref.rs +++ b/core/src/ops/deref.rs @@ -133,8 +133,7 @@ #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Deref"] -#[const_trait] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] +#[cfg_attr(not(bootstrap), const_trait)] pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] @@ -149,6 +148,18 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } +#[cfg(bootstrap)] +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for &T { + type Target = T; + + #[rustc_diagnostic_item = "noop_method_deref"] + fn deref(&self) -> &T { + *self + } +} + +#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const Deref for &T { type Target = T; @@ -162,6 +173,17 @@ impl const Deref for &T { #[stable(feature = "rust1", since = "1.0.0")] impl !DerefMut for &T {} +#[cfg(bootstrap)] +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for &mut T { + type Target = T; + + fn deref(&self) -> &T { + *self + } +} + +#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const Deref for &mut T { type Target = T; @@ -260,11 +282,11 @@ impl const Deref for &mut T { /// *x = 'b'; /// assert_eq!('b', x.value); /// ``` +#[cfg(not(bootstrap))] #[lang = "deref_mut"] #[doc(alias = "*")] #[stable(feature = "rust1", since = "1.0.0")] #[const_trait] -#[rustc_const_unstable(feature = "const_deref", issue = "88955")] pub trait DerefMut: ~const Deref { /// Mutably dereferences the value. #[stable(feature = "rust1", since = "1.0.0")] @@ -272,6 +294,27 @@ pub trait DerefMut: ~const Deref { fn deref_mut(&mut self) -> &mut Self::Target; } +/// Bootstrap +#[lang = "deref_mut"] +#[doc(alias = "*")] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg(bootstrap)] +pub trait DerefMut: Deref { + /// Mutably dereferences the value. + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "deref_mut_method"] + fn deref_mut(&mut self) -> &mut Self::Target; +} + +#[cfg(bootstrap)] +#[stable(feature = "rust1", since = "1.0.0")] +impl DerefMut for &mut T { + fn deref_mut(&mut self) -> &mut T { + *self + } +} + +#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const DerefMut for &mut T { fn deref_mut(&mut self) -> &mut T { @@ -362,15 +405,18 @@ unsafe impl DerefPure for &mut T {} /// } /// ``` #[lang = "receiver"] +#[cfg(not(bootstrap))] #[unstable(feature = "arbitrary_self_types", issue = "44874")] pub trait Receiver { /// The target type on which the method may be called. + #[cfg(not(bootstrap))] #[rustc_diagnostic_item = "receiver_target"] #[lang = "receiver_target"] #[unstable(feature = "arbitrary_self_types", issue = "44874")] type Target: ?Sized; } +#[cfg(not(bootstrap))] #[unstable(feature = "arbitrary_self_types", issue = "44874")] impl Receiver for P where @@ -387,7 +433,8 @@ where /// facility based around the current "arbitrary self types" unstable feature. /// That new facility will use the replacement trait above called `Receiver` /// which is why this is now named `LegacyReceiver`. -#[lang = "legacy_receiver"] +#[cfg_attr(bootstrap, lang = "receiver")] +#[cfg_attr(not(bootstrap), lang = "legacy_receiver")] #[unstable(feature = "legacy_receiver_trait", issue = "none")] #[doc(hidden)] pub trait LegacyReceiver { diff --git a/core/src/ops/drop.rs b/core/src/ops/drop.rs index e024b7fb4d301..a6f63ad68d695 100644 --- a/core/src/ops/drop.rs +++ b/core/src/ops/drop.rs @@ -203,8 +203,7 @@ /// [nomicon]: ../../nomicon/phantom-data.html#an-exception-the-special-case-of-the-standard-library-and-its-unstable-may_dangle #[lang = "drop"] #[stable(feature = "rust1", since = "1.0.0")] -#[const_trait] -#[rustc_const_unstable(feature = "const_destruct", issue = "133214")] +// FIXME(const_trait_impl) #[const_trait] pub trait Drop { /// Executes the destructor for this type. /// diff --git a/core/src/ops/index_range.rs b/core/src/ops/index_range.rs index b82184b15b2f5..dce3514a1595b 100644 --- a/core/src/ops/index_range.rs +++ b/core/src/ops/index_range.rs @@ -18,7 +18,7 @@ impl IndexRange { /// # Safety /// - `start <= end` #[inline] - pub(crate) const unsafe fn new_unchecked(start: usize, end: usize) -> Self { + pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self { ub_checks::assert_unsafe_precondition!( check_library_ub, "IndexRange::new_unchecked requires `start <= end`", @@ -28,22 +28,22 @@ impl IndexRange { } #[inline] - pub(crate) const fn zero_to(end: usize) -> Self { + pub const fn zero_to(end: usize) -> Self { IndexRange { start: 0, end } } #[inline] - pub(crate) const fn start(&self) -> usize { + pub const fn start(&self) -> usize { self.start } #[inline] - pub(crate) const fn end(&self) -> usize { + pub const fn end(&self) -> usize { self.end } #[inline] - pub(crate) const fn len(&self) -> usize { + pub const fn len(&self) -> usize { // SAFETY: By invariant, this cannot wrap // Using the intrinsic because a UB check here impedes LLVM optimization. (#131563) unsafe { crate::intrinsics::unchecked_sub(self.end, self.start) } @@ -79,7 +79,7 @@ impl IndexRange { /// /// This is designed to help implement `Iterator::advance_by`. #[inline] - pub(crate) fn take_prefix(&mut self, n: usize) -> Self { + pub fn take_prefix(&mut self, n: usize) -> Self { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the addition cannot overflow. @@ -99,7 +99,7 @@ impl IndexRange { /// /// This is designed to help implement `Iterator::advance_back_by`. #[inline] - pub(crate) fn take_suffix(&mut self, n: usize) -> Self { + pub fn take_suffix(&mut self, n: usize) -> Self { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the subtraction cannot overflow. diff --git a/core/src/ops/mod.rs b/core/src/ops/mod.rs index 7b2ced2cc4bdc..cea1f84f3fd60 100644 --- a/core/src/ops/mod.rs +++ b/core/src/ops/mod.rs @@ -171,6 +171,7 @@ pub use self::deref::DerefPure; #[unstable(feature = "legacy_receiver_trait", issue = "none")] pub use self::deref::LegacyReceiver; #[unstable(feature = "arbitrary_self_types", issue = "44874")] +#[cfg(not(bootstrap))] pub use self::deref::Receiver; #[stable(feature = "rust1", since = "1.0.0")] pub use self::deref::{Deref, DerefMut}; @@ -182,10 +183,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/core/src/ops/range.rs b/core/src/ops/range.rs index 42e07a0e51da4..727a22e454d3d 100644 --- a/core/src/ops/range.rs +++ b/core/src/ops/range.rs @@ -979,19 +979,6 @@ impl RangeBounds 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. @@ -999,38 +986,13 @@ pub enum OneSidedRangeBound { /// Types that implement `OneSidedRange` must return `Bound::Unbounded` /// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`. #[unstable(feature = "one_sided_range", issue = "69780")] -pub trait OneSidedRange: RangeBounds { - /// 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); -} +pub trait OneSidedRange: RangeBounds {} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeTo -where - Self: RangeBounds, -{ - fn bound(self) -> (OneSidedRangeBound, T) { - (OneSidedRangeBound::End, self.end) - } -} +impl OneSidedRange for RangeTo where Self: RangeBounds {} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeFrom -where - Self: RangeBounds, -{ - fn bound(self) -> (OneSidedRangeBound, T) { - (OneSidedRangeBound::StartInclusive, self.start) - } -} +impl OneSidedRange for RangeFrom where Self: RangeBounds {} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeToInclusive -where - Self: RangeBounds, -{ - fn bound(self) -> (OneSidedRangeBound, T) { - (OneSidedRangeBound::EndInclusive, self.end) - } -} +impl OneSidedRange for RangeToInclusive where Self: RangeBounds {} diff --git a/core/src/ops/try_trait.rs b/core/src/ops/try_trait.rs index 3ba2957526f9c..cd444c86ed06e 100644 --- a/core/src/ops/try_trait.rs +++ b/core/src/ops/try_trait.rs @@ -338,7 +338,6 @@ pub trait FromResidual::Residual> { #[inline] #[track_caller] // because `Result::from_residual` has it #[lang = "from_yeet"] -#[allow(unreachable_pub)] // not-exposed but still used via lang-item pub fn from_yeet(yeeted: Y) -> T where T: FromResidual>, @@ -384,14 +383,12 @@ impl NeverShortCircuit { /// This is useful for implementing infallible functions in terms of the `try_` ones, /// without accidentally capturing extra generic parameters in a closure. #[inline] - pub(crate) fn wrap_mut_1( - mut f: impl FnMut(A) -> T, - ) -> impl FnMut(A) -> NeverShortCircuit { + pub fn wrap_mut_1(mut f: impl FnMut(A) -> T) -> impl FnMut(A) -> NeverShortCircuit { move |a| NeverShortCircuit(f(a)) } #[inline] - pub(crate) fn wrap_mut_2(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self { + pub fn wrap_mut_2(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self { move |a, b| NeverShortCircuit(f(a, b)) } } diff --git a/core/src/option.rs b/core/src/option.rs index a9f06b92ad5dd..29d1956af9559 100644 --- a/core/src/option.rs +++ b/core/src/option.rs @@ -563,7 +563,7 @@ use crate::pin::Pin; use crate::{cmp, convert, hint, mem, slice}; /// The `Option` type. See [the module level documentation](self) for more. -#[doc(search_unbox)] +#[cfg_attr(not(bootstrap), doc(search_unbox))] #[derive(Copy, Eq, Debug, Hash)] #[rustc_diagnostic_item = "Option"] #[lang = "Option"] @@ -738,7 +738,7 @@ impl Option { #[inline] #[must_use] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_stable(feature = "const_option_ext", since = "1.84.0")] + #[rustc_const_stable(feature = "const_option_ext", since = "CURRENT_RUSTC_VERSION")] pub const fn as_pin_ref(self: Pin<&Self>) -> Option> { // FIXME(const-hack): use `map` once that is possible match Pin::get_ref(self).as_ref() { @@ -755,7 +755,7 @@ impl Option { #[inline] #[must_use] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_stable(feature = "const_option_ext", since = "1.84.0")] + #[rustc_const_stable(feature = "const_option_ext", since = "CURRENT_RUSTC_VERSION")] pub const fn as_pin_mut(self: Pin<&mut Self>) -> Option> { // SAFETY: `get_unchecked_mut` is never used to move the `Option` inside `self`. // `x` is guaranteed to be pinned because it comes from `self` which is pinned. @@ -802,7 +802,7 @@ impl Option { #[inline] #[must_use] #[stable(feature = "option_as_slice", since = "1.75.0")] - #[rustc_const_stable(feature = "const_option_ext", since = "1.84.0")] + #[rustc_const_stable(feature = "const_option_ext", since = "CURRENT_RUSTC_VERSION")] pub const fn as_slice(&self) -> &[T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to @@ -857,7 +857,7 @@ impl Option { #[inline] #[must_use] #[stable(feature = "option_as_slice", since = "1.75.0")] - #[rustc_const_stable(feature = "const_option_ext", since = "1.84.0")] + #[rustc_const_stable(feature = "const_option_ext", since = "CURRENT_RUSTC_VERSION")] pub const fn as_mut_slice(&mut self) -> &mut [T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to @@ -937,16 +937,10 @@ impl Option { /// Returns the contained [`Some`] value, consuming the `self` value. /// /// Because this function may panic, its use is generally discouraged. - /// Panics are meant for unrecoverable errors, and - /// [may abort the entire program][panic-abort]. - /// /// Instead, prefer to use pattern matching and handle the [`None`] /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or - /// [`unwrap_or_default`]. In functions returning `Option`, you can use - /// [the `?` (try) operator][try-option]. + /// [`unwrap_or_default`]. /// - /// [panic-abort]: https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html - /// [try-option]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#where-the--operator-can-be-used /// [`unwrap_or`]: Option::unwrap_or /// [`unwrap_or_else`]: Option::unwrap_or_else /// [`unwrap_or_default`]: Option::unwrap_or_default diff --git a/core/src/panic.rs b/core/src/panic.rs index 5fa340a6147f6..179aadf0c286c 100644 --- a/core/src/panic.rs +++ b/core/src/panic.rs @@ -208,13 +208,14 @@ pub macro const_panic { #[rustc_allow_const_fn_unstable(const_eval_select)] #[inline(always)] // inline the wrapper #[track_caller] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_panic", since = "CURRENT_RUSTC_VERSION"))] const fn do_panic($($arg: $ty),*) -> ! { $crate::intrinsics::const_eval_select!( @capture { $($arg: $ty = $arg),* } -> !: #[noinline] if const #[track_caller] #[inline] { // Inline this, to prevent codegen $crate::panic!($const_msg) - } else #[track_caller] { // Do not inline this, it makes perf worse + } else #[track_caller] #[cfg_attr(bootstrap, inline)] { // Do not inline this, it makes perf worse $crate::panic!($runtime_msg) } ) diff --git a/core/src/panic/panic_info.rs b/core/src/panic/panic_info.rs index 9d53567a26fd9..230a9918dbf3e 100644 --- a/core/src/panic/panic_info.rs +++ b/core/src/panic/panic_info.rs @@ -165,7 +165,7 @@ impl<'a> PanicMessage<'a> { /// /// See [`fmt::Arguments::as_str`] for details. #[stable(feature = "panic_info_message", since = "1.81.0")] - #[rustc_const_stable(feature = "const_arguments_as_str", since = "1.84.0")] + #[rustc_const_stable(feature = "const_arguments_as_str", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn as_str(&self) -> Option<&'static str> { diff --git a/core/src/panicking.rs b/core/src/panicking.rs index b97f19e1baa93..f603eb2971f6d 100644 --- a/core/src/panicking.rs +++ b/core/src/panicking.rs @@ -51,7 +51,8 @@ const _: () = assert!(cfg!(panic = "abort"), "panic_immediate_abort requires -C #[track_caller] #[lang = "panic_fmt"] // needed for const-evaluated panics #[rustc_do_not_const_check] // hooked by const-eval -#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] +#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { super::intrinsics::abort() @@ -85,7 +86,8 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { // and unwinds anyway, we will hit the "unwinding out of nounwind function" guard, // which causes a "panic in a function that cannot unwind". #[rustc_nounwind] -#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] +#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable #[rustc_allow_const_fn_unstable(const_eval_select)] pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! { const_eval_select!( @@ -128,7 +130,8 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] -#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] +#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable #[lang = "panic"] // used by lints and miri for panics pub const fn panic(expr: &'static str) -> ! { // Use Arguments::new_const instead of format_args!("{expr}") to potentially @@ -166,7 +169,8 @@ macro_rules! panic_const { #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] - #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] + #[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable #[lang = stringify!($lang)] pub const fn $lang() -> ! { // Use Arguments::new_const instead of format_args!("{expr}") to potentially @@ -213,7 +217,8 @@ panic_const! { #[cfg_attr(feature = "panic_immediate_abort", inline)] #[lang = "panic_nounwind"] // needed by codegen for non-unwinding panics #[rustc_nounwind] -#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] +#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable pub const fn panic_nounwind(expr: &'static str) -> ! { panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ false); } @@ -229,7 +234,8 @@ pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { #[track_caller] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] -#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] +#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable pub const fn panic_explicit() -> ! { panic_display(&"explicit panic"); } @@ -246,7 +252,8 @@ pub fn unreachable_display(x: &T) -> ! { #[inline] #[track_caller] #[rustc_diagnostic_item = "panic_str_2015"] -#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] +#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable pub const fn panic_str_2015(expr: &str) -> ! { panic_display(&expr); } @@ -256,7 +263,8 @@ pub const fn panic_str_2015(expr: &str) -> ! { #[rustc_do_not_const_check] // hooked by const-eval // enforce a &&str argument in const-check and hook this by const-eval #[rustc_const_panic_str] -#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] +#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable pub const fn panic_display(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); } @@ -291,22 +299,6 @@ 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 @@ -341,7 +333,8 @@ fn panic_in_cleanup() -> ! { /// This function is used instead of panic_fmt in const eval. #[lang = "const_panic_fmt"] // needed by const-eval machine to replace calls to `panic_fmt` lang item -#[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "panic_internals", issue = "none"))] +#[cfg_attr(not(bootstrap), rustc_const_stable_indirect)] // must follow stable const rules since it is exposed to stable pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { if let Some(msg) = fmt.as_str() { // The panic_display function is hooked by const eval. diff --git a/core/src/pat.rs b/core/src/pat.rs index 752e79c2dacee..1f89d960be67b 100644 --- a/core/src/pat.rs +++ b/core/src/pat.rs @@ -6,7 +6,7 @@ /// ``` #[macro_export] #[rustc_builtin_macro(pattern_type)] -#[unstable(feature = "pattern_type_macro", issue = "123646")] +#[unstable(feature = "core_pattern_type", issue = "123646")] macro_rules! pattern_type { ($($arg:tt)*) => { /* compiler built-in */ diff --git a/core/src/pin.rs b/core/src/pin.rs index 2a0bf89fcf7a9..c14c49a0d92f9 100644 --- a/core/src/pin.rs +++ b/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 are 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 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 @@ -331,7 +331,7 @@ //! //! Note that this invariant is enforced by simply making it impossible to call code that would //! perform a move on the pinned value. This is the case since the only way to access that pinned -//! value is through the pinning [Pin]<[&mut] T>, which in turn restricts our access. +//! value is through the pinning [Pin]<[&mut] T>>, which in turn restricts our access. //! //! ## [`Unpin`] //! @@ -373,13 +373,13 @@ //! exactly what we did with our `AddrTracker` example above. Without doing this, you *must not* //! rely on pinning-related guarantees to apply to your type! //! -//! If you really need to pin a value of a foreign or built-in type that implements [`Unpin`], -//! you'll need to create your own wrapper type around the [`Unpin`] type you want to pin and then -//! opt-out of [`Unpin`] using [`PhantomPinned`]. +//! If need to truly pin a value of a foreign or built-in type that implements [`Unpin`], you'll +//! need to create your own wrapper type around the [`Unpin`] type you want to pin and then +//! opts-out of [`Unpin`] using [`PhantomPinned`]. //! //! Exposing access to the inner field which you want to remain pinned must then be carefully //! considered as well! Remember, exposing a method that gives access to a -//! [Pin]<[&mut] InnerT> where InnerT: [Unpin] would allow safe code to +//! [Pin]<[&mut] InnerT>> where InnerT: [Unpin] would allow safe code to //! trivially move the inner value out of that pinning pointer, which is precisely what you're //! seeking to prevent! Exposing a field of a pinned value through a pinning pointer is called //! "projecting" a pin, and the more general case of deciding in which cases a pin should be able @@ -595,7 +595,7 @@ //! [drop-impl]: self#implementing-drop-for-types-with-address-sensitive-states //! //! The [`drop`] function takes [`&mut self`], but this is called *even if that `self` has been -//! pinned*! Implementing [`Drop`] for a type with address-sensitive states requires some care, because if `self` was +//! pinned*! Implementing [`Drop`] for a type with address-sensitive states, because if `self` was //! indeed in an address-sensitive state before [`drop`] was called, it is as if the compiler //! automatically called [`Pin::get_unchecked_mut`]. //! @@ -1186,7 +1186,7 @@ impl> Pin { /// let mut pinned: Pin<&mut u8> = Pin::new(&mut val); /// ``` #[inline(always)] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const fn new(pointer: Ptr) -> Pin { // SAFETY: the value pointed to is `Unpin`, and so has no requirements @@ -1215,7 +1215,7 @@ impl> Pin { /// ``` #[inline(always)] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const fn into_inner(pin: Pin) -> Ptr { pin.__pointer @@ -1352,7 +1352,7 @@ impl Pin { /// [`pin` module docs]: self #[lang = "new_unchecked"] #[inline(always)] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const unsafe fn new_unchecked(pointer: Ptr) -> Pin { Pin { __pointer: pointer } @@ -1423,7 +1423,7 @@ impl Pin { /// move in the future, and this method does not enable the pointee to move. "Malicious" /// implementations of `Ptr::DerefMut` are likewise ruled out by the contract of /// `Pin::new_unchecked`. - #[stable(feature = "pin_deref_mut", since = "1.84.0")] + #[stable(feature = "pin_deref_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "`self` will be dropped if the result is not used"] #[inline(always)] pub fn as_deref_mut(self: Pin<&mut Pin>) -> Pin<&mut Ptr::Target> { @@ -1505,7 +1505,7 @@ impl Pin { /// instead. #[inline(always)] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const unsafe fn into_inner_unchecked(pin: Pin) -> Ptr { pin.__pointer @@ -1561,7 +1561,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { /// ["pinning projections"]: self#projections-and-structural-pinning #[inline(always)] #[must_use] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const fn get_ref(self) -> &'a T { self.__pointer @@ -1572,7 +1572,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { /// Converts this `Pin<&mut T>` into a `Pin<&T>` with the same lifetime. #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const fn into_ref(self) -> Pin<&'a T> { Pin { __pointer: self.__pointer } @@ -1590,7 +1590,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const fn get_mut(self) -> &'a mut T where T: Unpin, @@ -1611,7 +1611,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn get_unchecked_mut(self) -> &'a mut T { self.__pointer } @@ -1654,7 +1654,7 @@ impl Pin<&'static T> { /// This is safe because `T` is borrowed immutably for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const fn static_ref(r: &'static T) -> Pin<&'static T> { // SAFETY: The 'static borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). @@ -1668,7 +1668,7 @@ impl Pin<&'static mut T> { /// This is safe because `T` is borrowed for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] - #[rustc_const_stable(feature = "const_pin", since = "1.84.0")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const fn static_mut(r: &'static mut T) -> Pin<&'static mut T> { // SAFETY: The 'static borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). diff --git a/core/src/prelude/common.rs b/core/src/prelude/common.rs index 8b116cecb5295..e38ef1e147c76 100644 --- a/core/src/prelude/common.rs +++ b/core/src/prelude/common.rs @@ -12,9 +12,6 @@ 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/core/src/prelude/mod.rs b/core/src/prelude/mod.rs index 0ab97f5bbd50e..496b78439ea6c 100644 --- a/core/src/prelude/mod.rs +++ b/core/src/prelude/mod.rs @@ -18,6 +18,16 @@ 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. @@ -61,7 +71,7 @@ pub mod rust_2021 { /// The 2024 version of the core prelude. /// /// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2024", since = "1.85.0")] +#[unstable(feature = "prelude_2024", issue = "121042")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; @@ -74,7 +84,7 @@ pub mod rust_2024 { #[doc(no_inline)] pub use crate::convert::{TryFrom, TryInto}; - #[stable(feature = "prelude_2024", since = "1.85.0")] + #[unstable(feature = "prelude_2024", issue = "121042")] #[doc(no_inline)] pub use crate::future::{Future, IntoFuture}; } diff --git a/core/src/primitive_docs.rs b/core/src/primitive_docs.rs index bbf5939fe1b05..e105ceadff757 100644 --- a/core/src/primitive_docs.rs +++ b/core/src/primitive_docs.rs @@ -563,11 +563,11 @@ impl () {} /// Note that here the call to [`drop`] is for clarity - it indicates /// that we are done with the given value and it should be destroyed. /// -/// ## 3. Create it using `&raw` +/// ## 3. Create it using `ptr::addr_of!` /// -/// Instead of coercing a reference to a raw pointer, you can use the raw borrow -/// operators `&raw const` (for `*const T`) and `&raw mut` (for `*mut T`). -/// These operators allow you to create raw pointers to fields to which you cannot +/// Instead of coercing a reference to a raw pointer, you can use the macros +/// [`ptr::addr_of!`] (for `*const T`) and [`ptr::addr_of_mut!`] (for `*mut T`). +/// These macros allow you to create raw pointers to fields to which you cannot /// create a reference (without causing undefined behavior), such as an /// unaligned field. This might be necessary if packed structs or uninitialized /// memory is involved. @@ -580,7 +580,7 @@ impl () {} /// unaligned: u32, /// } /// let s = S::default(); -/// let p = &raw const s.unaligned; // not allowed with coercion +/// let p = std::ptr::addr_of!(s.unaligned); // not allowed with coercion /// ``` /// /// ## 4. Get it from C. @@ -1160,9 +1160,9 @@ impl (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/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. +/// 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. /// /// *[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 (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`. +/// 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`. /// /// _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/core/src/ptr/alignment.rs b/core/src/ptr/alignment.rs index 2da94e72566e9..2538d60a8eee9 100644 --- a/core/src/ptr/alignment.rs +++ b/core/src/ptr/alignment.rs @@ -41,11 +41,11 @@ impl Alignment { /// This provides the same numerical value as [`mem::align_of`], /// but in an `Alignment` instead of a `usize`. #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] - #[must_use] pub const fn of() -> Self { - // This can't actually panic since type alignment is always a power of two. - const { Alignment::new(mem::align_of::()).unwrap() } + // SAFETY: rustc ensures that type alignment is always a power of two. + unsafe { Alignment::new_unchecked(mem::align_of::()) } } /// Creates an `Alignment` from a `usize`, or returns `None` if it's @@ -53,6 +53,7 @@ impl Alignment { /// /// Note that `0` is not a power of two, nor a valid alignment. #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] pub const fn new(align: usize) -> Option { if align.is_power_of_two() { @@ -72,6 +73,7 @@ impl Alignment { /// Equivalently, it must be `1 << exp` for some `exp` in `0..usize::BITS`. /// It must *not* be zero. #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] pub const unsafe fn new_unchecked(align: usize) -> Self { assert_unsafe_precondition!( @@ -87,6 +89,7 @@ impl Alignment { /// Returns the alignment as a [`usize`]. #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] pub const fn as_usize(self) -> usize { self.0 as usize @@ -94,15 +97,11 @@ impl Alignment { /// Returns the alignment as a [NonZero]<[usize]>. #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] pub const fn as_nonzero(self) -> NonZero { - // This transmutes directly to avoid the UbCheck in `NonZero::new_unchecked` - // since there's no way for the user to trip that check anyway -- the - // validity invariant of the type would have to have been broken earlier -- - // and emitting it in an otherwise simple method is bad for compile time. - // SAFETY: All the discriminants are non-zero. - unsafe { mem::transmute::>(self) } + unsafe { NonZero::new_unchecked(self.as_usize()) } } /// Returns the base-2 logarithm of the alignment. @@ -119,6 +118,7 @@ impl Alignment { /// assert_eq!(Alignment::new(1024).unwrap().log2(), 10); /// ``` #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] pub const fn log2(self) -> u32 { self.as_nonzero().trailing_zeros() @@ -148,6 +148,7 @@ impl Alignment { /// assert_ne!(one.mask(Alignment::of::().mask()), one); /// ``` #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070"))] #[inline] pub const fn mask(self) -> usize { // SAFETY: The alignment is always nonzero, and therefore decrementing won't overflow. diff --git a/core/src/ptr/const_ptr.rs b/core/src/ptr/const_ptr.rs index 0c6eaf60d0480..0dbe819acb1b9 100644 --- a/core/src/ptr/const_ptr.rs +++ b/core/src/ptr/const_ptr.rs @@ -12,17 +12,14 @@ impl *const T { /// Therefore, two pointers that are null may still not compare equal to /// each other. /// - /// # Panics during const evaluation + /// ## Behavior during const evaluation /// - /// If this method is used during const evaluation, and `self` is a pointer - /// that is offset beyond the bounds of the memory it initially pointed to, - /// then there might not be enough information to determine whether the - /// pointer is null. This is because the absolute address in memory is not - /// known at compile time. If the nullness of the pointer cannot be - /// determined, this method will panic. - /// - /// In-bounds pointers are never null, so the method will never panic for - /// such pointers. + /// When this function is used during const evaluation, it may return `false` for pointers + /// that turn out to be null at runtime. Specifically, when a pointer to some memory + /// is offset beyond its bounds in such a way that the resulting pointer is null, + /// the function will still return `false`. There is no way for CTFE to know + /// the absolute position of that memory, so we cannot tell if the pointer is + /// null or not. /// /// # Examples /// @@ -32,7 +29,7 @@ impl *const T { /// assert!(!ptr.is_null()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] #[rustc_diagnostic_item = "ptr_const_is_null"] #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] @@ -116,6 +113,7 @@ impl *const T { /// println!("{:?}", unsafe { &*bad }); /// ``` #[unstable(feature = "set_ptr_value", issue = "75091")] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub const fn with_metadata_of(self, meta: *const U) -> *const U @@ -161,7 +159,7 @@ impl *const T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline(always)] - #[stable(feature = "strict_provenance", since = "1.84.0")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn addr(self) -> usize { // A pointer-to-integer transmute currently has exactly the right semantics: it returns the // address without exposing the provenance. Note that this is *not* a stable guarantee about @@ -195,7 +193,7 @@ impl *const T { /// [`with_exposed_provenance`]: with_exposed_provenance #[must_use] #[inline(always)] - #[stable(feature = "exposed_provenance", since = "1.84.0")] + #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } @@ -213,7 +211,7 @@ impl *const T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "1.84.0")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn with_addr(self, addr: usize) -> Self { // This should probably be an intrinsic to avoid doing any sort of arithmetic, but // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's @@ -232,7 +230,7 @@ impl *const T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "1.84.0")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self { self.with_addr(f(self.addr())) } @@ -257,13 +255,6 @@ impl *const T { /// When calling this method, you have to ensure that *either* the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null - /// /// # Examples /// /// ``` @@ -291,7 +282,7 @@ impl *const T { /// } /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const unsafe fn as_ref<'a>(self) -> Option<&'a T> { // SAFETY: the caller must guarantee that `self` is valid @@ -341,13 +332,6 @@ impl *const T { /// When calling this method, you have to ensure that *either* the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null - /// /// # Examples /// /// ``` @@ -363,6 +347,7 @@ impl *const T { /// ``` #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] + #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit> where T: Sized, @@ -519,12 +504,11 @@ impl *const T { /// let mut out = String::new(); /// while ptr != end_rounded_up { /// unsafe { - /// write!(&mut out, "{}, ", *ptr)?; + /// write!(&mut out, "{}, ", *ptr).unwrap(); /// } /// ptr = ptr.wrapping_offset(step); /// } /// assert_eq!(out.as_str(), "1, 3, 5, "); - /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1034,6 +1018,7 @@ impl *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1143,12 +1128,11 @@ impl *const T { /// let mut out = String::new(); /// while ptr != end_rounded_up { /// unsafe { - /// write!(&mut out, "{}, ", *ptr)?; + /// write!(&mut out, "{}, ", *ptr).unwrap(); /// } /// ptr = ptr.wrapping_add(step); /// } /// assert_eq!(out, "1, 3, 5, "); - /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1222,12 +1206,11 @@ impl *const T { /// let mut out = String::new(); /// while ptr != start_rounded_down { /// unsafe { - /// write!(&mut out, "{}, ", *ptr)?; + /// write!(&mut out, "{}, ", *ptr).unwrap(); /// } /// ptr = ptr.wrapping_sub(step); /// } /// assert_eq!(out, "5, 3, 1, "); - /// # std::fmt::Result::Ok(()) /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1543,21 +1526,6 @@ impl *const [T] { self as *const T } - /// Gets a raw pointer to the underlying array. - /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[unstable(feature = "slice_as_array", issue = "133508")] - #[inline] - #[must_use] - pub const fn as_array(self) -> Option<*const [T; N]> { - if self.len() == N { - let me = self.as_ptr() as *const [T; N]; - Some(me) - } else { - None - } - } - /// Returns a raw pointer to an element or subslice, without doing bounds /// checking. /// @@ -1624,15 +1592,9 @@ impl *const [T] { /// /// [valid]: crate::ptr#safety /// [allocated object]: crate::ptr#allocated-object - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] + #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_slice<'a>(self) -> Option<&'a [MaybeUninit]> { if self.is_null() { None @@ -1681,7 +1643,7 @@ impl *const [T; N] { } } -/// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method. +// Equality for pointers #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *const T { #[inline] @@ -1691,11 +1653,10 @@ impl PartialEq for *const T { } } -/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *const T {} -/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. +// Comparison for pointers #[stable(feature = "rust1", since = "1.0.0")] impl Ord for *const T { #[inline] @@ -1711,7 +1672,6 @@ impl 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 PartialOrd for *const T { #[inline] diff --git a/core/src/ptr/metadata.rs b/core/src/ptr/metadata.rs index e93b5658e2436..5f20cb2ee7206 100644 --- a/core/src/ptr/metadata.rs +++ b/core/src/ptr/metadata.rs @@ -53,8 +53,7 @@ use crate::ptr::NonNull; /// /// [`to_raw_parts`]: *const::to_raw_parts #[lang = "pointee_trait"] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_deny_explicit_impl(implement_via_object = false)] pub trait Pointee { /// The type for metadata in pointers and references to `Self`. #[lang = "metadata_type"] @@ -93,6 +92,7 @@ pub trait Thin = Pointee; /// /// assert_eq!(std::ptr::metadata("foo"), 3_usize); /// ``` +#[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[inline] pub const fn metadata(ptr: *const T) -> ::Metadata { ptr_metadata(ptr) @@ -106,6 +106,7 @@ pub const fn metadata(ptr: *const T) -> ::Metadata { /// /// [`slice::from_raw_parts`]: crate::slice::from_raw_parts #[unstable(feature = "ptr_metadata", issue = "81513")] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[inline] pub const fn from_raw_parts( data_pointer: *const impl Thin, @@ -119,6 +120,7 @@ pub const fn from_raw_parts( /// /// See the documentation of [`from_raw_parts`] for more details. #[unstable(feature = "ptr_metadata", issue = "81513")] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[inline] pub const fn from_raw_parts_mut( data_pointer: *mut impl Thin, diff --git a/core/src/ptr/mod.rs b/core/src/ptr/mod.rs index e1348552b65c3..805edddfe6312 100644 --- a/core/src/ptr/mod.rs +++ b/core/src/ptr/mod.rs @@ -15,8 +15,8 @@ //! The precise rules for validity are not determined yet. The guarantees that are //! provided at this point are very minimal: //! -//! * For memory accesses of [size zero][zst], *every* pointer is valid, including the [null] -//! pointer. The following points are only concerned with non-zero-sized accesses. +//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer. +//! The following points are only concerned with non-zero-sized accesses. //! * A [null] pointer is *never* valid. //! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer be //! *dereferenceable*. The [provenance] of the pointer is used to determine which [allocated @@ -84,7 +84,7 @@ // ^ we use this term instead of saying that the produced reference must // be valid, as the validity of a reference is easily confused for the // validity of the thing it refers to, and while the two concepts are -// closely related, they are not identical. +// closly related, they are not identical. //! //! These rules apply even if the result is unused! //! (The part about being initialized is not yet fully decided, but until @@ -200,7 +200,7 @@ //! //! But it *is* still sound to: //! -//! * Create a pointer without provenance from just an address (see [`without_provenance`]). Such a +//! * Create a pointer without provenance from just an address (see [`ptr::dangling`]). Such a //! pointer cannot be used for memory accesses (except for zero-sized accesses). This can still be //! useful for sentinel values like `null` *or* to represent a tagged pointer that will never be //! dereferenceable. In general, it is always sound for an integer to pretend to be a pointer "for @@ -314,8 +314,8 @@ //! } //! ``` //! -//! (Yes, if you've been using [`AtomicUsize`] for pointers in concurrent datastructures, you should -//! be using [`AtomicPtr`] instead. If that messes up the way you atomically manipulate pointers, +//! (Yes, if you've been using AtomicUsize for pointers in concurrent datastructures, you should +//! be using AtomicPtr instead. If that messes up the way you atomically manipulate pointers, //! we would like to know why, and what needs to be done to fix it.) //! //! Situations where a valid pointer *must* be created from just an address, such as baremetal code @@ -381,8 +381,7 @@ //! [`with_addr`]: pointer::with_addr //! [`map_addr`]: pointer::map_addr //! [`addr`]: pointer::addr -//! [`AtomicUsize`]: crate::sync::atomic::AtomicUsize -//! [`AtomicPtr`]: crate::sync::atomic::AtomicPtr +//! [`ptr::dangling`]: core::ptr::dangling //! [`expose_provenance`]: pointer::expose_provenance //! [`with_exposed_provenance`]: with_exposed_provenance //! [Miri]: https://github.com/rust-lang/miri @@ -395,7 +394,6 @@ #![allow(clippy::not_unsafe_ptr_arg_deref)] use crate::cmp::Ordering; -use crate::intrinsics::const_eval_select; use crate::marker::FnPtr; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::{fmt, hash, intrinsics, ub_checks}; @@ -593,10 +591,15 @@ pub const fn null_mut() -> *mut T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[inline(always)] #[must_use] -#[stable(feature = "strict_provenance", since = "1.84.0")] -#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] +#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub const fn without_provenance(addr: usize) -> *const T { - without_provenance_mut(addr) + // An int-to-pointer transmute currently has exactly the intended semantics: it creates a + // pointer without provenance. Note that this is *not* a stable guarantee about transmute + // semantics, it relies on sysroot crates having special status. + // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that + // pointer). + unsafe { mem::transmute(addr) } } /// Creates a new pointer that is dangling, but non-null and well-aligned. @@ -610,10 +613,10 @@ pub const fn without_provenance(addr: usize) -> *const T { /// some other means. #[inline(always)] #[must_use] -#[stable(feature = "strict_provenance", since = "1.84.0")] -#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] +#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub const fn dangling() -> *const T { - dangling_mut() + without_provenance(mem::align_of::()) } /// Creates a pointer with the given address and no [provenance][crate::ptr#provenance]. @@ -631,8 +634,8 @@ pub const fn dangling() -> *const T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[inline(always)] #[must_use] -#[stable(feature = "strict_provenance", since = "1.84.0")] -#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] +#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub const fn without_provenance_mut(addr: usize) -> *mut T { // An int-to-pointer transmute currently has exactly the intended semantics: it creates a // pointer without provenance. Note that this is *not* a stable guarantee about transmute @@ -653,10 +656,10 @@ pub const fn without_provenance_mut(addr: usize) -> *mut T { /// some other means. #[inline(always)] #[must_use] -#[stable(feature = "strict_provenance", since = "1.84.0")] -#[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] +#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub const fn dangling_mut() -> *mut T { - NonNull::dangling().as_ptr() + without_provenance_mut(mem::align_of::()) } /// Converts an address back to a pointer, picking up some previously 'exposed' @@ -692,7 +695,7 @@ pub const fn dangling_mut() -> *mut T { /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. #[must_use] #[inline(always)] -#[stable(feature = "exposed_provenance", since = "1.84.0")] +#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub fn with_exposed_provenance(addr: usize) -> *const T { @@ -732,7 +735,7 @@ pub fn with_exposed_provenance(addr: usize) -> *const T { /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. #[must_use] #[inline(always)] -#[stable(feature = "exposed_provenance", since = "1.84.0")] +#[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead pub fn with_exposed_provenance_mut(addr: usize) -> *mut T { @@ -772,7 +775,7 @@ pub fn with_exposed_provenance_mut(addr: usize) -> *mut T { /// # type T = i32; /// # fn foo() -> T { 42 } /// // The temporary holding the return value of `foo` does *not* have its lifetime extended, -/// // because the surrounding expression involves a function call. +/// // because the surrounding expression involves no function call. /// let p = ptr::from_ref(&foo()); /// unsafe { p.read() }; // UB! Reading from a dangling pointer ⚠️ /// ``` @@ -823,7 +826,7 @@ pub const fn from_ref(r: &T) -> *const T { /// # type T = i32; /// # fn foo() -> T { 42 } /// // The temporary holding the return value of `foo` does *not* have its lifetime extended, -/// // because the surrounding expression involves a function call. +/// // because the surrounding expression involves no function call. /// let p = ptr::from_mut(&mut foo()); /// unsafe { p.write(T::default()) }; // UB! Writing to a dangling pointer ⚠️ /// ``` @@ -1004,7 +1007,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_swap", since = "1.85.0")] +#[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[rustc_diagnostic_item = "ptr_swap"] pub const unsafe fn swap(x: *mut T, y: *mut T) { // Give ourselves some scratch space to work with. @@ -1066,9 +1069,28 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] -#[rustc_const_unstable(feature = "const_swap_nonoverlapping", issue = "133668")] +#[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[rustc_diagnostic_item = "ptr_swap_nonoverlapping"] pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { + #[allow(unused)] + macro_rules! attempt_swap_as_chunks { + ($ChunkTy:ty) => { + if mem::align_of::() >= mem::align_of::<$ChunkTy>() + && mem::size_of::() % mem::size_of::<$ChunkTy>() == 0 + { + let x: *mut $ChunkTy = x.cast(); + let y: *mut $ChunkTy = y.cast(); + let count = count * (mem::size_of::() / mem::size_of::<$ChunkTy>()); + // SAFETY: these are the same bytes that the caller promised were + // ok, just typed as `MaybeUninit`s instead of as `T`s. + // The `if` condition above ensures that we're not violating + // alignment requirements, and that the division is exact so + // that we don't lose any bytes off the end. + return unsafe { swap_nonoverlapping_simple_untyped(x, y, count) }; + } + }; + } + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ @@ -1087,48 +1109,19 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { } ); - const_eval_select!( - @capture[T] { x: *mut T, y: *mut T, count: usize }: - if const { - // At compile-time we want to always copy this in chunks of `T`, to ensure that if there - // are pointers inside `T` we will copy them in one go rather than trying to copy a part - // of a pointer (which would not work). - // SAFETY: Same preconditions as this function - unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } - } else { - macro_rules! attempt_swap_as_chunks { - ($ChunkTy:ty) => { - if mem::align_of::() >= mem::align_of::<$ChunkTy>() - && mem::size_of::() % mem::size_of::<$ChunkTy>() == 0 - { - let x: *mut $ChunkTy = x.cast(); - let y: *mut $ChunkTy = y.cast(); - let count = count * (mem::size_of::() / mem::size_of::<$ChunkTy>()); - // SAFETY: these are the same bytes that the caller promised were - // ok, just typed as `MaybeUninit`s instead of as `T`s. - // The `if` condition above ensures that we're not violating - // alignment requirements, and that the division is exact so - // that we don't lose any bytes off the end. - return unsafe { swap_nonoverlapping_simple_untyped(x, y, count) }; - } - }; - } - - // Split up the slice into small power-of-two-sized chunks that LLVM is able - // to vectorize (unless it's a special type with more-than-pointer alignment, - // because we don't want to pessimize things like slices of SIMD vectors.) - if mem::align_of::() <= mem::size_of::() - && (!mem::size_of::().is_power_of_two() - || mem::size_of::() > mem::size_of::() * 2) - { - attempt_swap_as_chunks!(usize); - attempt_swap_as_chunks!(u8); - } + // Split up the slice into small power-of-two-sized chunks that LLVM is able + // to vectorize (unless it's a special type with more-than-pointer alignment, + // because we don't want to pessimize things like slices of SIMD vectors.) + if mem::align_of::() <= mem::size_of::() + && (!mem::size_of::().is_power_of_two() + || mem::size_of::() > mem::size_of::() * 2) + { + attempt_swap_as_chunks!(usize); + attempt_swap_as_chunks!(u8); + } - // SAFETY: Same preconditions as this function - unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } - } - ) + // SAFETY: Same preconditions as this function + unsafe { swap_nonoverlapping_simple_untyped(x, y, count) } } /// Same behavior and safety conditions as [`swap_nonoverlapping`] @@ -1136,6 +1129,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { /// LLVM can vectorize this (at least it can for the power-of-two-sized types /// `swap_nonoverlapping` tries to use) so no need to manually SIMD it. #[inline] +#[rustc_const_unstable(feature = "const_swap", issue = "83163")] const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, count: usize) { let x = x.cast::>(); let y = y.cast::>(); @@ -1398,6 +1392,8 @@ pub const unsafe fn read(src: *const T) -> T { /// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned /// value and the value at `*src` can [violate memory safety][read-ownership]. /// +/// Note that even if `T` has size `0`, the pointer must be non-null. +/// /// [read-ownership]: read#ownership-of-the-returned-value /// [valid]: self#safety /// @@ -1604,6 +1600,8 @@ pub const unsafe fn write(dst: *mut T, src: T) { /// /// * `dst` must be [valid] for writes. /// +/// Note that even if `T` has size `0`, the pointer must be non-null. +/// /// [valid]: self#safety /// /// ## On `packed` structs @@ -2113,6 +2111,7 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// when compiled with optimization: /// /// ``` +/// # #![feature(ptr_fn_addr_eq)] /// let f: fn(i32) -> i32 = |x| x; /// let g: fn(i32) -> i32 = |x| x + 0; // different closure, different body /// let h: fn(u32) -> u32 = |x| x + 0; // different signature too @@ -2137,6 +2136,7 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// # Examples /// /// ``` +/// #![feature(ptr_fn_addr_eq)] /// use std::ptr; /// /// fn a() { println!("a"); } @@ -2145,7 +2145,7 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// ``` /// /// [subtype]: https://doc.rust-lang.org/reference/subtyping.html -#[stable(feature = "ptr_fn_addr_eq", since = "1.85.0")] +#[unstable(feature = "ptr_fn_addr_eq", issue = "129322")] #[inline(always)] #[must_use = "function pointer comparison produces a value"] pub fn fn_addr_eq(f: T, g: U) -> bool { diff --git a/core/src/ptr/mut_ptr.rs b/core/src/ptr/mut_ptr.rs index d1b0104c0fa92..f0204bd0f773d 100644 --- a/core/src/ptr/mut_ptr.rs +++ b/core/src/ptr/mut_ptr.rs @@ -12,17 +12,14 @@ impl *mut T { /// Therefore, two pointers that are null may still not compare equal to /// each other. /// - /// # Panics during const evaluation + /// ## Behavior during const evaluation /// - /// If this method is used during const evaluation, and `self` is a pointer - /// that is offset beyond the bounds of the memory it initially pointed to, - /// then there might not be enough information to determine whether the - /// pointer is null. This is because the absolute address in memory is not - /// known at compile time. If the nullness of the pointer cannot be - /// determined, this method will panic. - /// - /// In-bounds pointers are never null, so the method will never panic for - /// such pointers. + /// When this function is used during const evaluation, it may return `false` for pointers + /// that turn out to be null at runtime. Specifically, when a pointer to some memory + /// is offset beyond its bounds in such a way that the resulting pointer is null, + /// the function will still return `false`. There is no way for CTFE to know + /// the absolute position of that memory, so we cannot tell if the pointer is + /// null or not. /// /// # Examples /// @@ -32,7 +29,7 @@ impl *mut T { /// assert!(!ptr.is_null()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] #[rustc_diagnostic_item = "ptr_is_null"] #[inline] pub const fn is_null(self) -> bool { @@ -97,6 +94,7 @@ impl *mut T { /// // This dereference is UB. The pointer only has provenance for `x` but points to `y`. /// println!("{:?}", unsafe { &*bad }); #[unstable(feature = "set_ptr_value", issue = "75091")] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "ptr_metadata_const", since = "1.83.0"))] #[must_use = "returns a new pointer rather than modifying its argument"] #[inline] pub const fn with_metadata_of(self, meta: *const U) -> *mut U @@ -148,7 +146,7 @@ impl *mut T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline(always)] - #[stable(feature = "strict_provenance", since = "1.84.0")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn addr(self) -> usize { // A pointer-to-integer transmute currently has exactly the right semantics: it returns the // address without exposing the provenance. Note that this is *not* a stable guarantee about @@ -181,7 +179,7 @@ impl *mut T { /// /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut #[inline(always)] - #[stable(feature = "exposed_provenance", since = "1.84.0")] + #[stable(feature = "exposed_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } @@ -199,7 +197,7 @@ impl *mut T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "1.84.0")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn with_addr(self, addr: usize) -> Self { // This should probably be an intrinsic to avoid doing any sort of arithmetic, but // meanwhile, we can implement it with `wrapping_offset`, which preserves the pointer's @@ -218,7 +216,7 @@ impl *mut T { /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "1.84.0")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self { self.with_addr(f(self.addr())) } @@ -246,13 +244,6 @@ impl *mut T { /// When calling this method, you have to ensure that *either* the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null-1 - /// /// # Examples /// /// ``` @@ -280,7 +271,7 @@ impl *mut T { /// } /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const unsafe fn as_ref<'a>(self) -> Option<&'a T> { // SAFETY: the caller must guarantee that `self` is valid for a @@ -337,13 +328,6 @@ impl *mut T { /// Note that because the created reference is to `MaybeUninit`, the /// source pointer can point to uninitialized memory. /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null-1 - /// /// # Examples /// /// ``` @@ -359,6 +343,7 @@ impl *mut T { /// ``` #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] + #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit> where T: Sized, @@ -607,12 +592,6 @@ impl *mut T { /// the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null-1 /// /// # Examples /// @@ -640,7 +619,7 @@ impl *mut T { /// println!("{s:?}"); // It'll print: "[4, 2, 3]". /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] - #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] + #[rustc_const_stable(feature = "const_ptr_is_null", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const unsafe fn as_mut<'a>(self) -> Option<&'a mut T> { // SAFETY: the caller must guarantee that `self` is be valid for @@ -696,15 +675,9 @@ impl *mut T { /// /// When calling this method, you have to ensure that *either* the pointer is null *or* /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null-1 #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] + #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_mut<'a>(self) -> Option<&'a mut MaybeUninit> where T: Sized, @@ -1124,6 +1097,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1594,7 +1568,7 @@ impl *mut T { /// /// [`ptr::swap`]: crate::ptr::swap() #[stable(feature = "pointer_methods", since = "1.26.0")] - #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[inline(always)] pub const unsafe fn swap(self, with: *mut T) where @@ -1617,6 +1591,15 @@ impl *mut T { /// beyond the allocation that the pointer points into. It is up to the caller to ensure that /// the returned offset is correct in all terms other than alignment. /// + /// When this is called during compile-time evaluation (which is unstable), the implementation + /// may return `usize::MAX` in cases where that can never happen at runtime. This is because the + /// actual alignment of pointers is not known yet during compile-time, so an offset with + /// guaranteed alignment can sometimes not be computed. For example, a buffer declared as `[u8; + /// N]` might be allocated at an odd or an even address, but at compile-time this is not yet + /// known, so the execution has to be correct for either choice. It is therefore impossible to + /// find an offset that is guaranteed to be 2-aligned. (This behavior is subject to change, as usual + /// for unstable APIs.) + /// /// # Panics /// /// The function panics if `align` is not a power-of-two. @@ -1777,21 +1760,6 @@ impl *mut [T] { self.len() == 0 } - /// Gets a raw, mutable pointer to the underlying array. - /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[unstable(feature = "slice_as_array", issue = "133508")] - #[inline] - #[must_use] - pub const fn as_mut_array(self) -> Option<*mut [T; N]> { - if self.len() == N { - let me = self.as_mut_ptr() as *mut [T; N]; - Some(me) - } else { - None - } - } - /// Divides one mutable raw slice into two at an index. /// /// The first will contain all indices from `[0, mid)` (excluding @@ -1979,15 +1947,9 @@ impl *mut [T] { /// /// [valid]: crate::ptr#safety /// [allocated object]: crate::ptr#allocated-object - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null-1 #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] + #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_slice<'a>(self) -> Option<&'a [MaybeUninit]> { if self.is_null() { None @@ -2037,15 +1999,9 @@ impl *mut [T] { /// /// [valid]: crate::ptr#safety /// [allocated object]: crate::ptr#allocated-object - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null-1 #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] + #[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")] pub const unsafe fn as_uninit_slice_mut<'a>(self) -> Option<&'a mut [MaybeUninit]> { if self.is_null() { None @@ -2097,7 +2053,7 @@ impl *mut [T; N] { } } -/// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. +// Equality for pointers #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *mut T { #[inline(always)] @@ -2107,11 +2063,9 @@ impl PartialEq for *mut T { } } -/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl 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 Ord for *mut T { #[inline] @@ -2127,7 +2081,6 @@ impl 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 PartialOrd for *mut T { #[inline(always)] diff --git a/core/src/ptr/non_null.rs b/core/src/ptr/non_null.rs index d93069d384edd..b69f8a4b9d3ea 100644 --- a/core/src/ptr/non_null.rs +++ b/core/src/ptr/non_null.rs @@ -7,7 +7,7 @@ use crate::pin::PinCoerceUnsized; use crate::ptr::Unique; use crate::slice::{self, SliceIndex}; use crate::ub_checks::assert_unsafe_precondition; -use crate::{fmt, hash, intrinsics, mem, ptr}; +use crate::{fmt, hash, intrinsics, ptr}; /// `*mut T` but non-zero and [covariant]. /// @@ -69,8 +69,6 @@ use crate::{fmt, hash, intrinsics, mem, ptr}; #[rustc_nonnull_optimization_guaranteed] #[rustc_diagnostic_item = "NonNull"] pub struct NonNull { - // Remember to use `.as_ptr()` instead of `.pointer`, as field projecting to - // this is banned by . pointer: *const T, } @@ -85,20 +83,6 @@ impl !Send for NonNull {} impl !Sync for NonNull {} impl NonNull { - /// Creates a pointer with the given address and no [provenance][crate::ptr#provenance]. - /// - /// For more details, see the equivalent method on a raw pointer, [`ptr::without_provenance_mut`]. - /// - /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. - #[unstable(feature = "nonnull_provenance", issue = "135243")] - #[must_use] - #[inline] - pub const fn without_provenance(addr: NonZero) -> Self { - let pointer = crate::ptr::without_provenance(addr.get()); - // SAFETY: we know `addr` is non-zero. - unsafe { NonNull { pointer } } - } - /// Creates a new `NonNull` that is dangling, but well-aligned. /// /// This is useful for initializing types which lazily allocate, like @@ -123,22 +107,9 @@ impl NonNull { #[must_use] #[inline] pub const fn dangling() -> Self { - let align = crate::ptr::Alignment::of::(); - NonNull::without_provenance(align.as_nonzero()) - } - - /// Converts an address back to a mutable pointer, picking up some previously 'exposed' - /// [provenance][crate::ptr#provenance]. - /// - /// For more details, see the equivalent method on a raw pointer, [`ptr::with_exposed_provenance_mut`]. - /// - /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. - #[unstable(feature = "nonnull_provenance", issue = "135243")] - #[inline] - pub fn with_exposed_provenance(addr: NonZero) -> Self { - // SAFETY: we know `addr` is non-zero. + // SAFETY: ptr::dangling_mut() returns a non-null well-aligned pointer. unsafe { - let ptr = crate::ptr::with_exposed_provenance_mut(addr.get()); + let ptr = crate::ptr::dangling_mut::(); NonNull::new_unchecked(ptr) } } @@ -231,13 +202,6 @@ impl NonNull { /// Creates a new `NonNull` if `ptr` is non-null. /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: ../primitive.pointer.html#method.is_null-1 - /// /// # Examples /// /// ``` @@ -251,7 +215,7 @@ impl NonNull { /// } /// ``` #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_stable(feature = "const_nonnull_new", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_nonnull_new", issue = "93235")] #[inline] pub const fn new(ptr: *mut T) -> Option { if !ptr.is_null() { @@ -309,54 +273,41 @@ impl NonNull { /// Gets the "address" portion of the pointer. /// - /// For more details, see the equivalent method on a raw pointer, [`pointer::addr`]. + /// For more details see the equivalent method on a raw pointer, [`pointer::addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "1.84.0")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn addr(self) -> NonZero { // SAFETY: The pointer is guaranteed by the type to be non-null, // meaning that the address will be non-zero. - unsafe { NonZero::new_unchecked(self.as_ptr().addr()) } - } - - /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in - /// [`with_exposed_provenance`][NonNull::with_exposed_provenance] and returns the "address" portion. - /// - /// For more details, see the equivalent method on a raw pointer, [`pointer::expose_provenance`]. - /// - /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. - #[unstable(feature = "nonnull_provenance", issue = "135243")] - pub fn expose_provenance(self) -> NonZero { - // SAFETY: The pointer is guaranteed by the type to be non-null, - // meaning that the address will be non-zero. - unsafe { NonZero::new_unchecked(self.as_ptr().expose_provenance()) } + unsafe { NonZero::new_unchecked(self.pointer.addr()) } } /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of /// `self`. /// - /// For more details, see the equivalent method on a raw pointer, [`pointer::with_addr`]. + /// For more details see the equivalent method on a raw pointer, [`pointer::with_addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "1.84.0")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn with_addr(self, addr: NonZero) -> Self { // SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero. - unsafe { NonNull::new_unchecked(self.as_ptr().with_addr(addr.get()) as *mut _) } + unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) } } /// Creates a new pointer by mapping `self`'s address to a new one, preserving the /// [provenance][crate::ptr#provenance] of `self`. /// - /// For more details, see the equivalent method on a raw pointer, [`pointer::map_addr`]. + /// For more details see the equivalent method on a raw pointer, [`pointer::map_addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] #[inline] - #[stable(feature = "strict_provenance", since = "1.84.0")] + #[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")] pub fn map_addr(self, f: impl FnOnce(NonZero) -> NonZero) -> Self { self.with_addr(f(self.addr())) } @@ -384,12 +335,7 @@ impl NonNull { #[must_use] #[inline(always)] pub const fn as_ptr(self) -> *mut T { - // This is a transmute for the same reasons as `NonZero::get`. - - // SAFETY: `NonNull` is `transparent` over a `*const T`, and `*const T` - // and `*mut T` have the same layout, so transitively we can transmute - // our `NonNull` to a `*mut T` directly. - unsafe { mem::transmute::(self) } + self.pointer as *mut T } /// Returns a shared reference to the value. If the value may be uninitialized, [`as_uninit_ref`] @@ -538,7 +484,7 @@ impl NonNull { // Additionally safety contract of `offset` guarantees that the resulting pointer is // pointing to an allocation, there can't be an allocation at null, thus it's safe to // construct `NonNull`. - unsafe { NonNull { pointer: intrinsics::offset(self.as_ptr(), count) } } + unsafe { NonNull { pointer: intrinsics::offset(self.pointer, count) } } } /// Calculates the offset from a pointer in bytes. @@ -562,7 +508,7 @@ impl NonNull { // Additionally safety contract of `offset` guarantees that the resulting pointer is // pointing to an allocation, there can't be an allocation at null, thus it's safe to // construct `NonNull`. - unsafe { NonNull { pointer: self.as_ptr().byte_offset(count) } } + unsafe { NonNull { pointer: self.pointer.byte_offset(count) } } } /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). @@ -614,7 +560,7 @@ impl NonNull { // Additionally safety contract of `offset` guarantees that the resulting pointer is // pointing to an allocation, there can't be an allocation at null, thus it's safe to // construct `NonNull`. - unsafe { NonNull { pointer: intrinsics::offset(self.as_ptr(), count) } } + unsafe { NonNull { pointer: intrinsics::offset(self.pointer, count) } } } /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`). @@ -638,7 +584,7 @@ impl NonNull { // Additionally safety contract of `add` guarantees that the resulting pointer is pointing // to an allocation, there can't be an allocation at null, thus it's safe to construct // `NonNull`. - unsafe { NonNull { pointer: self.as_ptr().byte_add(count) } } + unsafe { NonNull { pointer: self.pointer.byte_add(count) } } } /// Subtracts an offset from a pointer (convenience for @@ -683,6 +629,7 @@ impl NonNull { #[must_use = "returns a new pointer rather than modifying its argument"] #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))] pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, @@ -720,7 +667,7 @@ impl NonNull { // Additionally safety contract of `sub` guarantees that the resulting pointer is pointing // to an allocation, there can't be an allocation at null, thus it's safe to construct // `NonNull`. - unsafe { NonNull { pointer: self.as_ptr().byte_sub(count) } } + unsafe { NonNull { pointer: self.pointer.byte_sub(count) } } } /// Calculates the distance between two pointers within the same allocation. The returned value is in @@ -817,7 +764,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `offset_from`. - unsafe { self.as_ptr().offset_from(origin.as_ptr()) } + unsafe { self.pointer.offset_from(origin.pointer) } } /// Calculates the distance between two pointers within the same allocation. The returned value is in @@ -835,7 +782,7 @@ impl NonNull { #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn byte_offset_from(self, origin: NonNull) -> isize { // SAFETY: the caller must uphold the safety contract for `byte_offset_from`. - unsafe { self.as_ptr().byte_offset_from(origin.as_ptr()) } + unsafe { self.pointer.byte_offset_from(origin.pointer) } } // N.B. `wrapping_offset``, `wrapping_add`, etc are not implemented because they can wrap to null @@ -910,7 +857,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `sub_ptr`. - unsafe { self.as_ptr().sub_ptr(subtracted.as_ptr()) } + unsafe { self.pointer.sub_ptr(subtracted.pointer) } } /// Calculates the distance between two pointers within the same allocation, *where it's known that @@ -929,7 +876,7 @@ impl NonNull { #[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")] pub const unsafe fn byte_sub_ptr(self, origin: NonNull) -> usize { // SAFETY: the caller must uphold the safety contract for `byte_sub_ptr`. - unsafe { self.as_ptr().byte_sub_ptr(origin.as_ptr()) } + unsafe { self.pointer.byte_sub_ptr(origin.pointer) } } /// Reads the value from `self` without moving it. This leaves the @@ -947,7 +894,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `read`. - unsafe { ptr::read(self.as_ptr()) } + unsafe { ptr::read(self.pointer) } } /// Performs a volatile read of the value from `self` without moving it. This @@ -968,7 +915,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `read_volatile`. - unsafe { ptr::read_volatile(self.as_ptr()) } + unsafe { ptr::read_volatile(self.pointer) } } /// Reads the value from `self` without moving it. This leaves the @@ -988,7 +935,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `read_unaligned`. - unsafe { ptr::read_unaligned(self.as_ptr()) } + unsafe { ptr::read_unaligned(self.pointer) } } /// Copies `count * size_of` bytes from `self` to `dest`. The source @@ -1008,7 +955,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `copy`. - unsafe { ptr::copy(self.as_ptr(), dest.as_ptr(), count) } + unsafe { ptr::copy(self.pointer, dest.as_ptr(), count) } } /// Copies `count * size_of` bytes from `self` to `dest`. The source @@ -1028,7 +975,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `copy_nonoverlapping`. - unsafe { ptr::copy_nonoverlapping(self.as_ptr(), dest.as_ptr(), count) } + unsafe { ptr::copy_nonoverlapping(self.pointer, dest.as_ptr(), count) } } /// Copies `count * size_of` bytes from `src` to `self`. The source @@ -1048,7 +995,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `copy`. - unsafe { ptr::copy(src.as_ptr(), self.as_ptr(), count) } + unsafe { ptr::copy(src.pointer, self.as_ptr(), count) } } /// Copies `count * size_of` bytes from `src` to `self`. The source @@ -1068,7 +1015,7 @@ impl NonNull { T: Sized, { // SAFETY: the caller must uphold the safety contract for `copy_nonoverlapping`. - unsafe { ptr::copy_nonoverlapping(src.as_ptr(), self.as_ptr(), count) } + unsafe { ptr::copy_nonoverlapping(src.pointer, self.as_ptr(), count) } } /// Executes the destructor (if any) of the pointed-to value. @@ -1186,7 +1133,7 @@ impl NonNull { /// [`ptr::swap`]: crate::ptr::swap() #[inline(always)] #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_swap", issue = "83163")] pub const unsafe fn swap(self, with: NonNull) where T: Sized, @@ -1255,7 +1202,7 @@ impl NonNull { { // SAFETY: `align` has been checked to be a power of 2 above. - unsafe { ptr::align_offset(self.as_ptr(), align) } + unsafe { ptr::align_offset(self.pointer, align) } } } @@ -1283,7 +1230,7 @@ impl NonNull { where T: Sized, { - self.as_ptr().is_aligned() + self.pointer.is_aligned() } /// Returns whether the pointer is aligned to `align`. @@ -1320,7 +1267,7 @@ impl NonNull { #[must_use] #[unstable(feature = "pointer_is_aligned_to", issue = "96284")] pub fn is_aligned_to(self, align: usize) -> bool { - self.as_ptr().is_aligned_to(align) + self.pointer.is_aligned_to(align) } } @@ -1595,9 +1542,6 @@ impl DispatchFromDyn> for NonNull where T: U #[stable(feature = "pin", since = "1.33.0")] unsafe impl PinCoerceUnsized for NonNull {} -#[unstable(feature = "pointer_like_trait", issue = "none")] -impl core::marker::PointerLike for NonNull {} - #[stable(feature = "nonnull", since = "1.25.0")] impl fmt::Debug for NonNull { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/core/src/ptr/unique.rs b/core/src/ptr/unique.rs index 4810ebe01f9bb..a796820a7e468 100644 --- a/core/src/ptr/unique.rs +++ b/core/src/ptr/unique.rs @@ -92,6 +92,7 @@ impl Unique { /// Creates a new `Unique` if `ptr` is non-null. #[inline] + #[rustc_const_unstable(feature = "ptr_internals", issue = "none")] pub const fn new(ptr: *mut T) -> Option { if let Some(pointer) = NonNull::new(ptr) { Some(Unique { pointer, _marker: PhantomData }) diff --git a/core/src/range.rs b/core/src/range.rs index 6a62928873fe8..427526fd14b91 100644 --- a/core/src/range.rs +++ b/core/src/range.rs @@ -48,7 +48,6 @@ 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 { @@ -206,7 +205,6 @@ impl From> for Range { /// 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 { @@ -390,7 +388,6 @@ impl From> for RangeInclusive { /// 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 { diff --git a/core/src/result.rs b/core/src/result.rs index 92b5cba153166..b450123c5aa90 100644 --- a/core/src/result.rs +++ b/core/src/result.rs @@ -520,7 +520,7 @@ use crate::{convert, fmt, hint}; /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). /// /// See the [module documentation](self) for details. -#[doc(search_unbox)] +#[cfg_attr(not(bootstrap), doc(search_unbox))] #[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] #[must_use = "this `Result` may be an `Err` variant, which should be handled"] #[rustc_diagnostic_item = "Result"] @@ -1065,15 +1065,10 @@ impl Result { /// Returns the contained [`Ok`] value, consuming the `self` value. /// /// Because this function may panic, its use is generally discouraged. - /// Panics are meant for unrecoverable errors, and - /// [may abort the entire program][panic-abort]. - /// - /// Instead, prefer to use [the `?` (try) operator][try-operator], or pattern matching - /// to handle the [`Err`] case explicitly, or call [`unwrap_or`], - /// [`unwrap_or_else`], or [`unwrap_or_default`]. + /// Instead, prefer to use pattern matching and handle the [`Err`] + /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or + /// [`unwrap_or_default`]. /// - /// [panic-abort]: https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html - /// [try-operator]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator /// [`unwrap_or`]: Result::unwrap_or /// [`unwrap_or_else`]: Result::unwrap_or_else /// [`unwrap_or_default`]: Result::unwrap_or_default diff --git a/core/src/slice/ascii.rs b/core/src/slice/ascii.rs index 51b25fa40e3d9..17ad4fd8f677f 100644 --- a/core/src/slice/ascii.rs +++ b/core/src/slice/ascii.rs @@ -3,9 +3,8 @@ use core::ascii::EscapeDefault; use crate::fmt::{self, Write}; -#[cfg(not(all(target_arch = "x86_64", target_feature = "sse2")))] use crate::intrinsics::const_eval_select; -use crate::{ascii, iter, ops}; +use crate::{ascii, iter, mem, ops}; #[cfg(not(test))] impl [u8] { @@ -89,7 +88,7 @@ impl [u8] { /// /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn make_ascii_uppercase(&mut self) { // FIXME(const-hack): We would like to simply iterate using `for` loops but this isn't currently allowed in constant expressions. @@ -111,7 +110,7 @@ impl [u8] { /// /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn make_ascii_lowercase(&mut self) { // FIXME(const-hack): We would like to simply iterate using `for` loops but this isn't currently allowed in constant expressions. @@ -329,6 +328,14 @@ impl<'a> fmt::Debug for EscapeAscii<'a> { } } +/// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed +/// from `../str/mod.rs`, which does something similar for utf8 validation. +#[inline] +const fn contains_nonascii(v: usize) -> bool { + const NONASCII_MASK: usize = usize::repeat_u8(0x80); + (NONASCII_MASK & v) != 0 +} + /// ASCII test *without* the chunk-at-a-time optimizations. /// /// This is carefully structured to produce nice small code -- it's smaller in @@ -359,7 +366,6 @@ pub const fn is_ascii_simple(mut bytes: &[u8]) -> bool { /// /// If any of these loads produces something for which `contains_nonascii` /// (above) returns true, then we know the answer is false. -#[cfg(not(all(target_arch = "x86_64", target_feature = "sse2")))] #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] // fallback impl has same behavior const fn is_ascii(s: &[u8]) -> bool { @@ -370,14 +376,7 @@ const fn is_ascii(s: &[u8]) -> bool { if const { is_ascii_simple(s) } else { - /// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed - /// from `../str/mod.rs`, which does something similar for utf8 validation. - const fn contains_nonascii(v: usize) -> bool { - const NONASCII_MASK: usize = usize::repeat_u8(0x80); - (NONASCII_MASK & v) != 0 - } - - const USIZE_SIZE: usize = size_of::(); + const USIZE_SIZE: usize = mem::size_of::(); let len = s.len(); let align_offset = s.as_ptr().align_offset(USIZE_SIZE); @@ -387,7 +386,7 @@ const fn is_ascii(s: &[u8]) -> bool { // // We also do this for architectures where `size_of::()` isn't // sufficient alignment for `usize`, because it's a weird edge case. - if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < align_of::() { + if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem::align_of::() { return is_ascii_simple(s); } @@ -421,7 +420,7 @@ const fn is_ascii(s: &[u8]) -> bool { // have alignment information it should have given a `usize::MAX` for // `align_offset` earlier, sending things through the scalar path instead of // this one, so this check should pass if it's reachable. - debug_assert!(word_ptr.is_aligned_to(align_of::())); + debug_assert!(word_ptr.is_aligned_to(mem::align_of::())); // Read subsequent words until the last aligned word, excluding the last // aligned word by itself to be done in tail check later, to ensure that @@ -456,48 +455,3 @@ const fn is_ascii(s: &[u8]) -> bool { } ) } - -/// ASCII test optimized to use the `pmovmskb` instruction available on `x86-64` -/// platforms. -/// -/// Other platforms are not likely to benefit from this code structure, so they -/// use SWAR techniques to test for ASCII in `usize`-sized chunks. -#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] -#[inline] -const fn is_ascii(bytes: &[u8]) -> bool { - // Process chunks of 32 bytes at a time in the fast path to enable - // auto-vectorization and use of `pmovmskb`. Two 128-bit vector registers - // can be OR'd together and then the resulting vector can be tested for - // non-ASCII bytes. - const CHUNK_SIZE: usize = 32; - - let mut i = 0; - - while i + CHUNK_SIZE <= bytes.len() { - let chunk_end = i + CHUNK_SIZE; - - // Get LLVM to produce a `pmovmskb` instruction on x86-64 which - // creates a mask from the most significant bit of each byte. - // ASCII bytes are less than 128 (0x80), so their most significant - // bit is unset. - let mut count = 0; - while i < chunk_end { - count += bytes[i].is_ascii() as u8; - i += 1; - } - - // All bytes should be <= 127 so count is equal to chunk size. - if count != CHUNK_SIZE as u8 { - return false; - } - } - - // Process the remaining `bytes.len() % N` bytes. - let mut is_ascii = true; - while i < bytes.len() { - is_ascii &= bytes[i].is_ascii(); - i += 1; - } - - is_ascii -} diff --git a/core/src/slice/iter.rs b/core/src/slice/iter.rs index a687ed7129dc8..c5746157d01b2 100644 --- a/core/src/slice/iter.rs +++ b/core/src/slice/iter.rs @@ -46,19 +46,13 @@ impl<'a, T> IntoIterator for &'a mut [T] { /// Basic usage: /// /// ``` -/// // First, we need a slice to call the `iter` method on: +/// // First, we declare a type which has `iter` method to get the `Iter` struct (`&[usize]` here): /// let slice = &[1, 2, 3]; /// -/// // Then we call `iter` on the slice to get the `Iter` iterator, -/// // and iterate over it: +/// // Then, we iterate over it: /// for element in slice.iter() { /// println!("{element}"); /// } -/// -/// // This for loop actually already works without calling `iter`: -/// for element in slice { -/// println!("{element}"); -/// } /// ``` /// /// [`iter`]: slice::iter @@ -74,7 +68,7 @@ pub struct Iter<'a, T: 'a> { ptr: NonNull, /// For non-ZSTs, the non-null pointer to the past-the-end element. /// - /// For ZSTs, this is `ptr::without_provenance_mut(len)`. + /// For ZSTs, this is `ptr::dangling(len)`. end_or_len: *const T, _marker: PhantomData<&'a T>, } @@ -107,29 +101,27 @@ impl<'a, T> Iter<'a, T> { /// Views the underlying data as a subslice of the original data. /// + /// This has the same lifetime as the original slice, and so the + /// iterator can continue to be used while this exists. + /// /// # Examples /// /// Basic usage: /// /// ``` - /// // First, we need a slice to call the `iter` method on: + /// // First, we declare a type which has the `iter` method to get the `Iter` + /// // struct (`&[usize]` here): /// let slice = &[1, 2, 3]; /// - /// // Then we call `iter` on the slice to get the `Iter` iterator: + /// // Then, we get the iterator: /// let mut iter = slice.iter(); - /// // Here `as_slice` still returns the whole slice, so this prints "[1, 2, 3]": + /// // So if we print what `as_slice` method returns here, we have "[1, 2, 3]": /// println!("{:?}", iter.as_slice()); /// - /// // Now, we call the `next` method to remove the first element from the iterator: + /// // Next, we move to the second element of the slice: /// iter.next(); - /// // Here the iterator does not contain the first element of the slice any more, - /// // so `as_slice` only returns the last two elements of the slice, - /// // and so this prints "[2, 3]": + /// // Now `as_slice` returns "[2, 3]": /// println!("{:?}", iter.as_slice()); - /// - /// // The underlying slice has not been modified and still contains three elements, - /// // so this prints "[1, 2, 3]": - /// println!("{:?}", slice); /// ``` #[must_use] #[stable(feature = "iter_to_slice", since = "1.4.0")] @@ -174,11 +166,11 @@ impl AsRef<[T]> for Iter<'_, T> { /// Basic usage: /// /// ``` -/// // First, we need a slice to call the `iter_mut` method on: -/// let slice = &mut [1, 2, 3]; +/// // First, we declare a type which has `iter_mut` method to get the `IterMut` +/// // struct (`&[usize]` here): +/// let mut slice = &mut [1, 2, 3]; /// -/// // Then we call `iter_mut` on the slice to get the `IterMut` iterator, -/// // iterate over it and increment each element value: +/// // Then, we iterate over it and increment each element value: /// for element in slice.iter_mut() { /// *element += 1; /// } @@ -255,21 +247,28 @@ impl<'a, T> IterMut<'a, T> { /// Basic usage: /// /// ``` - /// // First, we need a slice to call the `iter_mut` method on: + /// // First, we declare a type which has `iter_mut` method to get the `IterMut` + /// // struct (`&[usize]` here): /// let mut slice = &mut [1, 2, 3]; /// - /// // Then we call `iter_mut` on the slice to get the `IterMut` struct: - /// let mut iter = slice.iter_mut(); - /// // Now, we call the `next` method to remove the first element of the iterator, - /// // unwrap and dereference what we get from `next` and increase its value by 1: - /// *iter.next().unwrap() += 1; - /// // Here the iterator does not contain the first element of the slice any more, - /// // so `into_slice` only returns the last two elements of the slice, - /// // and so this prints "[2, 3]": - /// println!("{:?}", iter.into_slice()); - /// // The underlying slice still contains three elements, but its first element - /// // was increased by 1, so this prints "[2, 2, 3]": - /// println!("{:?}", slice); + /// { + /// // Then, we get the iterator: + /// let mut iter = slice.iter_mut(); + /// // We move to next element: + /// iter.next(); + /// // So if we print what `into_slice` method returns here, we have "[2, 3]": + /// println!("{:?}", iter.into_slice()); + /// } + /// + /// // Now let's modify a value of the slice: + /// { + /// // First we get back the iterator: + /// let mut iter = slice.iter_mut(); + /// // We change the value of the first element of the slice returned by the `next` method: + /// *iter.next().unwrap() += 1; + /// } + /// // Now slice is "[2, 2, 3]": + /// println!("{slice:?}"); /// ``` #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "iter_to_slice", since = "1.4.0")] @@ -282,30 +281,25 @@ impl<'a, T> IterMut<'a, T> { /// Views the underlying data as a subslice of the original data. /// + /// To avoid creating `&mut [T]` references that alias, the returned slice + /// borrows its lifetime from the iterator the method is applied on. + /// /// # Examples /// /// Basic usage: /// /// ``` - /// // First, we need a slice to call the `iter_mut` method on: - /// let slice = &mut [1, 2, 3]; + /// let mut slice: &mut [usize] = &mut [1, 2, 3]; /// - /// // Then we call `iter_mut` on the slice to get the `IterMut` iterator: + /// // First, we get the iterator: /// let mut iter = slice.iter_mut(); - /// // Here `as_slice` still returns the whole slice, so this prints "[1, 2, 3]": - /// println!("{:?}", iter.as_slice()); + /// // So if we check what the `as_slice` method returns here, we have "[1, 2, 3]": + /// assert_eq!(iter.as_slice(), &[1, 2, 3]); /// - /// // Now, we call the `next` method to remove the first element from the iterator - /// // and increment its value: - /// *iter.next().unwrap() += 1; - /// // Here the iterator does not contain the first element of the slice any more, - /// // so `as_slice` only returns the last two elements of the slice, - /// // and so this prints "[2, 3]": - /// println!("{:?}", iter.as_slice()); - /// - /// // The underlying slice still contains three elements, but its first element - /// // was increased by 1, so this prints "[2, 2, 3]": - /// println!("{:?}", slice); + /// // Next, we move to the second element of the slice: + /// iter.next(); + /// // Now `as_slice` returns "[2, 3]": + /// assert_eq!(iter.as_slice(), &[2, 3]); /// ``` #[must_use] #[stable(feature = "slice_iter_mut_as_slice", since = "1.53.0")] @@ -316,6 +310,9 @@ impl<'a, T> IterMut<'a, T> { /// Views the underlying data as a mutable subslice of the original data. /// + /// To avoid creating `&mut [T]` references that alias, the returned slice + /// borrows its lifetime from the iterator the method is applied on. + /// /// # Examples /// /// Basic usage: diff --git a/core/src/slice/memchr.rs b/core/src/slice/memchr.rs index 98db7aaf53321..339adad1b17bf 100644 --- a/core/src/slice/memchr.rs +++ b/core/src/slice/memchr.rs @@ -16,6 +16,7 @@ const USIZE_BYTES: usize = mem::size_of::(); /// bytes where the borrow propagated all the way to the most significant /// bit." #[inline] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_memchr", since = "1.65.0"))] const fn contains_zero_byte(x: usize) -> bool { x.wrapping_sub(LO_USIZE) & !x & HI_USIZE != 0 } @@ -23,6 +24,7 @@ const fn contains_zero_byte(x: usize) -> bool { /// Returns the first index matching the byte `x` in `text`. #[inline] #[must_use] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_memchr", since = "1.65.0"))] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. if text.len() < 2 * USIZE_BYTES { @@ -33,6 +35,7 @@ pub const fn memchr(x: u8, text: &[u8]) -> Option { } #[inline] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_memchr", since = "1.65.0"))] const fn memchr_naive(x: u8, text: &[u8]) -> Option { let mut i = 0; @@ -49,6 +52,7 @@ const fn memchr_naive(x: u8, text: &[u8]) -> Option { } #[rustc_allow_const_fn_unstable(const_eval_select)] // fallback impl has same behavior +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_memchr", since = "1.65.0"))] const fn memchr_aligned(x: u8, text: &[u8]) -> Option { // The runtime version behaves the same as the compiletime version, it's // just more optimized. diff --git a/core/src/slice/mod.rs b/core/src/slice/mod.rs index fe9d7c10db28c..c855f963771ed 100644 --- a/core/src/slice/mod.rs +++ b/core/src/slice/mod.rs @@ -7,14 +7,13 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::cmp::Ordering::{self, Equal, Greater, Less}; -use crate::intrinsics::{exact_div, unchecked_sub}; +use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub}; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; -use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive}; -use crate::panic::const_panic; +use crate::ops::{Bound, OneSidedRange, Range, RangeBounds}; use crate::simd::{self, Simd}; use crate::ub_checks::assert_unsafe_precondition; -use crate::{fmt, hint, ptr, range, slice}; +use crate::{fmt, hint, ptr, slice}; #[unstable( feature = "slice_internals", @@ -83,12 +82,14 @@ 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) -> Option<(Direction, usize)> { - 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)?), + 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!(), }) } @@ -734,7 +735,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")] #[rustc_never_returns_null_ptr] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[inline(always)] #[must_use] pub const fn as_ptr(&self) -> *const T { @@ -765,7 +766,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[rustc_never_returns_null_ptr] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[inline(always)] #[must_use] pub const fn as_mut_ptr(&mut self) -> *mut T { @@ -854,42 +855,6 @@ impl [T] { start..end } - /// Gets a reference to the underlying array. - /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[unstable(feature = "slice_as_array", issue = "133508")] - #[inline] - #[must_use] - pub const fn as_array(&self) -> Option<&[T; N]> { - if self.len() == N { - let ptr = self.as_ptr() as *const [T; N]; - - // SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length. - let me = unsafe { &*ptr }; - Some(me) - } else { - None - } - } - - /// Gets a mutable reference to the slice's underlying array. - /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[unstable(feature = "slice_as_array", issue = "133508")] - #[inline] - #[must_use] - pub const fn as_mut_array(&mut self) -> Option<&mut [T; N]> { - if self.len() == N { - let ptr = self.as_mut_ptr() as *mut [T; N]; - - // SAFETY: The underlying array of a slice can be reinterpreted as an actual array `[T; N]` if `N` is not greater than the slice's length. - let me = unsafe { &mut *ptr }; - Some(me) - } else { - None - } - } - /// Swaps two elements in the slice. /// /// If `a` equals to `b`, it's guaranteed that elements won't change value. @@ -911,7 +876,7 @@ impl [T] { /// assert!(v == ["a", "b", "e", "d", "c"]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[inline] #[track_caller] pub const fn swap(&mut self, a: usize, b: usize) { @@ -956,7 +921,7 @@ impl [T] { /// [`swap`]: slice::swap /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[unstable(feature = "slice_swap_unchecked", issue = "88539")] - #[rustc_const_unstable(feature = "slice_swap_unchecked", issue = "88539")] + #[rustc_const_unstable(feature = "const_swap", issue = "83163")] pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) { assert_unsafe_precondition!( check_library_ub, @@ -985,9 +950,8 @@ impl [T] { /// assert!(v == [3, 2, 1]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_slice_reverse", issue = "135120")] #[inline] - pub const fn reverse(&mut self) { + pub fn reverse(&mut self) { let half_len = self.len() / 2; let Range { start, end } = self.as_mut_ptr_range(); @@ -1010,7 +974,7 @@ impl [T] { revswap(front_half, back_half, half_len); #[inline] - const fn revswap(a: &mut [T], b: &mut [T], n: usize) { + fn revswap(a: &mut [T], b: &mut [T], n: usize) { debug_assert!(a.len() == n); debug_assert!(b.len() == n); @@ -1018,8 +982,7 @@ impl [T] { // this check tells LLVM that the indexing below is // in-bounds. Then after inlining -- once the actual // lengths of the slices are known -- it's removed. - let (a, _) = a.split_at_mut(n); - let (b, _) = b.split_at_mut(n); + let (a, b) = (&mut a[..n], &mut b[..n]); let mut i = 0; while i < n { @@ -1076,7 +1039,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `size` is zero. + /// Panics if `size` is 0. /// /// # Examples /// @@ -1097,15 +1060,10 @@ impl [T] { /// assert!(iter.next().is_none()); /// ``` /// - /// 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 + /// 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: /// ``` /// use std::cell::Cell; /// @@ -1137,7 +1095,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is zero. + /// Panics if `chunk_size` is 0. /// /// # Examples /// @@ -1172,7 +1130,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is zero. + /// Panics if `chunk_size` is 0. /// /// # Examples /// @@ -1214,7 +1172,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is zero. + /// Panics if `chunk_size` is 0. /// /// # Examples /// @@ -1253,7 +1211,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is zero. + /// Panics if `chunk_size` is 0. /// /// # Examples /// @@ -1330,7 +1288,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time + /// Panics if `N` is 0. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1376,7 +1334,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time + /// Panics if `N` is 0. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1414,7 +1372,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time + /// Panics if `N` is 0. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1490,7 +1448,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time + /// Panics if `N` is 0. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1531,7 +1489,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time + /// Panics if `N` is 0. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1575,7 +1533,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time + /// Panics if `N` is 0. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1610,7 +1568,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time + /// Panics if `N` is 0. This check will most probably get changed to a compile time /// error before this method gets stabilized. /// /// # Examples @@ -1646,7 +1604,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is zero. + /// Panics if `chunk_size` is 0. /// /// # Examples /// @@ -1681,7 +1639,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is zero. + /// Panics if `chunk_size` is 0. /// /// # Examples /// @@ -1724,7 +1682,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is zero. + /// Panics if `chunk_size` is 0. /// /// # Examples /// @@ -1764,7 +1722,7 @@ impl [T] { /// /// # Panics /// - /// Panics if `chunk_size` is zero. + /// Panics if `chunk_size` is 0. /// /// # Examples /// @@ -1888,23 +1846,23 @@ impl [T] { /// # Examples /// /// ``` - /// let v = ['a', 'b', 'c']; + /// let v = [1, 2, 3, 4, 5, 6]; /// /// { /// let (left, right) = v.split_at(0); /// assert_eq!(left, []); - /// assert_eq!(right, ['a', 'b', 'c']); + /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); /// } /// /// { /// let (left, right) = v.split_at(2); - /// assert_eq!(left, ['a', 'b']); - /// assert_eq!(right, ['c']); + /// assert_eq!(left, [1, 2]); + /// assert_eq!(right, [3, 4, 5, 6]); /// } /// /// { - /// let (left, right) = v.split_at(3); - /// assert_eq!(left, ['a', 'b', 'c']); + /// let (left, right) = v.split_at(6); + /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); /// assert_eq!(right, []); /// } /// ``` @@ -1974,23 +1932,23 @@ impl [T] { /// # Examples /// /// ``` - /// let v = ['a', 'b', 'c']; + /// let v = [1, 2, 3, 4, 5, 6]; /// /// unsafe { /// let (left, right) = v.split_at_unchecked(0); /// assert_eq!(left, []); - /// assert_eq!(right, ['a', 'b', 'c']); + /// assert_eq!(right, [1, 2, 3, 4, 5, 6]); /// } /// /// unsafe { /// let (left, right) = v.split_at_unchecked(2); - /// assert_eq!(left, ['a', 'b']); - /// assert_eq!(right, ['c']); + /// assert_eq!(left, [1, 2]); + /// assert_eq!(right, [3, 4, 5, 6]); /// } /// /// unsafe { - /// let (left, right) = v.split_at_unchecked(3); - /// assert_eq!(left, ['a', 'b', 'c']); + /// let (left, right) = v.split_at_unchecked(6); + /// assert_eq!(left, [1, 2, 3, 4, 5, 6]); /// assert_eq!(right, []); /// } /// ``` @@ -2840,7 +2798,7 @@ impl [T] { // Binary search interacts poorly with branch prediction, so force // the compiler to use conditional moves if supported by the target // architecture. - base = (cmp == Greater).select_unpredictable(base, mid); + base = select_unpredictable(cmp == Greater, base, mid); // This is imprecise in the case where `size` is odd and the // comparison returns Greater: the mid element still gets included @@ -3074,21 +3032,19 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } - /// Reorders the slice such that the element at `index` is at a sort-order position. All - /// elements before `index` will be `<=` to this value, and all elements after will be `>=` to - /// it. - /// - /// This reordering is unstable (i.e. any element that compares equal to the nth element may end - /// up at that position), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This - /// function is also known as "kth element" in other libraries. + /// Reorders the slice such that the element at `index` after the reordering is at its final + /// sorted position. /// - /// Returns a triple that partitions the reordered slice: + /// This reordering has the additional property that any value at position `i < index` will be + /// less than or equal to any value at a position `j > index`. Additionally, this reordering is + /// unstable (i.e. any number of equal elements may end up at position `index`), in-place (i.e. + /// does not allocate), and runs in *O*(*n*) time. This function is also known as "kth element" + /// in other libraries. /// - /// * The unsorted subslice before `index`, whose elements all satisfy `x <= self[index]`. - /// - /// * The element at `index`. - /// - /// * The unsorted subslice after `index`, whose elements all satisfy `x >= self[index]`. + /// It returns a triplet of the following from the reordered slice: the subslice prior to + /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in + /// those two subslices will respectively all be less-than-or-equal-to and + /// greater-than-or-equal-to the value of the element at `index`. /// /// # Current implementation /// @@ -3101,7 +3057,7 @@ impl [T] { /// /// # Panics /// - /// Panics when `index >= len()`, and so always panics on empty slices. + /// Panics when `index >= len()`, meaning it always panics on empty slices. /// /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// @@ -3110,7 +3066,8 @@ impl [T] { /// ``` /// let mut v = [-5i32, 4, 2, -3, 1]; /// - /// // Find the items `<=` to the median, the median itself, and the items `>=` to it. + /// // Find the items less than or equal to the median, the median, and greater than or equal to + /// // the median. /// let (lesser, median, greater) = v.select_nth_unstable(2); /// /// assert!(lesser == [-3, -5] || lesser == [-5, -3]); @@ -3136,23 +3093,19 @@ impl [T] { sort::select::partition_at_index(self, index, T::lt) } - /// Reorders the slice with a comparator function such that the element at `index` is at a - /// sort-order position. All elements before `index` will be `<=` to this value, and all - /// elements after will be `>=` to it, according to the comparator function. + /// Reorders the slice with a comparator function such that the element at `index` after the + /// reordering is at its final sorted position. /// - /// This reordering is unstable (i.e. any element that compares equal to the nth element may end - /// up at that position), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// This reordering has the additional property that any value at position `i < index` will be + /// less than or equal to any value at a position `j > index` using the comparator function. + /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at + /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This /// function is also known as "kth element" in other libraries. /// - /// Returns a triple partitioning the reordered slice: - /// - /// * The unsorted subslice before `index`, whose elements all satisfy - /// `compare(x, self[index]).is_le()`. - /// - /// * The element at `index`. - /// - /// * The unsorted subslice after `index`, whose elements all satisfy - /// `compare(x, self[index]).is_ge()`. + /// It returns a triplet of the following from the slice reordered according to the provided + /// comparator function: the subslice prior to `index`, the element at `index`, and the subslice + /// after `index`; accordingly, the values in those two subslices will respectively all be + /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. /// /// # Current implementation /// @@ -3165,7 +3118,7 @@ impl [T] { /// /// # Panics /// - /// Panics when `index >= len()`, and so always panics on empty slices. + /// Panics when `index >= len()`, meaning it always panics on empty slices. /// /// May panic if `compare` does not implement a [total order]. /// @@ -3174,13 +3127,13 @@ impl [T] { /// ``` /// let mut v = [-5i32, 4, 2, -3, 1]; /// - /// // Find the items `>=` to the median, the median itself, and the items `<=` to it, by using - /// // a reversed comparator. - /// let (before, median, after) = v.select_nth_unstable_by(2, |a, b| b.cmp(a)); + /// // Find the items less than or equal to the median, the median, and greater than or equal to + /// // the median as if the slice were sorted in descending order. + /// let (lesser, median, greater) = v.select_nth_unstable_by(2, |a, b| b.cmp(a)); /// - /// assert!(before == [4, 2] || before == [2, 4]); + /// assert!(lesser == [4, 2] || lesser == [2, 4]); /// assert_eq!(median, &mut 1); - /// assert!(after == [-3, -5] || after == [-5, -3]); + /// assert!(greater == [-3, -5] || greater == [-5, -3]); /// /// // We are only guaranteed the slice will be one of the following, based on the way we sort /// // about the specified index. @@ -3205,21 +3158,19 @@ impl [T] { sort::select::partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less) } - /// Reorders the slice with a key extraction function such that the element at `index` is at a - /// sort-order position. All elements before `index` will have keys `<=` to the key at `index`, - /// and all elements after will have keys `>=` to it. + /// Reorders the slice with a key extraction function such that the element at `index` after the + /// reordering is at its final sorted position. /// - /// This reordering is unstable (i.e. any element that compares equal to the nth element may end - /// up at that position), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// This reordering has the additional property that any value at position `i < index` will be + /// less than or equal to any value at a position `j > index` using the key extraction function. + /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at + /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This /// function is also known as "kth element" in other libraries. /// - /// Returns a triple partitioning the reordered slice: - /// - /// * The unsorted subslice before `index`, whose elements all satisfy `f(x) <= f(self[index])`. - /// - /// * The element at `index`. - /// - /// * The unsorted subslice after `index`, whose elements all satisfy `f(x) >= f(self[index])`. + /// It returns a triplet of the following from the slice reordered according to the provided key + /// extraction function: the subslice prior to `index`, the element at `index`, and the subslice + /// after `index`; accordingly, the values in those two subslices will respectively all be + /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. /// /// # Current implementation /// @@ -3241,8 +3192,8 @@ impl [T] { /// ``` /// let mut v = [-5i32, 4, 1, -3, 2]; /// - /// // Find the items `<=` to the absolute median, the absolute median itself, and the items - /// // `>=` to it. + /// // Find the items less than or equal to the median, the median, and greater than or equal to + /// // the median as if the slice were sorted according to absolute value. /// let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs()); /// /// assert!(lesser == [1, 2] || lesser == [2, 1]); @@ -3713,11 +3664,9 @@ impl [T] { /// [`clone_from_slice`]: slice::clone_from_slice /// [`split_at_mut`]: slice::split_at_mut #[doc(alias = "memcpy")] - #[inline] #[stable(feature = "copy_from_slice", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_copy_from_slice", issue = "131415")] #[track_caller] - pub const fn copy_from_slice(&mut self, src: &[T]) + pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy, { @@ -3726,13 +3675,11 @@ impl [T] { #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] - const fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { - const_panic!( - "copy_from_slice: source slice length does not match destination slice length", - "copy_from_slice: source slice length ({src_len}) does not match destination slice length ({dst_len})", - src_len: usize, - dst_len: usize, - ) + fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { + panic!( + "source slice length ({}) does not match destination slice length ({})", + src_len, dst_len, + ); } if self.len() != src.len() { @@ -4292,25 +4239,25 @@ impl [T] { /// /// # Examples /// - /// Splitting off the first three elements of a slice: + /// Taking the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut first_three = slice.split_off(..3).unwrap(); + /// let mut first_three = slice.take(..3).unwrap(); /// /// assert_eq!(slice, &['d']); /// assert_eq!(first_three, &['a', 'b', 'c']); /// ``` /// - /// Splitting off the last two elements of a slice: + /// Taking the last two elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut tail = slice.split_off(2..).unwrap(); + /// let mut tail = slice.take(2..).unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(tail, &['c', 'd']); @@ -4323,19 +4270,16 @@ impl [T] { /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.split_off(5..)); - /// assert_eq!(None, slice.split_off(..5)); - /// assert_eq!(None, slice.split_off(..=4)); + /// assert_eq!(None, slice.take(5..)); + /// assert_eq!(None, slice.take(..5)); + /// assert_eq!(None, slice.take(..=4)); /// let expected: &[char] = &['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.split_off(..4)); + /// assert_eq!(Some(expected), slice.take(..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 split_off<'a, R: OneSidedRange>( - self: &mut &'a Self, - range: R, - ) -> Option<&'a Self> { + pub fn take<'a, R: OneSidedRange>(self: &mut &'a Self, range: R) -> Option<&'a Self> { let (direction, split_index) = split_point_of(range)?; if split_index > self.len() { return None; @@ -4364,13 +4308,13 @@ impl [T] { /// /// # Examples /// - /// Splitting off the first three elements of a slice: + /// Taking the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut first_three = slice.split_off_mut(..3).unwrap(); + /// let mut first_three = slice.take_mut(..3).unwrap(); /// /// assert_eq!(slice, &mut ['d']); /// assert_eq!(first_three, &mut ['a', 'b', 'c']); @@ -4382,7 +4326,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut tail = slice.split_off_mut(2..).unwrap(); + /// let mut tail = slice.take_mut(2..).unwrap(); /// /// assert_eq!(slice, &mut ['a', 'b']); /// assert_eq!(tail, &mut ['c', 'd']); @@ -4395,16 +4339,16 @@ impl [T] { /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.split_off_mut(5..)); - /// assert_eq!(None, slice.split_off_mut(..5)); - /// assert_eq!(None, slice.split_off_mut(..=4)); + /// assert_eq!(None, slice.take_mut(5..)); + /// assert_eq!(None, slice.take_mut(..5)); + /// assert_eq!(None, slice.take_mut(..=4)); /// let expected: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.split_off_mut(..4)); + /// assert_eq!(Some(expected), slice.take_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 split_off_mut<'a, R: OneSidedRange>( + pub fn take_mut<'a, R: OneSidedRange>( self: &mut &'a mut Self, range: R, ) -> Option<&'a mut Self> { @@ -4436,14 +4380,14 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let first = slice.split_off_first().unwrap(); + /// let first = slice.take_first().unwrap(); /// /// assert_eq!(slice, &['b', 'c']); /// assert_eq!(first, &'a'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn split_off_first<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn take_first<'a>(self: &mut &'a Self) -> Option<&'a T> { let (first, rem) = self.split_first()?; *self = rem; Some(first) @@ -4460,7 +4404,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let first = slice.split_off_first_mut().unwrap(); + /// let first = slice.take_first_mut().unwrap(); /// *first = 'd'; /// /// assert_eq!(slice, &['b', 'c']); @@ -4468,7 +4412,7 @@ impl [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn split_off_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn take_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) @@ -4485,14 +4429,14 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let last = slice.split_off_last().unwrap(); + /// let last = slice.take_last().unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(last, &'c'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn split_off_last<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn take_last<'a>(self: &mut &'a Self) -> Option<&'a T> { let (last, rem) = self.split_last()?; *self = rem; Some(last) @@ -4509,7 +4453,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let last = slice.split_off_last_mut().unwrap(); + /// let last = slice.take_last_mut().unwrap(); /// *last = 'd'; /// /// assert_eq!(slice, &['a', 'b']); @@ -4517,7 +4461,7 @@ impl [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn split_off_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn take_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) @@ -4525,12 +4469,6 @@ impl [T] { /// Returns mutable references to many indices at once, without doing any checks. /// - /// An index can be either a `usize`, a [`Range`] or a [`RangeInclusive`]. Note - /// that this method takes an array, so all indices must be of the same type. - /// If passed an array of `usize`s this method gives back an array of mutable references - /// to single elements, while if passed an array of ranges it gives back an array of - /// mutable references to slices. - /// /// For a safe alternative see [`get_many_mut`]. /// /// # Safety @@ -4551,49 +4489,30 @@ impl [T] { /// *b *= 100; /// } /// assert_eq!(x, &[10, 2, 400]); - /// - /// unsafe { - /// let [a, b] = x.get_many_unchecked_mut([0..1, 1..3]); - /// a[0] = 8; - /// b[0] = 88; - /// b[1] = 888; - /// } - /// assert_eq!(x, &[8, 88, 888]); - /// - /// unsafe { - /// let [a, b] = x.get_many_unchecked_mut([1..=2, 0..=0]); - /// a[0] = 11; - /// a[1] = 111; - /// b[0] = 1; - /// } - /// assert_eq!(x, &[1, 11, 111]); /// ``` /// /// [`get_many_mut`]: slice::get_many_mut /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html #[unstable(feature = "get_many_mut", issue = "104642")] #[inline] - pub unsafe fn get_many_unchecked_mut( + pub unsafe fn get_many_unchecked_mut( &mut self, - indices: [I; N], - ) -> [&mut I::Output; N] - where - I: GetManyMutIndex + SliceIndex, - { + indices: [usize; N], + ) -> [&mut T; N] { // NB: This implementation is written as it is because any variation of // `indices.map(|i| self.get_unchecked_mut(i))` would make miri unhappy, // or generate worse code otherwise. This is also why we need to go // through a raw pointer here. let slice: *mut [T] = self; - let mut arr: mem::MaybeUninit<[&mut I::Output; N]> = mem::MaybeUninit::uninit(); + let mut arr: mem::MaybeUninit<[&mut T; N]> = mem::MaybeUninit::uninit(); let arr_ptr = arr.as_mut_ptr(); // SAFETY: We expect `indices` to contain disjunct values that are // in bounds of `self`. unsafe { for i in 0..N { - let idx = indices.get_unchecked(i).clone(); - arr_ptr.cast::<&mut I::Output>().add(i).write(&mut *slice.get_unchecked_mut(idx)); + let idx = *indices.get_unchecked(i); + *(*arr_ptr).get_unchecked_mut(i) = &mut *slice.get_unchecked_mut(idx); } arr.assume_init() } @@ -4601,18 +4520,8 @@ impl [T] { /// Returns mutable references to many indices at once. /// - /// An index can be either a `usize`, a [`Range`] or a [`RangeInclusive`]. Note - /// that this method takes an array, so all indices must be of the same type. - /// If passed an array of `usize`s this method gives back an array of mutable references - /// to single elements, while if passed an array of ranges it gives back an array of - /// mutable references to slices. - /// - /// Returns an error if any index is out-of-bounds, or if there are overlapping indices. - /// An empty range is not considered to overlap if it is located at the beginning or at - /// the end of another range, but is considered to overlap if it is located in the middle. - /// - /// This method does a O(n^2) check to check that there are no overlapping indices, so be careful - /// when passing many indices. + /// Returns an error if any index is out-of-bounds, or if the same index was + /// passed more than once. /// /// # Examples /// @@ -4625,31 +4534,16 @@ impl [T] { /// *b = 612; /// } /// assert_eq!(v, &[413, 2, 612]); - /// - /// if let Ok([a, b]) = v.get_many_mut([0..1, 1..3]) { - /// a[0] = 8; - /// b[0] = 88; - /// b[1] = 888; - /// } - /// assert_eq!(v, &[8, 88, 888]); - /// - /// if let Ok([a, b]) = v.get_many_mut([1..=2, 0..=0]) { - /// a[0] = 11; - /// a[1] = 111; - /// b[0] = 1; - /// } - /// assert_eq!(v, &[1, 11, 111]); /// ``` #[unstable(feature = "get_many_mut", issue = "104642")] #[inline] - pub fn get_many_mut( + pub fn get_many_mut( &mut self, - indices: [I; N], - ) -> Result<[&mut I::Output; N], GetManyMutError> - where - I: GetManyMutIndex + SliceIndex, - { - get_many_check_valid(&indices, self.len())?; + indices: [usize; N], + ) -> Result<[&mut T; N], GetManyMutError> { + if !get_many_check_valid(&indices, self.len()) { + return Err(GetManyMutError { _private: () }); + } // SAFETY: The `get_many_check_valid()` call checked that all indices // are disjunct and in bounds. unsafe { Ok(self.get_many_unchecked_mut(indices)) } @@ -4657,7 +4551,7 @@ impl [T] { /// Returns the index that an element reference points to. /// - /// Returns `None` if `element` does not point to the start of an element within the slice. + /// Returns `None` if `element` does not point within the slice or if it points between elements. /// /// This method is useful for extending slice iterators like [`slice::split`]. /// @@ -4677,9 +4571,9 @@ impl [T] { /// let num = &nums[2]; /// /// assert_eq!(num, &1); - /// assert_eq!(nums.element_offset(num), Some(2)); + /// assert_eq!(nums.elem_offset(num), Some(2)); /// ``` - /// Returning `None` with an unaligned element: + /// Returning `None` with an in-between element: /// ``` /// #![feature(substr_range)] /// @@ -4692,12 +4586,12 @@ impl [T] { /// assert_eq!(ok_elm, &[0, 1]); /// assert_eq!(weird_elm, &[1, 2]); /// - /// assert_eq!(arr.element_offset(ok_elm), Some(0)); // Points to element 0 - /// assert_eq!(arr.element_offset(weird_elm), None); // Points between element 0 and 1 + /// assert_eq!(arr.elem_offset(ok_elm), Some(0)); // Points to element 0 + /// assert_eq!(arr.elem_offset(weird_elm), None); // Points between element 0 and 1 /// ``` #[must_use] #[unstable(feature = "substr_range", issue = "126769")] - pub fn element_offset(&self, element: &T) -> Option { + pub fn elem_offset(&self, element: &T) -> Option { if T::IS_ZST { panic!("elements are zero-sized"); } @@ -4718,8 +4612,7 @@ impl [T] { /// Returns the range of indices that a subslice points to. /// - /// Returns `None` if `subslice` does not point within the slice or if it is not aligned with the - /// elements in the slice. + /// Returns `None` if `subslice` does not point within the slice or if it points between elements. /// /// This method **does not compare elements**. Instead, this method finds the location in the slice that /// `subslice` was obtained from. To find the index of a subslice via comparison, instead use @@ -4837,8 +4730,7 @@ impl [[T; N]] { /// assert_eq!(array, [[6, 7, 8], [9, 10, 11], [12, 13, 14]]); /// ``` #[stable(feature = "slice_flatten", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_slice_flatten", issue = "95629")] - pub const fn as_flattened_mut(&mut self) -> &mut [T] { + pub fn as_flattened_mut(&mut self) -> &mut [T] { let len = if T::IS_ZST { self.len().checked_mul(N).expect("slice len overflow") } else { @@ -4993,167 +4885,51 @@ impl SlicePattern for [T; N] { /// /// This will do `binomial(N + 1, 2) = N * (N + 1) / 2 = 0, 1, 3, 6, 10, ..` /// comparison operations. -#[inline] -fn get_many_check_valid( - indices: &[I; N], - len: usize, -) -> Result<(), GetManyMutError> { +fn get_many_check_valid(indices: &[usize; N], len: usize) -> bool { // NB: The optimizer should inline the loops into a sequence // of instructions without additional branching. - for (i, idx) in indices.iter().enumerate() { - if !idx.is_in_bounds(len) { - return Err(GetManyMutError::IndexOutOfBounds); - } - for idx2 in &indices[..i] { - if idx.is_overlapping(idx2) { - return Err(GetManyMutError::OverlappingIndices); - } + let mut valid = true; + for (i, &idx) in indices.iter().enumerate() { + valid &= idx < len; + for &idx2 in &indices[..i] { + valid &= idx != idx2; } } - Ok(()) + valid } -/// The error type returned by [`get_many_mut`][`slice::get_many_mut`]. +/// The error type returned by [`get_many_mut`][`slice::get_many_mut`]. /// /// It indicates one of two possible errors: /// - An index is out-of-bounds. -/// - The same index appeared multiple times in the array -/// (or different but overlapping indices when ranges are provided). +/// - The same index appeared multiple times in the array. /// /// # Examples /// /// ``` /// #![feature(get_many_mut)] -/// use std::slice::GetManyMutError; /// /// let v = &mut [1, 2, 3]; -/// assert_eq!(v.get_many_mut([0, 999]), Err(GetManyMutError::IndexOutOfBounds)); -/// assert_eq!(v.get_many_mut([1, 1]), Err(GetManyMutError::OverlappingIndices)); +/// assert!(v.get_many_mut([0, 999]).is_err()); +/// assert!(v.get_many_mut([1, 1]).is_err()); /// ``` #[unstable(feature = "get_many_mut", issue = "104642")] -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum GetManyMutError { - /// An index provided was out-of-bounds for the slice. - IndexOutOfBounds, - /// Two indices provided were overlapping. - OverlappingIndices, +// NB: The N here is there to be forward-compatible with adding more details +// to the error type at a later point +pub struct GetManyMutError { + _private: (), } #[unstable(feature = "get_many_mut", issue = "104642")] -impl fmt::Display for GetManyMutError { +impl fmt::Debug for GetManyMutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let msg = match self { - GetManyMutError::IndexOutOfBounds => "an index is out of bounds", - GetManyMutError::OverlappingIndices => "there were overlapping indices", - }; - fmt::Display::fmt(msg, f) - } -} - -mod private_get_many_mut_index { - use super::{Range, RangeInclusive, range}; - - #[unstable(feature = "get_many_mut_helpers", issue = "none")] - pub trait Sealed {} - - #[unstable(feature = "get_many_mut_helpers", issue = "none")] - impl Sealed for usize {} - #[unstable(feature = "get_many_mut_helpers", issue = "none")] - impl Sealed for Range {} - #[unstable(feature = "get_many_mut_helpers", issue = "none")] - impl Sealed for RangeInclusive {} - #[unstable(feature = "get_many_mut_helpers", issue = "none")] - impl Sealed for range::Range {} - #[unstable(feature = "get_many_mut_helpers", issue = "none")] - impl Sealed for range::RangeInclusive {} -} - -/// A helper trait for `<[T]>::get_many_mut()`. -/// -/// # Safety -/// -/// If `is_in_bounds()` returns `true` and `is_overlapping()` returns `false`, -/// it must be safe to index the slice with the indices. -#[unstable(feature = "get_many_mut_helpers", issue = "none")] -pub unsafe trait GetManyMutIndex: Clone + private_get_many_mut_index::Sealed { - /// Returns `true` if `self` is in bounds for `len` slice elements. - #[unstable(feature = "get_many_mut_helpers", issue = "none")] - fn is_in_bounds(&self, len: usize) -> bool; - - /// Returns `true` if `self` overlaps with `other`. - /// - /// Note that we don't consider zero-length ranges to overlap at the beginning or the end, - /// but do consider them to overlap in the middle. - #[unstable(feature = "get_many_mut_helpers", issue = "none")] - fn is_overlapping(&self, other: &Self) -> bool; -} - -#[unstable(feature = "get_many_mut_helpers", issue = "none")] -// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for usize { - #[inline] - fn is_in_bounds(&self, len: usize) -> bool { - *self < len - } - - #[inline] - fn is_overlapping(&self, other: &Self) -> bool { - *self == *other - } -} - -#[unstable(feature = "get_many_mut_helpers", issue = "none")] -// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for Range { - #[inline] - fn is_in_bounds(&self, len: usize) -> bool { - (self.start <= self.end) & (self.end <= len) - } - - #[inline] - fn is_overlapping(&self, other: &Self) -> bool { - (self.start < other.end) & (other.start < self.end) + f.debug_struct("GetManyMutError").finish_non_exhaustive() } } -#[unstable(feature = "get_many_mut_helpers", issue = "none")] -// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for RangeInclusive { - #[inline] - fn is_in_bounds(&self, len: usize) -> bool { - (self.start <= self.end) & (self.end < len) - } - - #[inline] - fn is_overlapping(&self, other: &Self) -> bool { - (self.start <= other.end) & (other.start <= self.end) - } -} - -#[unstable(feature = "get_many_mut_helpers", issue = "none")] -// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for range::Range { - #[inline] - fn is_in_bounds(&self, len: usize) -> bool { - Range::from(*self).is_in_bounds(len) - } - - #[inline] - fn is_overlapping(&self, other: &Self) -> bool { - Range::from(*self).is_overlapping(&Range::from(*other)) - } -} - -#[unstable(feature = "get_many_mut_helpers", issue = "none")] -// SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for range::RangeInclusive { - #[inline] - fn is_in_bounds(&self, len: usize) -> bool { - RangeInclusive::from(*self).is_in_bounds(len) - } - - #[inline] - fn is_overlapping(&self, other: &Self) -> bool { - RangeInclusive::from(*self).is_overlapping(&RangeInclusive::from(*other)) +#[unstable(feature = "get_many_mut", issue = "104642")] +impl fmt::Display for GetManyMutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt("an index is out of bounds or appeared multiple times in the array", f) } } diff --git a/core/src/slice/rotate.rs b/core/src/slice/rotate.rs index 5d5ee4c7b6240..1e4865a7caad9 100644 --- a/core/src/slice/rotate.rs +++ b/core/src/slice/rotate.rs @@ -1,8 +1,6 @@ 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. @@ -10,82 +8,14 @@ type BufType = [usize; 32]; /// # Safety /// /// The specified range must be valid for reading and writing. -#[inline] -pub(super) unsafe fn ptr_rotate(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::() / mem::size_of::() - { - // 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::() > 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. /// -/// # Safety +/// # Algorithm /// -/// The specified range must be valid for reading and writing. -#[inline] -unsafe fn ptr_rotate_memmove(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: +/// 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: /// ```text /// left = 10, right = 6 /// the `^` indicates an element in its final place @@ -109,104 +39,17 @@ unsafe fn ptr_rotate_memmove(left: usize, mid: *mut T, right: usize) { /// `gcd(left + right, right)` value). The end result is that all elements are finalized once and /// only once. /// -/// 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. -/// -/// # Safety +/// 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. /// -/// The specified range must be valid for reading and writing. -#[inline] -unsafe fn ptr_rotate_gcd(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. +/// 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 +/// 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. /// -/// /// /// ```text /// left = 11, right = 4 /// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3] @@ -217,14 +60,144 @@ unsafe fn ptr_rotate_gcd(left: usize, mid: *mut T, right: usize) { /// we cannot swap any more, but a smaller rotation problem is left to solve /// ``` /// when `left < right` the swapping happens from the left instead. -/// -/// # Safety -/// -/// The specified range must be valid for reading and writing. -#[inline] -unsafe fn ptr_rotate_swap(mut left: usize, mut mid: *mut T, mut right: usize) { +pub unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) { + type BufType = [usize; 32]; + if T::IS_ZST { + return; + } loop { - if left >= right { + // 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::() > 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::() / mem::size_of::() + { + // 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 { // 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 @@ -260,8 +233,5 @@ unsafe fn ptr_rotate_swap(mut left: usize, mut mid: *mut T, mut right: usize) } } } - if (right == 0) || (left == 0) { - return; - } } } diff --git a/core/src/slice/sort/stable/drift.rs b/core/src/slice/sort/stable/drift.rs index cf1df1e91a50d..644e75a4581e9 100644 --- a/core/src/slice/sort/stable/drift.rs +++ b/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() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` otherwise the implementation may abort. +/// worst-case complexity. `scratch.len()` must be at least `max(v.len() / 2, +/// MIN_SMALL_SORT_SCRATCH_LEN)` otherwise the implementation may abort. /// Fully ascending and descending inputs will be sorted with exactly N - 1 /// comparisons. /// diff --git a/core/src/slice/sort/stable/mod.rs b/core/src/slice/sort/stable/mod.rs index 3ff2e71fd05bc..7adcc83b818d1 100644 --- a/core/src/slice/sort/stable/mod.rs +++ b/core/src/slice/sort/stable/mod.rs @@ -41,8 +41,6 @@ pub fn sort bool, BufT: BufGuard>(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! { @@ -93,26 +91,16 @@ fn driftsort_main bool, BufT: BufGuard>(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 - 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 + // 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 // 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::(); let len = v.len(); - let alloc_len = cmp::max( - cmp::max(len - len / 2, cmp::min(len, max_full_alloc)), - SMALL_SORT_GENERAL_SCRATCH_LEN, - ); + let alloc_len = + cmp::max(cmp::max(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/core/src/slice/sort/stable/quicksort.rs b/core/src/slice/sort/stable/quicksort.rs index 630c6ff907703..0c8308bfce00e 100644 --- a/core/src/slice/sort/stable/quicksort.rs +++ b/core/src/slice/sort/stable/quicksort.rs @@ -7,8 +7,6 @@ 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/core/src/str/converts.rs b/core/src/str/converts.rs index de68f80aa0c8e..c7bae42765f4e 100644 --- a/core/src/str/converts.rs +++ b/core/src/str/converts.rs @@ -47,11 +47,10 @@ use crate::{mem, ptr}; /// // 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)?; +/// // We know these bytes are valid, so just use `unwrap()`. +/// let sparkle_heart = str::from_utf8(&sparkle_heart).unwrap(); /// /// assert_eq!("💖", sparkle_heart); -/// # Ok::<_, str::Utf8Error>(()) /// ``` /// /// Incorrect bytes: diff --git a/core/src/str/lossy.rs b/core/src/str/lossy.rs index ed2cefc59a51c..e7677c8317a9f 100644 --- a/core/src/str/lossy.rs +++ b/core/src/str/lossy.rs @@ -8,7 +8,7 @@ impl [u8] { /// Creates an iterator over the contiguous valid UTF-8 ranges of this /// slice, and the non-UTF-8 fragments in between. /// - /// See the [`Utf8Chunk`] type for documentation of the items yielded by this iterator. + /// See the [`Utf8Chunk`] type for documenation of the items yielded by this iterator. /// /// # Examples /// @@ -150,7 +150,7 @@ impl fmt::Debug for Debug<'_> { /// If you want a simple conversion from UTF-8 byte slices to string slices, /// [`from_utf8`] is easier to use. /// -/// See the [`Utf8Chunk`] type for documentation of the items yielded by this iterator. +/// See the [`Utf8Chunk`] type for documenation of the items yielded by this iterator. /// /// [byteslice]: slice /// [`from_utf8`]: super::from_utf8 diff --git a/core/src/str/mod.rs b/core/src/str/mod.rs index 5b258a7c844fe..4629b770cb46d 100644 --- a/core/src/str/mod.rs +++ b/core/src/str/mod.rs @@ -160,182 +160,6 @@ 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. /// @@ -549,7 +373,7 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "rustc_str_as_ptr", since = "1.32.0")] #[rustc_never_returns_null_ptr] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[must_use] #[inline(always)] pub const fn as_ptr(&self) -> *const u8 { @@ -567,7 +391,7 @@ impl str { #[stable(feature = "str_as_mut_ptr", since = "1.36.0")] #[rustc_const_stable(feature = "const_str_as_mut", since = "1.83.0")] #[rustc_never_returns_null_ptr] - #[rustc_as_ptr] + #[cfg_attr(not(bootstrap), rustc_as_ptr)] #[must_use] #[inline(always)] pub const fn as_mut_ptr(&mut self) -> *mut u8 { @@ -1284,8 +1108,7 @@ impl str { LinesAny(self.lines()) } - /// Returns an iterator of `u16` over the string encoded - /// as native endian UTF-16 (without byte-order mark). + /// Returns an iterator of `u16` over the string encoded as UTF-16. /// /// # Examples /// @@ -2680,7 +2503,7 @@ impl str { /// assert_eq!("GRüßE, JüRGEN ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn make_ascii_uppercase(&mut self) { // SAFETY: changing ASCII letters only does not invalidate UTF-8. @@ -2708,7 +2531,7 @@ impl str { /// assert_eq!("grÜße, jÜrgen ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[rustc_const_stable(feature = "const_make_ascii", since = "1.84.0")] + #[rustc_const_stable(feature = "const_make_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn make_ascii_lowercase(&mut self) { // SAFETY: changing ASCII letters only does not invalidate UTF-8. diff --git a/core/src/sync/atomic.rs b/core/src/sync/atomic.rs index 73180bde54aa9..7f2a5424787f7 100644 --- a/core/src/sync/atomic.rs +++ b/core/src/sync/atomic.rs @@ -86,7 +86,7 @@ //! // This is fine: `join` synchronizes the code in a way such that the atomic //! // store happens-before the non-atomic write. //! let handle = s.spawn(|| atomic.store(1, Ordering::Relaxed)); // atomic store -//! handle.join().expect("thread won't panic"); // synchronize +//! handle.join().unwrap(); // synchronize //! s.spawn(|| unsafe { atomic.as_ptr().write(2) }); // non-atomic write //! }); //! @@ -103,7 +103,7 @@ //! // This is fine: `join` synchronizes the code in a way such that //! // the 1-byte store happens-before the 2-byte store. //! let handle = s.spawn(|| atomic.store(1, Ordering::Relaxed)); -//! handle.join().expect("thread won't panic"); +//! handle.join().unwrap(); //! s.spawn(|| unsafe { //! let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic); //! differently_sized.store(2, Ordering::Relaxed); @@ -469,7 +469,7 @@ impl AtomicBool { /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses #[stable(feature = "atomic_from_ptr", since = "1.75.0")] - #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")] + #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn from_ptr<'a>(ptr: *mut bool) -> &'a AtomicBool { // SAFETY: guaranteed by the caller unsafe { &*ptr.cast() } @@ -716,12 +716,6 @@ 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. @@ -1170,7 +1164,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]. /// @@ -1209,125 +1203,6 @@ 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, - ) -> Result { - // 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")] @@ -1389,7 +1264,7 @@ impl AtomicPtr { /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses #[stable(feature = "atomic_from_ptr", since = "1.75.0")] - #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")] + #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn from_ptr<'a>(ptr: *mut *mut T) -> &'a AtomicPtr { // SAFETY: guaranteed by the caller unsafe { &*ptr.cast() } @@ -1657,12 +1532,6 @@ impl AtomicPtr { /// 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. @@ -1815,7 +1684,7 @@ impl AtomicPtr { /// /// # 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]. /// @@ -1863,137 +1732,6 @@ impl AtomicPtr { } 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. @@ -2525,7 +2263,7 @@ macro_rules! atomic_int { /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses #[stable(feature = "atomic_from_ptr", since = "1.75.0")] - #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "1.84.0")] + #[rustc_const_stable(feature = "const_atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn from_ptr<'a>(ptr: *mut $int_type) -> &'a $atomic_type { // SAFETY: guaranteed by the caller unsafe { &*ptr.cast() } @@ -2559,7 +2297,7 @@ macro_rules! atomic_int { $int_type, no = [ "**Note:** This function is only available on targets where `", - stringify!($atomic_type), "` has the same alignment as `", stringify!($int_type), "`." + stringify!($int_type), "` has an alignment of ", $align, " bytes." ], }] /// @@ -2783,12 +2521,6 @@ 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. @@ -3143,7 +2875,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. @@ -3181,127 +2913,6 @@ 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 @@ -4116,33 +3727,33 @@ pub fn fence(order: Ordering) { /// /// # Examples /// -/// 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). +/// 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. /// /// ``` -/// use std::sync::atomic::AtomicBool; +/// use std::sync::atomic::{AtomicBool, AtomicUsize}; /// use std::sync::atomic::Ordering; /// use std::sync::atomic::compiler_fence; /// -/// static mut IMPORTANT_VARIABLE: usize = 0; +/// static IMPORTANT_VARIABLE: AtomicUsize = AtomicUsize::new(0); /// static IS_READY: AtomicBool = AtomicBool::new(false); /// /// fn main() { -/// unsafe { IMPORTANT_VARIABLE = 42 }; -/// // Marks earlier writes as being released with future relaxed stores. +/// IMPORTANT_VARIABLE.store(42, Ordering::Relaxed); +/// // prevent earlier writes from being moved beyond this point /// compiler_fence(Ordering::Release); /// IS_READY.store(true, Ordering::Relaxed); /// } /// /// fn signal_handler() { /// if IS_READY.load(Ordering::Relaxed) { -/// // Acquires writes that were released with relaxed stores that we read from. -/// compiler_fence(Ordering::Acquire); -/// assert_eq!(unsafe { IMPORTANT_VARIABLE }, 42); +/// assert_eq!(IMPORTANT_VARIABLE.load(Ordering::Relaxed), 42); /// } /// } /// ``` diff --git a/core/src/task/wake.rs b/core/src/task/wake.rs index 4c51ca0a5e437..34673707f010a 100644 --- a/core/src/task/wake.rs +++ b/core/src/task/wake.rs @@ -60,7 +60,7 @@ impl RawWaker { RawWaker { data, vtable } } - #[stable(feature = "noop_waker", since = "1.85.0")] + #[unstable(feature = "noop_waker", issue = "98286")] const NOOP: RawWaker = { const VTABLE: RawWakerVTable = RawWakerVTable::new( // Cloning just returns a new no-op raw waker @@ -283,6 +283,7 @@ impl fmt::Debug for Context<'_> { /// # Examples /// ``` /// #![feature(local_waker)] +/// #![feature(noop_waker)] /// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; /// use std::future::Future; /// @@ -318,11 +319,12 @@ impl<'a> ContextBuilder<'a> { /// Creates a ContextBuilder from a Waker. #[inline] #[unstable(feature = "local_waker", issue = "118959")] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_waker", since = "1.82.0"))] pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; Self { - waker, + waker: waker, local_waker, ext: ExtData::None(()), _marker: PhantomData, @@ -371,6 +373,7 @@ impl<'a> ContextBuilder<'a> { /// Builds the `Context`. #[inline] #[unstable(feature = "local_waker", issue = "118959")] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_waker", since = "1.82.0"))] pub const fn build(self) -> Context<'a> { let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self; Context { waker, local_waker, ext: AssertUnwindSafe(ext), _marker, _marker2 } @@ -554,6 +557,8 @@ impl Waker { /// # Examples /// /// ``` + /// #![feature(noop_waker)] + /// /// use std::future::Future; /// use std::task; /// @@ -564,8 +569,7 @@ impl Waker { /// ``` #[inline] #[must_use] - #[stable(feature = "noop_waker", since = "1.85.0")] - #[rustc_const_stable(feature = "noop_waker", since = "1.85.0")] + #[unstable(feature = "noop_waker", issue = "98286")] pub const fn noop() -> &'static Waker { const WAKER: &Waker = &Waker { waker: RawWaker::NOOP }; WAKER @@ -848,6 +852,8 @@ impl LocalWaker { /// /// ``` /// #![feature(local_waker)] + /// #![feature(noop_waker)] + /// /// use std::future::Future; /// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; /// @@ -860,7 +866,7 @@ impl LocalWaker { /// ``` #[inline] #[must_use] - #[unstable(feature = "local_waker", issue = "118959")] + #[unstable(feature = "noop_waker", issue = "98286")] pub const fn noop() -> &'static LocalWaker { const WAKER: &LocalWaker = &LocalWaker { waker: RawWaker::NOOP }; WAKER diff --git a/core/src/time.rs b/core/src/time.rs index 22bd46c567eaa..2d93148bac09f 100644 --- a/core/src/time.rs +++ b/core/src/time.rs @@ -21,7 +21,6 @@ use crate::fmt; use crate::iter::Sum; -use crate::num::niche_types::Nanoseconds; use crate::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; const NANOS_PER_SEC: u32 = 1_000_000_000; @@ -38,6 +37,24 @@ const HOURS_PER_DAY: u64 = 24; #[unstable(feature = "duration_units", issue = "120301")] const DAYS_PER_WEEK: u64 = 7; +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +#[rustc_layout_scalar_valid_range_end(999_999_999)] +struct Nanoseconds(u32); + +impl Nanoseconds { + // SAFETY: 0 is within the valid range + const ZERO: Self = unsafe { Nanoseconds(0) }; +} + +impl Default for Nanoseconds { + #[inline] + fn default() -> Self { + Self::ZERO + } +} + /// A `Duration` type to represent a span of time, typically used for system /// timeouts. /// @@ -194,14 +211,14 @@ impl Duration { pub const fn new(secs: u64, nanos: u32) -> Duration { if nanos < NANOS_PER_SEC { // SAFETY: nanos < NANOS_PER_SEC, therefore nanos is within the valid range - Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } + Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } } else { let secs = secs .checked_add((nanos / NANOS_PER_SEC) as u64) .expect("overflow in Duration::new"); let nanos = nanos % NANOS_PER_SEC; // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range - Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } + Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } } } @@ -246,7 +263,7 @@ impl Duration { let subsec_millis = (millis % MILLIS_PER_SEC) as u32; // SAFETY: (x % 1_000) * 1_000_000 < 1_000_000_000 // => x % 1_000 < 1_000 - let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_millis * NANOS_PER_MILLI) }; + let subsec_nanos = unsafe { Nanoseconds(subsec_millis * NANOS_PER_MILLI) }; Duration { secs, nanos: subsec_nanos } } @@ -272,7 +289,7 @@ impl Duration { let subsec_micros = (micros % MICROS_PER_SEC) as u32; // SAFETY: (x % 1_000_000) * 1_000 < 1_000_000_000 // => x % 1_000_000 < 1_000_000 - let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_micros * NANOS_PER_MICRO) }; + let subsec_nanos = unsafe { Nanoseconds(subsec_micros * NANOS_PER_MICRO) }; Duration { secs, nanos: subsec_nanos } } @@ -303,7 +320,7 @@ impl Duration { let secs = nanos / NANOS_PER_SEC; let subsec_nanos = (nanos % NANOS_PER_SEC) as u32; // SAFETY: x % 1_000_000_000 < 1_000_000_000 - let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_nanos) }; + let subsec_nanos = unsafe { Nanoseconds(subsec_nanos) }; Duration { secs, nanos: subsec_nanos } } @@ -441,7 +458,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")] #[inline] pub const fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos.as_inner() == 0 + self.secs == 0 && self.nanos.0 == 0 } /// Returns the number of _whole_ seconds contained by this `Duration`. @@ -492,7 +509,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_millis(&self) -> u32 { - self.nanos.as_inner() / NANOS_PER_MILLI + self.nanos.0 / NANOS_PER_MILLI } /// Returns the fractional part of this `Duration`, in whole microseconds. @@ -515,7 +532,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_micros(&self) -> u32 { - self.nanos.as_inner() / NANOS_PER_MICRO + self.nanos.0 / NANOS_PER_MICRO } /// Returns the fractional part of this `Duration`, in nanoseconds. @@ -538,7 +555,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_nanos(&self) -> u32 { - self.nanos.as_inner() + self.nanos.0 } /// Returns the total number of whole milliseconds contained by this `Duration`. @@ -556,8 +573,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_millis(&self) -> u128 { - self.secs as u128 * MILLIS_PER_SEC as u128 - + (self.nanos.as_inner() / NANOS_PER_MILLI) as u128 + self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MILLI) as u128 } /// Returns the total number of whole microseconds contained by this `Duration`. @@ -575,8 +591,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_micros(&self) -> u128 { - self.secs as u128 * MICROS_PER_SEC as u128 - + (self.nanos.as_inner() / NANOS_PER_MICRO) as u128 + self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MICRO) as u128 } /// Returns the total number of nanoseconds contained by this `Duration`. @@ -594,7 +609,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_nanos(&self) -> u128 { - self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.as_inner() as u128 + self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.0 as u128 } /// Computes the absolute difference between `self` and `other`. @@ -634,7 +649,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_add(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_add(rhs.secs) { - let mut nanos = self.nanos.as_inner() + rhs.nanos.as_inner(); + let mut nanos = self.nanos.0 + rhs.nanos.0; if nanos >= NANOS_PER_SEC { nanos -= NANOS_PER_SEC; if let Some(new_secs) = secs.checked_add(1) { @@ -692,11 +707,11 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_sub(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { - let nanos = if self.nanos.as_inner() >= rhs.nanos.as_inner() { - self.nanos.as_inner() - rhs.nanos.as_inner() + let nanos = if self.nanos.0 >= rhs.nanos.0 { + self.nanos.0 - rhs.nanos.0 } else if let Some(sub_secs) = secs.checked_sub(1) { secs = sub_secs; - self.nanos.as_inner() + NANOS_PER_SEC - rhs.nanos.as_inner() + self.nanos.0 + NANOS_PER_SEC - rhs.nanos.0 } else { return None; }; @@ -748,7 +763,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_mul(self, rhs: u32) -> Option { // Multiply nanoseconds as u64, because it cannot overflow that way. - let total_nanos = self.nanos.as_inner() as u64 * rhs as u64; + let total_nanos = self.nanos.0 as u64 * rhs as u64; let extra_secs = total_nanos / (NANOS_PER_SEC as u64); let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; // FIXME(const-hack): use `and_then` once that is possible. @@ -805,8 +820,7 @@ impl Duration { pub const fn checked_div(self, rhs: u32) -> Option { if rhs != 0 { let (secs, extra_secs) = (self.secs / (rhs as u64), self.secs % (rhs as u64)); - let (mut nanos, extra_nanos) = - (self.nanos.as_inner() / rhs, self.nanos.as_inner() % rhs); + let (mut nanos, extra_nanos) = (self.nanos.0 / rhs, self.nanos.0 % rhs); nanos += ((extra_secs * (NANOS_PER_SEC as u64) + extra_nanos as u64) / (rhs as u64)) as u32; debug_assert!(nanos < NANOS_PER_SEC); @@ -832,7 +846,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn as_secs_f64(&self) -> f64 { - (self.secs as f64) + (self.nanos.as_inner() as f64) / (NANOS_PER_SEC as f64) + (self.secs as f64) + (self.nanos.0 as f64) / (NANOS_PER_SEC as f64) } /// Returns the number of seconds contained by this `Duration` as `f32`. @@ -851,7 +865,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn as_secs_f32(&self) -> f32 { - (self.secs as f32) + (self.nanos.as_inner() as f32) / (NANOS_PER_SEC as f32) + (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32) } /// Returns the number of milliseconds contained by this `Duration` as `f64`. @@ -871,7 +885,7 @@ impl Duration { #[inline] pub const fn as_millis_f64(&self) -> f64 { (self.secs as f64) * (MILLIS_PER_SEC as f64) - + (self.nanos.as_inner() as f64) / (NANOS_PER_MILLI as f64) + + (self.nanos.0 as f64) / (NANOS_PER_MILLI as f64) } /// Returns the number of milliseconds contained by this `Duration` as `f32`. @@ -891,7 +905,7 @@ impl Duration { #[inline] pub const fn as_millis_f32(&self) -> f32 { (self.secs as f32) * (MILLIS_PER_SEC as f32) - + (self.nanos.as_inner() as f32) / (NANOS_PER_MILLI as f32) + + (self.nanos.0 as f32) / (NANOS_PER_MILLI as f32) } /// Creates a new `Duration` from the specified number of seconds represented @@ -1070,9 +1084,8 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn div_duration_f64(self, rhs: Duration) -> f64 { - let self_nanos = - (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.as_inner() as f64); - let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.as_inner() as f64); + let self_nanos = (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.0 as f64); + let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.0 as f64); self_nanos / rhs_nanos } @@ -1092,9 +1105,8 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn div_duration_f32(self, rhs: Duration) -> f32 { - let self_nanos = - (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.as_inner() as f32); - let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.as_inner() as f32); + let self_nanos = (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.0 as f32); + let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.0 as f32); self_nanos / rhs_nanos } } @@ -1189,13 +1201,13 @@ macro_rules! sum_durations { for entry in $iter { total_secs = total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations"); - total_nanos = match total_nanos.checked_add(entry.nanos.as_inner() as u64) { + total_nanos = match total_nanos.checked_add(entry.nanos.0 as u64) { Some(n) => n, None => { total_secs = total_secs .checked_add(total_nanos / NANOS_PER_SEC as u64) .expect("overflow in iter::sum over durations"); - (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.as_inner() as u64 + (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.0 as u64 } }; } @@ -1387,27 +1399,27 @@ impl fmt::Debug for Duration { let prefix = if f.sign_plus() { "+" } else { "" }; if self.secs > 0 { - fmt_decimal(f, self.secs, self.nanos.as_inner(), NANOS_PER_SEC / 10, prefix, "s") - } else if self.nanos.as_inner() >= NANOS_PER_MILLI { + fmt_decimal(f, self.secs, self.nanos.0, NANOS_PER_SEC / 10, prefix, "s") + } else if self.nanos.0 >= NANOS_PER_MILLI { fmt_decimal( f, - (self.nanos.as_inner() / NANOS_PER_MILLI) as u64, - self.nanos.as_inner() % NANOS_PER_MILLI, + (self.nanos.0 / NANOS_PER_MILLI) as u64, + self.nanos.0 % NANOS_PER_MILLI, NANOS_PER_MILLI / 10, prefix, "ms", ) - } else if self.nanos.as_inner() >= NANOS_PER_MICRO { + } else if self.nanos.0 >= NANOS_PER_MICRO { fmt_decimal( f, - (self.nanos.as_inner() / NANOS_PER_MICRO) as u64, - self.nanos.as_inner() % NANOS_PER_MICRO, + (self.nanos.0 / NANOS_PER_MICRO) as u64, + self.nanos.0 % NANOS_PER_MICRO, NANOS_PER_MICRO / 10, prefix, "µs", ) } else { - fmt_decimal(f, self.nanos.as_inner() as u64, 0, 1, prefix, "ns") + fmt_decimal(f, self.nanos.0 as u64, 0, 1, prefix, "ns") } } } diff --git a/core/src/ub_checks.rs b/core/src/ub_checks.rs index b289f6026ffcb..3e6110c9c88a7 100644 --- a/core/src/ub_checks.rs +++ b/core/src/ub_checks.rs @@ -47,6 +47,7 @@ use crate::intrinsics::{self, const_eval_select}; /// order to call it. Since the precompiled standard library is built with full debuginfo and these /// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough /// debuginfo to have a measurable compile-time impact on debug builds. +#[cfg_attr(bootstrap, allow_internal_unstable(const_ub_checks))] // permit this to be called in stably-const fn #[macro_export] #[unstable(feature = "ub_checks", issue = "none")] macro_rules! assert_unsafe_precondition { @@ -88,6 +89,7 @@ pub use intrinsics::ub_checks as check_library_ub; /// /// The intention is to not do that when running in the interpreter, as that one has its own /// language UB checks which generally produce better errors. +#[cfg_attr(bootstrap, rustc_const_unstable(feature = "const_ub_checks", issue = "none"))] #[inline] #[rustc_allow_const_fn_unstable(const_eval_select)] pub(crate) const fn check_language_ub() -> bool { diff --git a/core/src/unicode/mod.rs b/core/src/unicode/mod.rs index 49dbdeb1a6d1c..6066aa9921607 100644 --- a/core/src/unicode/mod.rs +++ b/core/src/unicode/mod.rs @@ -17,8 +17,6 @@ pub(crate) use unicode_data::uppercase::lookup as Uppercase; pub(crate) use unicode_data::white_space::lookup as White_Space; pub(crate) mod printable; - -#[allow(unreachable_pub)] mod unicode_data; /// The version of [Unicode](https://www.unicode.org/) that the Unicode parts of diff --git a/core/src/unicode/printable.py b/core/src/unicode/printable.py index 260fa9f9e6ad2..4d39ace066c46 100755 --- a/core/src/unicode/printable.py +++ b/core/src/unicode/printable.py @@ -9,8 +9,7 @@ import os import subprocess -NUM_CODEPOINTS = 0x110000 - +NUM_CODEPOINTS=0x110000 def to_ranges(iter): current = None @@ -24,15 +23,11 @@ def to_ranges(iter): if current is not None: yield tuple(current) - def get_escaped(codepoints): for c in codepoints: - if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord( - " " - ): + if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(' '): yield c.value - def get_file(f): try: return open(os.path.basename(f)) @@ -40,9 +35,7 @@ def get_file(f): subprocess.run(["curl", "-O", f], check=True) return open(os.path.basename(f)) - -Codepoint = namedtuple("Codepoint", "value class_") - +Codepoint = namedtuple('Codepoint', 'value class_') def get_codepoints(f): r = csv.reader(f, delimiter=";") @@ -73,14 +66,13 @@ def get_codepoints(f): for c in range(prev_codepoint + 1, NUM_CODEPOINTS): yield Codepoint(c, None) - def compress_singletons(singletons): - uppers = [] # (upper, # items in lowers) + uppers = [] # (upper, # items in lowers) lowers = [] for i in singletons: upper = i >> 8 - lower = i & 0xFF + lower = i & 0xff if len(uppers) == 0 or uppers[-1][0] != upper: uppers.append((upper, 1)) else: @@ -90,11 +82,10 @@ def compress_singletons(singletons): return uppers, lowers - def compress_normal(normal): # lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f # lengths 0x80..0x7fff are encoded as 80 80, 80 81, ..., ff fe, ff ff - compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)] + compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)] prev_start = 0 for start, count in normal: @@ -104,22 +95,21 @@ def compress_normal(normal): assert truelen < 0x8000 and falselen < 0x8000 entry = [] - if truelen > 0x7F: + if truelen > 0x7f: entry.append(0x80 | (truelen >> 8)) - entry.append(truelen & 0xFF) + entry.append(truelen & 0xff) else: - entry.append(truelen & 0x7F) - if falselen > 0x7F: + entry.append(truelen & 0x7f) + if falselen > 0x7f: entry.append(0x80 | (falselen >> 8)) - entry.append(falselen & 0xFF) + entry.append(falselen & 0xff) else: - entry.append(falselen & 0x7F) + entry.append(falselen & 0x7f) compressed.append(entry) return compressed - def print_singletons(uppers, lowers, uppersname, lowersname): print("#[rustfmt::skip]") print("const {}: &[(u8, u8)] = &[".format(uppersname)) @@ -129,12 +119,9 @@ def print_singletons(uppers, lowers, uppersname, lowersname): print("#[rustfmt::skip]") print("const {}: &[u8] = &[".format(lowersname)) for i in range(0, len(lowers), 8): - print( - " {}".format(" ".join("{:#04x},".format(x) for x in lowers[i : i + 8])) - ) + print(" {}".format(" ".join("{:#04x},".format(x) for x in lowers[i:i+8]))) print("];") - def print_normal(normal, normalname): print("#[rustfmt::skip]") print("const {}: &[u8] = &[".format(normalname)) @@ -142,13 +129,12 @@ def print_normal(normal, normalname): print(" {}".format(" ".join("{:#04x},".format(i) for i in v))) print("];") - def main(): file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt") codepoints = get_codepoints(file) - CUTOFF = 0x10000 + CUTOFF=0x10000 singletons0 = [] singletons1 = [] normal0 = [] @@ -248,11 +234,10 @@ def main(): }\ """) print() - print_singletons(singletons0u, singletons0l, "SINGLETONS0U", "SINGLETONS0L") - print_singletons(singletons1u, singletons1l, "SINGLETONS1U", "SINGLETONS1L") - print_normal(normal0, "NORMAL0") - print_normal(normal1, "NORMAL1") - + print_singletons(singletons0u, singletons0l, 'SINGLETONS0U', 'SINGLETONS0L') + print_singletons(singletons1u, singletons1l, 'SINGLETONS1U', 'SINGLETONS1L') + print_normal(normal0, 'NORMAL0') + print_normal(normal1, 'NORMAL1') -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/core/src/unicode/unicode_data.rs b/core/src/unicode/unicode_data.rs index 4655d35e9c437..7f4826402eb53 100644 --- a/core/src/unicode/unicode_data.rs +++ b/core/src/unicode/unicode_data.rs @@ -1,6 +1,7 @@ ///! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually! #[inline(always)] +#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))] const fn bitset_search< const N: usize, const CHUNK_SIZE: usize, @@ -423,6 +424,7 @@ pub mod lowercase { (5, 187), (6, 78), (7, 132), ]; + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))] pub const fn lookup(c: char) -> bool { super::bitset_search( c as u32, @@ -547,6 +549,7 @@ pub mod uppercase { (2, 146), (2, 20), (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), ]; + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))] pub const fn lookup(c: char) -> bool { super::bitset_search( c as u32, diff --git a/core/src/unsafe_binder.rs b/core/src/unsafe_binder.rs deleted file mode 100644 index 98f53e07d9d8d..0000000000000 --- a/core/src/unsafe_binder.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Operators used to turn types into unsafe binders and back. - -/// Unwrap an unsafe binder into its underlying type. -#[allow_internal_unstable(builtin_syntax)] -#[unstable(feature = "unsafe_binders", issue = "130516")] -pub macro unwrap_binder { - ($expr:expr) => { - builtin # unwrap_binder ( $expr ) - }, - ($expr:expr ; $ty:ty) => { - builtin # unwrap_binder ( $expr, $ty ) - }, -} - -/// Wrap a type into an unsafe binder. -#[allow_internal_unstable(builtin_syntax)] -#[unstable(feature = "unsafe_binders", issue = "130516")] -pub macro wrap_binder { - ($expr:expr) => { - builtin # wrap_binder ( $expr ) - }, - ($expr:expr ; $ty:ty) => { - builtin # wrap_binder ( $expr, $ty ) - }, -} diff --git a/coretests/tests/alloc.rs b/core/tests/alloc.rs similarity index 100% rename from coretests/tests/alloc.rs rename to core/tests/alloc.rs diff --git a/coretests/tests/any.rs b/core/tests/any.rs similarity index 100% rename from coretests/tests/any.rs rename to core/tests/any.rs diff --git a/coretests/tests/array.rs b/core/tests/array.rs similarity index 100% rename from coretests/tests/array.rs rename to core/tests/array.rs diff --git a/coretests/tests/ascii.rs b/core/tests/ascii.rs similarity index 100% rename from coretests/tests/ascii.rs rename to core/tests/ascii.rs diff --git a/coretests/tests/ascii_char.rs b/core/tests/ascii_char.rs similarity index 100% rename from coretests/tests/ascii_char.rs rename to core/tests/ascii_char.rs diff --git a/coretests/tests/asserting.rs b/core/tests/asserting.rs similarity index 100% rename from coretests/tests/asserting.rs rename to core/tests/asserting.rs diff --git a/coretests/tests/async_iter/mod.rs b/core/tests/async_iter/mod.rs similarity index 100% rename from coretests/tests/async_iter/mod.rs rename to core/tests/async_iter/mod.rs diff --git a/coretests/tests/atomic.rs b/core/tests/atomic.rs similarity index 100% rename from coretests/tests/atomic.rs rename to core/tests/atomic.rs diff --git a/coretests/tests/bool.rs b/core/tests/bool.rs similarity index 96% rename from coretests/tests/bool.rs rename to core/tests/bool.rs index bcd6dc2abac6c..47f6459915b3e 100644 --- a/coretests/tests/bool.rs +++ b/core/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/coretests/tests/cell.rs b/core/tests/cell.rs similarity index 100% rename from coretests/tests/cell.rs rename to core/tests/cell.rs diff --git a/coretests/tests/char.rs b/core/tests/char.rs similarity index 100% rename from coretests/tests/char.rs rename to core/tests/char.rs diff --git a/coretests/tests/clone.rs b/core/tests/clone.rs similarity index 100% rename from coretests/tests/clone.rs rename to core/tests/clone.rs diff --git a/coretests/tests/cmp.rs b/core/tests/cmp.rs similarity index 100% rename from coretests/tests/cmp.rs rename to core/tests/cmp.rs diff --git a/coretests/tests/const_ptr.rs b/core/tests/const_ptr.rs similarity index 100% rename from coretests/tests/const_ptr.rs rename to core/tests/const_ptr.rs diff --git a/coretests/tests/convert.rs b/core/tests/convert.rs similarity index 100% rename from coretests/tests/convert.rs rename to core/tests/convert.rs diff --git a/coretests/tests/error.rs b/core/tests/error.rs similarity index 100% rename from coretests/tests/error.rs rename to core/tests/error.rs diff --git a/coretests/tests/ffi.rs b/core/tests/ffi.rs similarity index 100% rename from coretests/tests/ffi.rs rename to core/tests/ffi.rs diff --git a/coretests/tests/ffi/cstr.rs b/core/tests/ffi/cstr.rs similarity index 100% rename from coretests/tests/ffi/cstr.rs rename to core/tests/ffi/cstr.rs diff --git a/coretests/tests/fmt/builders.rs b/core/tests/fmt/builders.rs similarity index 100% rename from coretests/tests/fmt/builders.rs rename to core/tests/fmt/builders.rs diff --git a/coretests/tests/fmt/float.rs b/core/tests/fmt/float.rs similarity index 100% rename from coretests/tests/fmt/float.rs rename to core/tests/fmt/float.rs diff --git a/core/tests/fmt/mod.rs b/core/tests/fmt/mod.rs new file mode 100644 index 0000000000000..704d246139947 --- /dev/null +++ b/core/tests/fmt/mod.rs @@ -0,0 +1,45 @@ +mod builders; +mod float; +mod num; + +#[test] +fn test_format_flags() { + // No residual flags left by pointer formatting + let p = "".as_ptr(); + assert_eq!(format!("{:p} {:x}", p, 16), format!("{p:p} 10")); + + assert_eq!(format!("{: >3}", 'a'), " a"); +} + +#[test] +fn test_pointer_formats_data_pointer() { + let b: &[u8] = b""; + let s: &str = ""; + assert_eq!(format!("{s:p}"), format!("{:p}", s.as_ptr())); + assert_eq!(format!("{b:p}"), format!("{:p}", b.as_ptr())); +} + +#[test] +fn test_estimated_capacity() { + assert_eq!(format_args!("").estimated_capacity(), 0); + assert_eq!(format_args!("{}", { "" }).estimated_capacity(), 0); + assert_eq!(format_args!("Hello").estimated_capacity(), 5); + assert_eq!(format_args!("Hello, {}!", { "" }).estimated_capacity(), 16); + assert_eq!(format_args!("{}, hello!", { "World" }).estimated_capacity(), 0); + assert_eq!(format_args!("{}. 16-bytes piece", { "World" }).estimated_capacity(), 32); +} + +#[test] +fn pad_integral_resets() { + struct Bar; + + impl core::fmt::Display for Bar { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + "1".fmt(f)?; + f.pad_integral(true, "", "5")?; + "1".fmt(f) + } + } + + assert_eq!(format!("{Bar:<03}"), "1 0051 "); +} diff --git a/coretests/tests/fmt/num.rs b/core/tests/fmt/num.rs similarity index 100% rename from coretests/tests/fmt/num.rs rename to core/tests/fmt/num.rs diff --git a/coretests/tests/future.rs b/core/tests/future.rs similarity index 100% rename from coretests/tests/future.rs rename to core/tests/future.rs diff --git a/coretests/tests/hash/mod.rs b/core/tests/hash/mod.rs similarity index 90% rename from coretests/tests/hash/mod.rs rename to core/tests/hash/mod.rs index 1f10a4733b053..bf91e9e5df0e2 100644 --- a/coretests/tests/hash/mod.rs +++ b/core/tests/hash/mod.rs @@ -4,11 +4,16 @@ use std::hash::{BuildHasher, Hash, Hasher}; use std::ptr; use std::rc::Rc; -#[derive(Default)] struct MyHasher { hash: u64, } +impl Default for MyHasher { + fn default() -> MyHasher { + MyHasher { hash: 0 } + } +} + impl Hasher for MyHasher { fn write(&mut self, buf: &[u8]) { for byte in buf { @@ -102,8 +107,6 @@ fn test_writer_hasher() { struct Custom { hash: u64, } - -#[derive(Default)] struct CustomHasher { output: u64, } @@ -120,6 +123,12 @@ impl Hasher for CustomHasher { } } +impl Default for CustomHasher { + fn default() -> CustomHasher { + CustomHasher { output: 0 } + } +} + impl Hash for Custom { fn hash(&self, state: &mut H) { state.write_u64(self.hash); @@ -141,6 +150,9 @@ 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/coretests/tests/hash/sip.rs b/core/tests/hash/sip.rs similarity index 100% rename from coretests/tests/hash/sip.rs rename to core/tests/hash/sip.rs diff --git a/coretests/tests/intrinsics.rs b/core/tests/intrinsics.rs similarity index 62% rename from coretests/tests/intrinsics.rs rename to core/tests/intrinsics.rs index 744a6a0d2dd8f..8b731cf5b25d1 100644 --- a/coretests/tests/intrinsics.rs +++ b/core/tests/intrinsics.rs @@ -125,71 +125,3 @@ fn test_three_way_compare_in_const_contexts() { assert_eq!(SIGNED_EQUAL, Equal); assert_eq!(SIGNED_GREATER, Greater); } - -fn fallback_cma( - a: T, - b: T, - c: T, - d: T, -) -> (T::Unsigned, T) { - a.carrying_mul_add(b, c, d) -} - -#[test] -fn carrying_mul_add_fallback_u32() { - let r = fallback_cma::(0x9e37_79b9, 0x7f4a_7c15, 0xf39c_c060, 0x5ced_c834); - assert_eq!(r, (0x2087_20c1, 0x4eab_8e1d)); - let r = fallback_cma::(0x1082_276b, 0xf3a2_7251, 0xf86c_6a11, 0xd0c1_8e95); - assert_eq!(r, (0x7aa0_1781, 0x0fb6_0528)); -} - -#[test] -fn carrying_mul_add_fallback_i32() { - let r = fallback_cma::(-1, -1, -1, -1); - assert_eq!(r, (u32::MAX, -1)); - let r = fallback_cma::(1, -1, 1, 1); - assert_eq!(r, (1, 0)); -} - -#[test] -fn carrying_mul_add_fallback_u128() { - assert_eq!(fallback_cma::(u128::MAX, u128::MAX, 0, 0), (1, u128::MAX - 1)); - assert_eq!(fallback_cma::(1, 1, 1, 1), (3, 0)); - assert_eq!(fallback_cma::(0, 0, u128::MAX, u128::MAX), (u128::MAX - 1, 1)); - assert_eq!( - fallback_cma::(u128::MAX, u128::MAX, u128::MAX, u128::MAX), - (u128::MAX, u128::MAX), - ); - - let r = fallback_cma::( - 0x243f6a8885a308d313198a2e03707344, - 0xa4093822299f31d0082efa98ec4e6c89, - 0x452821e638d01377be5466cf34e90c6c, - 0xc0ac29b7c97c50dd3f84d5b5b5470917, - ); - assert_eq!(r, (0x8050ec20ed554e40338d277e00b674e7, 0x1739ee6cea07da409182d003859b59d8)); - let r = fallback_cma::( - 0x9216d5d98979fb1bd1310ba698dfb5ac, - 0x2ffd72dbd01adfb7b8e1afed6a267e96, - 0xba7c9045f12c7f9924a19947b3916cf7, - 0x0801f2e2858efc16636920d871574e69, - ); - assert_eq!(r, (0x185525545fdb2fefb502a3a602efd628, 0x1b62d35fe3bff6b566f99667ef7ebfd6)); -} - -#[test] -fn carrying_mul_add_fallback_i128() { - assert_eq!(fallback_cma::(-1, -1, 0, 0), (1, 0)); - let r = fallback_cma::(-1, -1, -1, -1); - assert_eq!(r, (u128::MAX, -1)); - let r = fallback_cma::(1, -1, 1, 1); - assert_eq!(r, (1, 0)); - assert_eq!( - fallback_cma::(i128::MAX, i128::MAX, i128::MAX, i128::MAX), - (u128::MAX, i128::MAX / 2), - ); - assert_eq!( - fallback_cma::(i128::MIN, i128::MIN, i128::MAX, i128::MAX), - (u128::MAX - 1, -(i128::MIN / 2)), - ); -} diff --git a/coretests/tests/io/borrowed_buf.rs b/core/tests/io/borrowed_buf.rs similarity index 100% rename from coretests/tests/io/borrowed_buf.rs rename to core/tests/io/borrowed_buf.rs diff --git a/coretests/tests/io/mod.rs b/core/tests/io/mod.rs similarity index 100% rename from coretests/tests/io/mod.rs rename to core/tests/io/mod.rs diff --git a/coretests/tests/iter/adapters/array_chunks.rs b/core/tests/iter/adapters/array_chunks.rs similarity index 100% rename from coretests/tests/iter/adapters/array_chunks.rs rename to core/tests/iter/adapters/array_chunks.rs diff --git a/coretests/tests/iter/adapters/by_ref_sized.rs b/core/tests/iter/adapters/by_ref_sized.rs similarity index 100% rename from coretests/tests/iter/adapters/by_ref_sized.rs rename to core/tests/iter/adapters/by_ref_sized.rs diff --git a/coretests/tests/iter/adapters/chain.rs b/core/tests/iter/adapters/chain.rs similarity index 100% rename from coretests/tests/iter/adapters/chain.rs rename to core/tests/iter/adapters/chain.rs diff --git a/coretests/tests/iter/adapters/cloned.rs b/core/tests/iter/adapters/cloned.rs similarity index 100% rename from coretests/tests/iter/adapters/cloned.rs rename to core/tests/iter/adapters/cloned.rs diff --git a/coretests/tests/iter/adapters/copied.rs b/core/tests/iter/adapters/copied.rs similarity index 100% rename from coretests/tests/iter/adapters/copied.rs rename to core/tests/iter/adapters/copied.rs diff --git a/coretests/tests/iter/adapters/cycle.rs b/core/tests/iter/adapters/cycle.rs similarity index 100% rename from coretests/tests/iter/adapters/cycle.rs rename to core/tests/iter/adapters/cycle.rs diff --git a/coretests/tests/iter/adapters/enumerate.rs b/core/tests/iter/adapters/enumerate.rs similarity index 100% rename from coretests/tests/iter/adapters/enumerate.rs rename to core/tests/iter/adapters/enumerate.rs diff --git a/coretests/tests/iter/adapters/filter.rs b/core/tests/iter/adapters/filter.rs similarity index 100% rename from coretests/tests/iter/adapters/filter.rs rename to core/tests/iter/adapters/filter.rs diff --git a/coretests/tests/iter/adapters/filter_map.rs b/core/tests/iter/adapters/filter_map.rs similarity index 100% rename from coretests/tests/iter/adapters/filter_map.rs rename to core/tests/iter/adapters/filter_map.rs diff --git a/coretests/tests/iter/adapters/flat_map.rs b/core/tests/iter/adapters/flat_map.rs similarity index 100% rename from coretests/tests/iter/adapters/flat_map.rs rename to core/tests/iter/adapters/flat_map.rs diff --git a/coretests/tests/iter/adapters/flatten.rs b/core/tests/iter/adapters/flatten.rs similarity index 100% rename from coretests/tests/iter/adapters/flatten.rs rename to core/tests/iter/adapters/flatten.rs diff --git a/coretests/tests/iter/adapters/fuse.rs b/core/tests/iter/adapters/fuse.rs similarity index 100% rename from coretests/tests/iter/adapters/fuse.rs rename to core/tests/iter/adapters/fuse.rs diff --git a/coretests/tests/iter/adapters/inspect.rs b/core/tests/iter/adapters/inspect.rs similarity index 100% rename from coretests/tests/iter/adapters/inspect.rs rename to core/tests/iter/adapters/inspect.rs diff --git a/coretests/tests/iter/adapters/intersperse.rs b/core/tests/iter/adapters/intersperse.rs similarity index 100% rename from coretests/tests/iter/adapters/intersperse.rs rename to core/tests/iter/adapters/intersperse.rs diff --git a/coretests/tests/iter/adapters/map.rs b/core/tests/iter/adapters/map.rs similarity index 100% rename from coretests/tests/iter/adapters/map.rs rename to core/tests/iter/adapters/map.rs diff --git a/coretests/tests/iter/adapters/map_windows.rs b/core/tests/iter/adapters/map_windows.rs similarity index 98% rename from coretests/tests/iter/adapters/map_windows.rs rename to core/tests/iter/adapters/map_windows.rs index 01cebc9b27fd8..b677f1cfd55e7 100644 --- a/coretests/tests/iter/adapters/map_windows.rs +++ b/core/tests/iter/adapters/map_windows.rs @@ -159,10 +159,11 @@ fn output_n2() { >::new(), ); assert_eq!("ab".chars().map_windows(|a: &[_; 2]| *a).collect::>(), vec![['a', 'b']]); - assert_eq!( - "abcd".chars().map_windows(|a: &[_; 2]| *a).collect::>(), - vec![['a', 'b'], ['b', 'c'], ['c', 'd']], - ); + assert_eq!("abcd".chars().map_windows(|a: &[_; 2]| *a).collect::>(), vec![ + ['a', 'b'], + ['b', 'c'], + ['c', 'd'] + ],); } #[test] diff --git a/coretests/tests/iter/adapters/mod.rs b/core/tests/iter/adapters/mod.rs similarity index 100% rename from coretests/tests/iter/adapters/mod.rs rename to core/tests/iter/adapters/mod.rs diff --git a/coretests/tests/iter/adapters/peekable.rs b/core/tests/iter/adapters/peekable.rs similarity index 100% rename from coretests/tests/iter/adapters/peekable.rs rename to core/tests/iter/adapters/peekable.rs diff --git a/coretests/tests/iter/adapters/scan.rs b/core/tests/iter/adapters/scan.rs similarity index 100% rename from coretests/tests/iter/adapters/scan.rs rename to core/tests/iter/adapters/scan.rs diff --git a/coretests/tests/iter/adapters/skip.rs b/core/tests/iter/adapters/skip.rs similarity index 100% rename from coretests/tests/iter/adapters/skip.rs rename to core/tests/iter/adapters/skip.rs diff --git a/coretests/tests/iter/adapters/skip_while.rs b/core/tests/iter/adapters/skip_while.rs similarity index 100% rename from coretests/tests/iter/adapters/skip_while.rs rename to core/tests/iter/adapters/skip_while.rs diff --git a/coretests/tests/iter/adapters/step_by.rs b/core/tests/iter/adapters/step_by.rs similarity index 100% rename from coretests/tests/iter/adapters/step_by.rs rename to core/tests/iter/adapters/step_by.rs diff --git a/coretests/tests/iter/adapters/take.rs b/core/tests/iter/adapters/take.rs similarity index 99% rename from coretests/tests/iter/adapters/take.rs rename to core/tests/iter/adapters/take.rs index b932059afec8a..65a8a93b4a916 100644 --- a/coretests/tests/iter/adapters/take.rs +++ b/core/tests/iter/adapters/take.rs @@ -255,7 +255,7 @@ fn test_reverse_on_zip() { let zipped_iter = vec_1.iter().zip(core::iter::repeat(0).take(20)); - // Cannot call rev here for automatic reversed zip construction + // Cannot call rev here for automatic reversed zip constuction for (&one, zero) in zipped_iter.rev() { assert_eq!((1, 0), (one, zero)); } diff --git a/coretests/tests/iter/adapters/take_while.rs b/core/tests/iter/adapters/take_while.rs similarity index 100% rename from coretests/tests/iter/adapters/take_while.rs rename to core/tests/iter/adapters/take_while.rs diff --git a/coretests/tests/iter/adapters/zip.rs b/core/tests/iter/adapters/zip.rs similarity index 100% rename from coretests/tests/iter/adapters/zip.rs rename to core/tests/iter/adapters/zip.rs diff --git a/coretests/tests/iter/mod.rs b/core/tests/iter/mod.rs similarity index 100% rename from coretests/tests/iter/mod.rs rename to core/tests/iter/mod.rs diff --git a/coretests/tests/iter/range.rs b/core/tests/iter/range.rs similarity index 100% rename from coretests/tests/iter/range.rs rename to core/tests/iter/range.rs diff --git a/coretests/tests/iter/sources.rs b/core/tests/iter/sources.rs similarity index 100% rename from coretests/tests/iter/sources.rs rename to core/tests/iter/sources.rs diff --git a/coretests/tests/iter/traits/accum.rs b/core/tests/iter/traits/accum.rs similarity index 100% rename from coretests/tests/iter/traits/accum.rs rename to core/tests/iter/traits/accum.rs diff --git a/coretests/tests/iter/traits/double_ended.rs b/core/tests/iter/traits/double_ended.rs similarity index 100% rename from coretests/tests/iter/traits/double_ended.rs rename to core/tests/iter/traits/double_ended.rs diff --git a/coretests/tests/iter/traits/iterator.rs b/core/tests/iter/traits/iterator.rs similarity index 96% rename from coretests/tests/iter/traits/iterator.rs rename to core/tests/iter/traits/iterator.rs index e31d2e15b6d7e..93ef9c0812b16 100644 --- a/coretests/tests/iter/traits/iterator.rs +++ b/core/tests/iter/traits/iterator.rs @@ -617,31 +617,6 @@ fn test_next_chunk() { assert_eq!(it.next_chunk::<0>().unwrap(), []); } -#[test] -fn test_collect_into_tuples() { - let a = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; - let b = vec![1, 4, 7]; - let c = vec![2, 5, 8]; - let d = vec![3, 6, 9]; - let mut e = (Vec::new(), Vec::new(), Vec::new()); - a.iter().cloned().collect_into(&mut e); - assert!(e.0 == b); - assert!(e.1 == c); - assert!(e.2 == d); -} - -#[test] -fn test_collect_for_tuples() { - let a = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; - let b = vec![1, 4, 7]; - let c = vec![2, 5, 8]; - let d = vec![3, 6, 9]; - let e: (Vec<_>, Vec<_>, Vec<_>) = a.into_iter().collect(); - assert!(e.0 == b); - assert!(e.1 == c); - assert!(e.2 == d); -} - // just tests by whether or not this compiles fn _empty_impl_all_auto_traits() { use std::panic::{RefUnwindSafe, UnwindSafe}; diff --git a/coretests/tests/iter/traits/mod.rs b/core/tests/iter/traits/mod.rs similarity index 100% rename from coretests/tests/iter/traits/mod.rs rename to core/tests/iter/traits/mod.rs diff --git a/coretests/tests/iter/traits/step.rs b/core/tests/iter/traits/step.rs similarity index 100% rename from coretests/tests/iter/traits/step.rs rename to core/tests/iter/traits/step.rs diff --git a/coretests/tests/lazy.rs b/core/tests/lazy.rs similarity index 100% rename from coretests/tests/lazy.rs rename to core/tests/lazy.rs diff --git a/coretests/tests/lib.rs b/core/tests/lib.rs similarity index 90% rename from coretests/tests/lib.rs rename to core/tests/lib.rs index f1bbed3de3017..f7825571cd7a8 100644 --- a/coretests/tests/lib.rs +++ b/core/tests/lib.rs @@ -1,4 +1,7 @@ // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(const_three_way_compare))] +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] #![feature(alloc_layout_extra)] @@ -11,14 +14,15 @@ #![feature(async_iter_from_iter)] #![feature(async_iterator)] #![feature(bigint_helper_methods)] -#![feature(bstr)] #![feature(cell_update)] #![feature(clone_to_uninit)] +#![feature(const_align_of_val_raw)] +#![feature(const_black_box)] #![feature(const_eval_select)] -#![feature(const_swap_nonoverlapping)] +#![feature(const_heap)] +#![feature(const_nonnull_new)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] -#![feature(core_intrinsics_fallbacks)] #![feature(core_io_borrowed_buf)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] @@ -31,7 +35,6 @@ #![feature(float_minimum_maximum)] #![feature(flt2dec)] #![feature(fmt_internals)] -#![feature(formatting_options)] #![feature(freeze)] #![feature(future_join)] #![feature(generic_assert_internals)] @@ -62,7 +65,8 @@ #![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(never_type)] -#![feature(num_midpoint_signed)] +#![feature(noop_waker)] +#![feature(num_midpoint)] #![feature(numfmt)] #![feature(pattern)] #![feature(pointer_is_aligned_to)] @@ -79,8 +83,8 @@ #![feature(step_trait)] #![feature(str_internals)] #![feature(strict_provenance_atomic_ptr)] -#![feature(strict_provenance_lints)] #![feature(test)] +#![feature(trait_upcasting)] #![feature(trusted_len)] #![feature(trusted_random_access)] #![feature(try_blocks)] @@ -97,13 +101,10 @@ /// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality. macro_rules! assert_eq_const_safe { - ($left:expr, $right:expr) => { - assert_eq_const_safe!($left, $right, concat!(stringify!($left), " == ", stringify!($right))); - }; ($left:expr, $right:expr$(, $($arg:tt)+)?) => { { fn runtime() { - assert_eq!($left, $right, $($($arg)*),*); + assert_eq!($left, $right, $($arg)*); } const fn compiletime() { assert!(matches!($left, const { $right })); @@ -138,7 +139,6 @@ mod asserting; mod async_iter; mod atomic; mod bool; -mod bstr; mod cell; mod char; mod clone; @@ -153,10 +153,7 @@ mod intrinsics; mod io; mod iter; mod lazy; -#[cfg(not(bootstrap))] mod macros; -#[cfg(bootstrap)] -mod macros_bootstrap; mod manually_drop; mod mem; mod net; diff --git a/coretests/tests/macros_bootstrap.rs b/core/tests/macros.rs similarity index 97% rename from coretests/tests/macros_bootstrap.rs rename to core/tests/macros.rs index f10ef862c5dd9..fdb4ea2941285 100644 --- a/coretests/tests/macros_bootstrap.rs +++ b/core/tests/macros.rs @@ -183,6 +183,8 @@ fn _accepts_expressions() -> i32 { } } +// The current implementation expands to a macro call, which allows the use of expression +// statements. fn _allows_stmt_expr_attributes() { let one = 1; let two = 2; diff --git a/coretests/tests/manually_drop.rs b/core/tests/manually_drop.rs similarity index 100% rename from coretests/tests/manually_drop.rs rename to core/tests/manually_drop.rs diff --git a/coretests/tests/mem.rs b/core/tests/mem.rs similarity index 95% rename from coretests/tests/mem.rs rename to core/tests/mem.rs index 1b5c5fc82a69d..f3b4387f6a898 100644 --- a/coretests/tests/mem.rs +++ b/core/tests/mem.rs @@ -200,60 +200,60 @@ fn uninit_array_assume_init() { } #[test] -fn uninit_write_copy_of_slice() { +fn uninit_write_slice() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 64]; - assert_eq!(dst.write_copy_of_slice(&src), &src); + assert_eq!(MaybeUninit::copy_from_slice(&mut dst, &src), &src); } #[test] #[should_panic(expected = "source slice length (32) does not match destination slice length (64)")] -fn uninit_write_copy_of_slice_panic_lt() { +fn uninit_write_slice_panic_lt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 32]; - dst.write_copy_of_slice(&src); + MaybeUninit::copy_from_slice(&mut dst, &src); } #[test] #[should_panic(expected = "source slice length (128) does not match destination slice length (64)")] -fn uninit_write_copy_of_slice_panic_gt() { +fn uninit_write_slice_panic_gt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 128]; - dst.write_copy_of_slice(&src); + MaybeUninit::copy_from_slice(&mut dst, &src); } #[test] -fn uninit_write_clone_of_slice() { +fn uninit_clone_from_slice() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 64]; - assert_eq!(dst.write_clone_of_slice(&src), &src); + assert_eq!(MaybeUninit::clone_from_slice(&mut dst, &src), &src); } #[test] #[should_panic(expected = "destination and source slices have different lengths")] -fn uninit_write_clone_of_slice_panic_lt() { +fn uninit_write_slice_cloned_panic_lt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 32]; - dst.write_clone_of_slice(&src); + MaybeUninit::clone_from_slice(&mut dst, &src); } #[test] #[should_panic(expected = "destination and source slices have different lengths")] -fn uninit_write_clone_of_slice_panic_gt() { +fn uninit_write_slice_cloned_panic_gt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 128]; - dst.write_clone_of_slice(&src); + MaybeUninit::clone_from_slice(&mut dst, &src); } #[test] #[cfg(panic = "unwind")] -fn uninit_write_clone_of_slice_mid_panic() { +fn uninit_write_slice_cloned_mid_panic() { use std::panic; enum IncrementOrPanic { @@ -289,7 +289,7 @@ fn uninit_write_clone_of_slice_mid_panic() { ]; let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { - dst.write_clone_of_slice(&src); + MaybeUninit::clone_from_slice(&mut dst, &src); })); drop(src); @@ -317,11 +317,11 @@ impl Drop for Bomb { } #[test] -fn uninit_write_clone_of_slice_no_drop() { +fn uninit_write_slice_cloned_no_drop() { let mut dst = [MaybeUninit::uninit()]; let src = [Bomb]; - dst.write_clone_of_slice(&src); + MaybeUninit::clone_from_slice(&mut dst, &src); forget(src); } diff --git a/coretests/tests/net/ip_addr.rs b/core/tests/net/ip_addr.rs similarity index 99% rename from coretests/tests/net/ip_addr.rs rename to core/tests/net/ip_addr.rs index f01b43282ec42..707f9a160e127 100644 --- a/coretests/tests/net/ip_addr.rs +++ b/core/tests/net/ip_addr.rs @@ -332,7 +332,6 @@ fn ip_properties() { check!("ff08::", global | multicast); check!("ff0e::", global | multicast); check!("2001:db8:85a3::8a2e:370:7334", doc); - check!("3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff", doc); check!("2001:2::ac32:23ff:21", benchmarking); check!("102:304:506:708:90a:b0c:d0e:f10", global); } @@ -791,15 +790,6 @@ fn ipv6_properties() { documentation ); - check!( - "3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff", - &[ - 0x3f, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff - ], - documentation - ); - check!( "2001:2::ac32:23ff:21", &[0x20, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0xac, 0x32, 0x23, 0xff, 0, 0x21], diff --git a/coretests/tests/net/mod.rs b/core/tests/net/mod.rs similarity index 100% rename from coretests/tests/net/mod.rs rename to core/tests/net/mod.rs diff --git a/coretests/tests/net/parser.rs b/core/tests/net/parser.rs similarity index 100% rename from coretests/tests/net/parser.rs rename to core/tests/net/parser.rs diff --git a/coretests/tests/net/socket_addr.rs b/core/tests/net/socket_addr.rs similarity index 100% rename from coretests/tests/net/socket_addr.rs rename to core/tests/net/socket_addr.rs diff --git a/coretests/tests/nonzero.rs b/core/tests/nonzero.rs similarity index 100% rename from coretests/tests/nonzero.rs rename to core/tests/nonzero.rs diff --git a/coretests/tests/num/bignum.rs b/core/tests/num/bignum.rs similarity index 100% rename from coretests/tests/num/bignum.rs rename to core/tests/num/bignum.rs diff --git a/coretests/tests/num/const_from.rs b/core/tests/num/const_from.rs similarity index 100% rename from coretests/tests/num/const_from.rs rename to core/tests/num/const_from.rs diff --git a/coretests/tests/num/dec2flt/float.rs b/core/tests/num/dec2flt/float.rs similarity index 100% rename from coretests/tests/num/dec2flt/float.rs rename to core/tests/num/dec2flt/float.rs diff --git a/coretests/tests/num/dec2flt/lemire.rs b/core/tests/num/dec2flt/lemire.rs similarity index 100% rename from coretests/tests/num/dec2flt/lemire.rs rename to core/tests/num/dec2flt/lemire.rs diff --git a/coretests/tests/num/dec2flt/mod.rs b/core/tests/num/dec2flt/mod.rs similarity index 100% rename from coretests/tests/num/dec2flt/mod.rs rename to core/tests/num/dec2flt/mod.rs diff --git a/coretests/tests/num/dec2flt/parse.rs b/core/tests/num/dec2flt/parse.rs similarity index 100% rename from coretests/tests/num/dec2flt/parse.rs rename to core/tests/num/dec2flt/parse.rs diff --git a/coretests/tests/num/float_iter_sum_identity.rs b/core/tests/num/float_iter_sum_identity.rs similarity index 100% rename from coretests/tests/num/float_iter_sum_identity.rs rename to core/tests/num/float_iter_sum_identity.rs diff --git a/coretests/tests/num/flt2dec/estimator.rs b/core/tests/num/flt2dec/estimator.rs similarity index 100% rename from coretests/tests/num/flt2dec/estimator.rs rename to core/tests/num/flt2dec/estimator.rs diff --git a/coretests/tests/num/flt2dec/mod.rs b/core/tests/num/flt2dec/mod.rs similarity index 100% rename from coretests/tests/num/flt2dec/mod.rs rename to core/tests/num/flt2dec/mod.rs diff --git a/coretests/tests/num/flt2dec/random.rs b/core/tests/num/flt2dec/random.rs similarity index 96% rename from coretests/tests/num/flt2dec/random.rs rename to core/tests/num/flt2dec/random.rs index 90042ae03bf7d..99fc23af7ea9d 100644 --- a/coretests/tests/num/flt2dec/random.rs +++ b/core/tests/num/flt2dec/random.rs @@ -84,6 +84,9 @@ where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'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, |_| { @@ -97,6 +100,9 @@ where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'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/coretests/tests/num/flt2dec/strategy/dragon.rs b/core/tests/num/flt2dec/strategy/dragon.rs similarity index 100% rename from coretests/tests/num/flt2dec/strategy/dragon.rs rename to core/tests/num/flt2dec/strategy/dragon.rs diff --git a/coretests/tests/num/flt2dec/strategy/grisu.rs b/core/tests/num/flt2dec/strategy/grisu.rs similarity index 100% rename from coretests/tests/num/flt2dec/strategy/grisu.rs rename to core/tests/num/flt2dec/strategy/grisu.rs diff --git a/core/tests/num/i128.rs b/core/tests/num/i128.rs new file mode 100644 index 0000000000000..1ddd20f33d0b1 --- /dev/null +++ b/core/tests/num/i128.rs @@ -0,0 +1 @@ +int_module!(i128); diff --git a/core/tests/num/i16.rs b/core/tests/num/i16.rs new file mode 100644 index 0000000000000..c7aa9fff964ed --- /dev/null +++ b/core/tests/num/i16.rs @@ -0,0 +1 @@ +int_module!(i16); diff --git a/coretests/tests/num/i32.rs b/core/tests/num/i32.rs similarity index 97% rename from coretests/tests/num/i32.rs rename to core/tests/num/i32.rs index 38d5071f71d6c..efd5b1596a80d 100644 --- a/coretests/tests/num/i32.rs +++ b/core/tests/num/i32.rs @@ -1,4 +1,4 @@ -int_module!(i32, u32); +int_module!(i32); #[test] fn test_arith_operation() { diff --git a/core/tests/num/i64.rs b/core/tests/num/i64.rs new file mode 100644 index 0000000000000..93d23c10adf7e --- /dev/null +++ b/core/tests/num/i64.rs @@ -0,0 +1 @@ +int_module!(i64); diff --git a/core/tests/num/i8.rs b/core/tests/num/i8.rs new file mode 100644 index 0000000000000..887d4f17d25ff --- /dev/null +++ b/core/tests/num/i8.rs @@ -0,0 +1 @@ +int_module!(i8); diff --git a/coretests/tests/num/ieee754.rs b/core/tests/num/ieee754.rs similarity index 100% rename from coretests/tests/num/ieee754.rs rename to core/tests/num/ieee754.rs diff --git a/coretests/tests/num/int_log.rs b/core/tests/num/int_log.rs similarity index 100% rename from coretests/tests/num/int_log.rs rename to core/tests/num/int_log.rs diff --git a/coretests/tests/num/int_macros.rs b/core/tests/num/int_macros.rs similarity index 82% rename from coretests/tests/num/int_macros.rs rename to core/tests/num/int_macros.rs index f13b836378b9e..474d57049ab65 100644 --- a/coretests/tests/num/int_macros.rs +++ b/core/tests/num/int_macros.rs @@ -1,10 +1,8 @@ macro_rules! int_module { - ($T:ident, $U:ident) => { + ($T:ident) => { use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; use core::$T::*; - const UMAX: $U = $U::MAX; - use crate::num; #[test] @@ -357,102 +355,6 @@ macro_rules! int_module { assert_eq_const_safe!((0 as $T).borrowing_sub(MIN, true), (MAX, false)); } - fn test_widening_mul() { - assert_eq_const_safe!(MAX.widening_mul(MAX), (1, MAX / 2)); - assert_eq_const_safe!(MIN.widening_mul(MAX), (MIN as $U, MIN / 2)); - assert_eq_const_safe!(MIN.widening_mul(MIN), (0, MAX / 2 + 1)); - } - - fn test_carrying_mul() { - assert_eq_const_safe!(MAX.carrying_mul(MAX, 0), (1, MAX / 2)); - assert_eq_const_safe!( - MAX.carrying_mul(MAX, MAX), - (UMAX / 2 + 1, MAX / 2) - ); - assert_eq_const_safe!( - MAX.carrying_mul(MAX, MIN), - (UMAX / 2 + 2, MAX / 2 - 1) - ); - assert_eq_const_safe!(MIN.carrying_mul(MAX, 0), (MIN as $U, MIN / 2)); - assert_eq_const_safe!(MIN.carrying_mul(MAX, MAX), (UMAX, MIN / 2)); - assert_eq_const_safe!(MIN.carrying_mul(MAX, MIN), (0, MIN / 2)); - assert_eq_const_safe!(MIN.carrying_mul(MIN, 0), (0, MAX / 2 + 1)); - assert_eq_const_safe!( - MIN.carrying_mul(MIN, MAX), - (UMAX / 2, MAX / 2 + 1) - ); - assert_eq_const_safe!( - MIN.carrying_mul(MIN, MIN), - (UMAX / 2 + 1, MAX / 2) - ); - } - - fn test_carrying_mul_add() { - assert_eq_const_safe!(MAX.carrying_mul_add(MAX, 0, 0), (1, MAX / 2)); - assert_eq_const_safe!( - MAX.carrying_mul_add(MAX, MAX, 0), - (UMAX / 2 + 1, MAX / 2) - ); - assert_eq_const_safe!( - MAX.carrying_mul_add(MAX, MIN, 0), - (UMAX / 2 + 2, MAX / 2 - 1) - ); - assert_eq_const_safe!( - MAX.carrying_mul_add(MAX, MAX, MAX), - (UMAX, MAX / 2) - ); - assert_eq_const_safe!( - MAX.carrying_mul_add(MAX, MAX, MIN), - (0, MAX / 2) - ); - assert_eq_const_safe!( - MAX.carrying_mul_add(MAX, MIN, MIN), - (1, MAX / 2 - 1) - ); - assert_eq_const_safe!( - MIN.carrying_mul_add(MAX, 0, 0), - (MIN as $U, MIN / 2) - ); - assert_eq_const_safe!( - MIN.carrying_mul_add(MAX, MAX, 0), - (UMAX, MIN / 2) - ); - assert_eq_const_safe!(MIN.carrying_mul_add(MAX, MIN, 0), (0, MIN / 2)); - assert_eq_const_safe!( - MIN.carrying_mul_add(MAX, MAX, MAX), - (UMAX / 2 - 1, MIN / 2 + 1) - ); - assert_eq_const_safe!( - MIN.carrying_mul_add(MAX, MAX, MIN), - (UMAX / 2, MIN / 2) - ); - assert_eq_const_safe!( - MIN.carrying_mul_add(MAX, MIN, MIN), - (UMAX / 2 + 1, MIN / 2 - 1) - ); - assert_eq_const_safe!(MIN.carrying_mul_add(MIN, 0, 0), (0, MAX / 2 + 1)); - assert_eq_const_safe!( - MIN.carrying_mul_add(MIN, MAX, 0), - (UMAX / 2, MAX / 2 + 1) - ); - assert_eq_const_safe!( - MIN.carrying_mul_add(MIN, MIN, 0), - (UMAX / 2 + 1, MAX / 2) - ); - assert_eq_const_safe!( - MIN.carrying_mul_add(MIN, MAX, MAX), - (UMAX - 1, MAX / 2 + 1) - ); - assert_eq_const_safe!( - MIN.carrying_mul_add(MIN, MAX, MIN), - (UMAX, MAX / 2) - ); - assert_eq_const_safe!( - MIN.carrying_mul_add(MIN, MIN, MIN), - (0, MAX / 2) - ); - } - fn test_midpoint() { assert_eq_const_safe!(<$T>::midpoint(1, 3), 2); assert_eq_const_safe!(<$T>::midpoint(3, 1), 2); diff --git a/coretests/tests/num/int_sqrt.rs b/core/tests/num/int_sqrt.rs similarity index 100% rename from coretests/tests/num/int_sqrt.rs rename to core/tests/num/int_sqrt.rs diff --git a/coretests/tests/num/midpoint.rs b/core/tests/num/midpoint.rs similarity index 100% rename from coretests/tests/num/midpoint.rs rename to core/tests/num/midpoint.rs diff --git a/coretests/tests/num/mod.rs b/core/tests/num/mod.rs similarity index 100% rename from coretests/tests/num/mod.rs rename to core/tests/num/mod.rs diff --git a/coretests/tests/num/nan.rs b/core/tests/num/nan.rs similarity index 100% rename from coretests/tests/num/nan.rs rename to core/tests/num/nan.rs diff --git a/coretests/tests/num/ops.rs b/core/tests/num/ops.rs similarity index 81% rename from coretests/tests/num/ops.rs rename to core/tests/num/ops.rs index 7b2aad4897808..ae8b938250ec9 100644 --- a/coretests/tests/num/ops.rs +++ b/core/tests/num/ops.rs @@ -51,7 +51,9 @@ macro_rules! test_op { }; } -test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, i128, f32, f64); +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_not_defined_bool, Not::not(true), false, bool); @@ -67,17 +69,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)) => { @@ -91,17 +93,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); } }; } @@ -129,15 +131,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); } }; @@ -154,15 +156,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); } }; @@ -180,11 +182,9 @@ 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, i128, isize, - u8, u16, u32, u64, u128, usize - ); + 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); }; } @@ -195,11 +195,9 @@ macro_rules! test_shift { ($test_name:ident, $op:ident::$method:ident) => { #[test] fn $test_name() { - test_shift!( - $op::$method, - i8, i16, i32, i64, i128, isize, - u8, u16, u32, u64, u128, usize - ); + 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); } }; } @@ -209,11 +207,9 @@ 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, i128, isize, - u8, u16, u32, u64, u128, usize - ); + 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); }; } @@ -224,11 +220,9 @@ 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, i128, isize, - u8, u16, u32, u64, u128, usize - ); + 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); } }; } diff --git a/coretests/tests/num/u128.rs b/core/tests/num/u128.rs similarity index 100% rename from coretests/tests/num/u128.rs rename to core/tests/num/u128.rs diff --git a/coretests/tests/num/u16.rs b/core/tests/num/u16.rs similarity index 100% rename from coretests/tests/num/u16.rs rename to core/tests/num/u16.rs diff --git a/coretests/tests/num/u32.rs b/core/tests/num/u32.rs similarity index 100% rename from coretests/tests/num/u32.rs rename to core/tests/num/u32.rs diff --git a/coretests/tests/num/u64.rs b/core/tests/num/u64.rs similarity index 100% rename from coretests/tests/num/u64.rs rename to core/tests/num/u64.rs diff --git a/coretests/tests/num/u8.rs b/core/tests/num/u8.rs similarity index 100% rename from coretests/tests/num/u8.rs rename to core/tests/num/u8.rs diff --git a/coretests/tests/num/uint_macros.rs b/core/tests/num/uint_macros.rs similarity index 94% rename from coretests/tests/num/uint_macros.rs rename to core/tests/num/uint_macros.rs index 99a2d4cd462b1..ad8e48491e829 100644 --- a/coretests/tests/num/uint_macros.rs +++ b/core/tests/num/uint_macros.rs @@ -277,21 +277,6 @@ macro_rules! uint_module { assert_eq_const_safe!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true)); } - fn test_widening_mul() { - assert_eq_const_safe!($T::MAX.widening_mul($T::MAX), (1, $T::MAX - 1)); - } - - fn test_carrying_mul() { - assert_eq_const_safe!($T::MAX.carrying_mul($T::MAX, 0), (1, $T::MAX - 1)); - assert_eq_const_safe!($T::MAX.carrying_mul($T::MAX, $T::MAX), (0, $T::MAX)); - } - - fn test_carrying_mul_add() { - assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, 0, 0), (1, $T::MAX - 1)); - assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, $T::MAX, 0), (0, $T::MAX)); - assert_eq_const_safe!($T::MAX.carrying_mul_add($T::MAX, $T::MAX, $T::MAX), ($T::MAX, $T::MAX)); - } - fn test_midpoint() { assert_eq_const_safe!(<$T>::midpoint(1, 3), 2); assert_eq_const_safe!(<$T>::midpoint(3, 1), 2); diff --git a/coretests/tests/num/wrapping.rs b/core/tests/num/wrapping.rs similarity index 99% rename from coretests/tests/num/wrapping.rs rename to core/tests/num/wrapping.rs index 0b9fca8455b81..c5a7198839517 100644 --- a/coretests/tests/num/wrapping.rs +++ b/core/tests/num/wrapping.rs @@ -64,12 +64,14 @@ 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/coretests/tests/ops.rs b/core/tests/ops.rs similarity index 100% rename from coretests/tests/ops.rs rename to core/tests/ops.rs diff --git a/coretests/tests/ops/control_flow.rs b/core/tests/ops/control_flow.rs similarity index 100% rename from coretests/tests/ops/control_flow.rs rename to core/tests/ops/control_flow.rs diff --git a/coretests/tests/ops/from_residual.rs b/core/tests/ops/from_residual.rs similarity index 100% rename from coretests/tests/ops/from_residual.rs rename to core/tests/ops/from_residual.rs diff --git a/coretests/tests/option.rs b/core/tests/option.rs similarity index 100% rename from coretests/tests/option.rs rename to core/tests/option.rs diff --git a/coretests/tests/panic.rs b/core/tests/panic.rs similarity index 100% rename from coretests/tests/panic.rs rename to core/tests/panic.rs diff --git a/coretests/tests/panic/location.rs b/core/tests/panic/location.rs similarity index 100% rename from coretests/tests/panic/location.rs rename to core/tests/panic/location.rs diff --git a/coretests/tests/pattern.rs b/core/tests/pattern.rs similarity index 100% rename from coretests/tests/pattern.rs rename to core/tests/pattern.rs diff --git a/coretests/tests/pin.rs b/core/tests/pin.rs similarity index 100% rename from coretests/tests/pin.rs rename to core/tests/pin.rs diff --git a/coretests/tests/pin_macro.rs b/core/tests/pin_macro.rs similarity index 100% rename from coretests/tests/pin_macro.rs rename to core/tests/pin_macro.rs diff --git a/coretests/tests/ptr.rs b/core/tests/ptr.rs similarity index 91% rename from coretests/tests/ptr.rs rename to core/tests/ptr.rs index 345bec345d128..91f8c977d088a 100644 --- a/coretests/tests/ptr.rs +++ b/core/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)); } } @@ -304,7 +304,6 @@ fn test_const_nonnull_new() { #[test] #[cfg(unix)] // printf may not be available on other platforms #[allow(deprecated)] // For SipHasher -#[allow(unpredictable_function_pointer_comparisons)] pub fn test_variadic_fnptr() { use core::ffi; use core::hash::{Hash, SipHasher}; @@ -860,10 +859,7 @@ fn swap_copy_untyped() { } #[test] -fn test_const_copy_ptr() { - // `copy` and `copy_nonoverlapping` are thin layers on top of intrinsics. Ensure they correctly - // deal with pointers even when the pointers cross the boundary from one "element" being copied - // to another. +fn test_const_copy() { const { let ptr1 = &1; let mut ptr2 = &666; @@ -901,65 +897,6 @@ fn test_const_copy_ptr() { }; } -#[test] -fn test_const_swap_ptr() { - // The `swap` functions are implemented in the library, they are not primitives. - // Only `swap_nonoverlapping` takes a count; pointers that cross multiple elements - // are *not* supported. - // We put the pointer at an odd offset in the type and copy them as an array of bytes, - // which should catch most of the ways that the library implementation can get it wrong. - - #[cfg(target_pointer_width = "32")] - type HalfPtr = i16; - #[cfg(target_pointer_width = "64")] - type HalfPtr = i32; - - #[repr(C, packed)] - #[allow(unused)] - struct S { - f1: HalfPtr, - // Crucially this field is at an offset that is not a multiple of the pointer size. - ptr: &'static i32, - // Make sure the entire type does not have a power-of-2 size: - // make it 3 pointers in size. This used to hit a bug in `swap_nonoverlapping`. - f2: [HalfPtr; 3], - } - - // Ensure the entire thing is usize-aligned, so in principle this - // looks like it could be eligible for a `usize` copying loop. - #[cfg_attr(target_pointer_width = "32", repr(align(4)))] - #[cfg_attr(target_pointer_width = "64", repr(align(8)))] - struct A(S); - - const { - let mut s1 = A(S { ptr: &1, f1: 0, f2: [0; 3] }); - let mut s2 = A(S { ptr: &666, f1: 0, f2: [0; 3] }); - - // Swap ptr1 and ptr2, as an array. - type T = [u8; mem::size_of::()]; - unsafe { - ptr::swap(ptr::from_mut(&mut s1).cast::(), ptr::from_mut(&mut s2).cast::()); - } - - // Make sure they still work. - assert!(*s1.0.ptr == 666); - assert!(*s2.0.ptr == 1); - - // Swap them back, again as an array. - unsafe { - ptr::swap_nonoverlapping( - ptr::from_mut(&mut s1).cast::(), - ptr::from_mut(&mut s2).cast::(), - 1, - ); - } - - // Make sure they still work. - assert!(*s1.0.ptr == 1); - assert!(*s2.0.ptr == 666); - }; -} - #[test] fn test_null_array_as_slice() { let arr: *mut [u8; 4] = null_mut(); diff --git a/coretests/tests/result.rs b/core/tests/result.rs similarity index 100% rename from coretests/tests/result.rs rename to core/tests/result.rs diff --git a/coretests/tests/simd.rs b/core/tests/simd.rs similarity index 100% rename from coretests/tests/simd.rs rename to core/tests/simd.rs diff --git a/coretests/tests/slice.rs b/core/tests/slice.rs similarity index 93% rename from coretests/tests/slice.rs rename to core/tests/slice.rs index ea5322da3812d..9ae2bcc852649 100644 --- a/coretests/tests/slice.rs +++ b/core/tests/slice.rs @@ -2,7 +2,6 @@ use core::cell::Cell; use core::cmp::Ordering; use core::mem::MaybeUninit; use core::num::NonZero; -use core::ops::{Range, RangeInclusive}; use core::slice; #[test] @@ -2399,18 +2398,18 @@ fn slice_rsplit_once() { assert_eq!(v.rsplit_once(|&x| x == 0), None); } -macro_rules! split_off_tests { +macro_rules! take_tests { (slice: &[], $($tts:tt)*) => { - split_off_tests!(ty: &[()], slice: &[], $($tts)*); + take_tests!(ty: &[()], slice: &[], $($tts)*); }; (slice: &mut [], $($tts:tt)*) => { - split_off_tests!(ty: &mut [()], slice: &mut [], $($tts)*); + take_tests!(ty: &mut [()], slice: &mut [], $($tts)*); }; (slice: &$slice:expr, $($tts:tt)*) => { - split_off_tests!(ty: &[_], slice: &$slice, $($tts)*); + take_tests!(ty: &[_], slice: &$slice, $($tts)*); }; (slice: &mut $slice:expr, $($tts:tt)*) => { - split_off_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); + take_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 +2424,64 @@ macro_rules! split_off_tests { }; } -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: &[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: &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: &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: &[1, 2], method: split_off_first, - (split_off_first_nonempty, (), Some(&1), &[2]), +take_tests! { + slice: &[1, 2], method: take_first, + (take_first_nonempty, (), Some(&1), &[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: &mut [1, 2], method: take_first_mut, + (take_first_mut_nonempty, (), Some(&mut 1), &mut [2]), } -split_off_tests! { - slice: &[1, 2], method: split_off_last, - (split_off_last_nonempty, (), Some(&2), &[1]), +take_tests! { + slice: &[1, 2], method: take_last, + (take_last_nonempty, (), Some(&2), &[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: &mut [1, 2], method: take_last_mut, + (take_last_mut_nonempty, (), Some(&mut 2), &mut [1]), } -split_off_tests! { - slice: &[], method: split_off_first, - (split_off_first_empty, (), None, &[]), +take_tests! { + slice: &[], method: take_first, + (take_first_empty, (), None, &[]), } -split_off_tests! { - slice: &mut [], method: split_off_first_mut, - (split_off_first_mut_empty, (), None, &mut []), +take_tests! { + slice: &mut [], method: take_first_mut, + (take_first_mut_empty, (), None, &mut []), } -split_off_tests! { - slice: &[], method: split_off_last, - (split_off_last_empty, (), None, &[]), +take_tests! { + slice: &[], method: take_last, + (take_last_empty, (), None, &[]), } -split_off_tests! { - slice: &mut [], method: split_off_last_mut, - (split_off_last_mut_empty, (), None, &mut []), +take_tests! { + slice: &mut [], method: take_last_mut, + (take_last_mut_empty, (), None, &mut []), } #[cfg(not(miri))] // unused in Miri @@ -2497,19 +2496,19 @@ macro_rules! empty_max_mut { } #[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -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), +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), } #[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -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!()), +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!()), } #[test] @@ -2554,14 +2553,6 @@ fn test_get_many_mut_normal_2() { *a += 10; *b += 100; assert_eq!(v, vec![101, 2, 3, 14, 5]); - - let [a, b] = v.get_many_mut([0..=1, 2..=2]).unwrap(); - assert_eq!(a, &mut [101, 2][..]); - assert_eq!(b, &mut [3][..]); - a[0] += 10; - a[1] += 20; - b[0] += 100; - assert_eq!(v, vec![111, 22, 103, 14, 5]); } #[test] @@ -2572,23 +2563,12 @@ fn test_get_many_mut_normal_3() { *b += 100; *c += 1000; assert_eq!(v, vec![11, 2, 1003, 4, 105]); - - let [a, b, c] = v.get_many_mut([0..1, 4..5, 1..4]).unwrap(); - assert_eq!(a, &mut [11][..]); - assert_eq!(b, &mut [105][..]); - assert_eq!(c, &mut [2, 1003, 4][..]); - a[0] += 10; - b[0] += 100; - c[0] += 1000; - assert_eq!(v, vec![21, 1002, 1003, 4, 205]); } #[test] fn test_get_many_mut_empty() { let mut v = vec![1, 2, 3, 4, 5]; - let [] = v.get_many_mut::([]).unwrap(); - let [] = v.get_many_mut::, 0>([]).unwrap(); - let [] = v.get_many_mut::, 0>([]).unwrap(); + let [] = v.get_many_mut([]).unwrap(); assert_eq!(v, vec![1, 2, 3, 4, 5]); } @@ -2626,54 +2606,6 @@ fn test_get_many_mut_duplicate() { assert!(v.get_many_mut([1, 3, 3, 4]).is_err()); } -#[test] -fn test_get_many_mut_range_oob() { - let mut v = vec![1, 2, 3, 4, 5]; - assert!(v.get_many_mut([0..6]).is_err()); - assert!(v.get_many_mut([5..6]).is_err()); - assert!(v.get_many_mut([6..6]).is_err()); - assert!(v.get_many_mut([0..=5]).is_err()); - assert!(v.get_many_mut([0..=6]).is_err()); - assert!(v.get_many_mut([5..=5]).is_err()); -} - -#[test] -fn test_get_many_mut_range_overlapping() { - let mut v = vec![1, 2, 3, 4, 5]; - assert!(v.get_many_mut([0..1, 0..2]).is_err()); - assert!(v.get_many_mut([0..1, 1..2, 0..1]).is_err()); - assert!(v.get_many_mut([0..3, 1..1]).is_err()); - assert!(v.get_many_mut([0..3, 1..2]).is_err()); - assert!(v.get_many_mut([0..=0, 2..=2, 0..=1]).is_err()); - assert!(v.get_many_mut([0..=4, 0..=0]).is_err()); - assert!(v.get_many_mut([4..=4, 0..=0, 3..=4]).is_err()); -} - -#[test] -fn test_get_many_mut_range_empty_at_edge() { - let mut v = vec![1, 2, 3, 4, 5]; - assert_eq!( - v.get_many_mut([0..0, 0..5, 5..5]), - Ok([&mut [][..], &mut [1, 2, 3, 4, 5], &mut []]), - ); - assert_eq!( - v.get_many_mut([0..0, 0..1, 1..1, 1..2, 2..2, 2..3, 3..3, 3..4, 4..4, 4..5, 5..5]), - Ok([ - &mut [][..], - &mut [1], - &mut [], - &mut [2], - &mut [], - &mut [3], - &mut [], - &mut [4], - &mut [], - &mut [5], - &mut [], - ]), - ); -} - #[test] fn test_slice_from_raw_parts_in_const() { static FANCY: i32 = 4; diff --git a/coretests/tests/str.rs b/core/tests/str.rs similarity index 100% rename from coretests/tests/str.rs rename to core/tests/str.rs diff --git a/coretests/tests/str_lossy.rs b/core/tests/str_lossy.rs similarity index 100% rename from coretests/tests/str_lossy.rs rename to core/tests/str_lossy.rs diff --git a/coretests/tests/task.rs b/core/tests/task.rs similarity index 100% rename from coretests/tests/task.rs rename to core/tests/task.rs diff --git a/coretests/tests/time.rs b/core/tests/time.rs similarity index 100% rename from coretests/tests/time.rs rename to core/tests/time.rs diff --git a/coretests/tests/tuple.rs b/core/tests/tuple.rs similarity index 100% rename from coretests/tests/tuple.rs rename to core/tests/tuple.rs diff --git a/coretests/tests/unicode.rs b/core/tests/unicode.rs similarity index 100% rename from coretests/tests/unicode.rs rename to core/tests/unicode.rs diff --git a/coretests/tests/waker.rs b/core/tests/waker.rs similarity index 100% rename from coretests/tests/waker.rs rename to core/tests/waker.rs diff --git a/coretests/Cargo.toml b/coretests/Cargo.toml deleted file mode 100644 index ec940abea1171..0000000000000 --- a/coretests/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[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/coretests/lib.rs b/coretests/lib.rs deleted file mode 100644 index b49208cd4eb3a..0000000000000 --- a/coretests/lib.rs +++ /dev/null @@ -1 +0,0 @@ -// Intentionally left empty. diff --git a/coretests/tests/bstr.rs b/coretests/tests/bstr.rs deleted file mode 100644 index cd4d69d6b337d..0000000000000 --- a/coretests/tests/bstr.rs +++ /dev/null @@ -1,52 +0,0 @@ -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/coretests/tests/fmt/mod.rs b/coretests/tests/fmt/mod.rs deleted file mode 100644 index 025c69c4f6236..0000000000000 --- a/coretests/tests/fmt/mod.rs +++ /dev/null @@ -1,82 +0,0 @@ -mod builders; -mod float; -mod num; - -#[test] -fn test_format_flags() { - // No residual flags left by pointer formatting - let p = "".as_ptr(); - assert_eq!(format!("{:p} {:x}", p, 16), format!("{p:p} 10")); - - assert_eq!(format!("{: >3}", 'a'), " a"); -} - -#[test] -fn test_pointer_formats_data_pointer() { - let b: &[u8] = b""; - let s: &str = ""; - assert_eq!(format!("{s:p}"), format!("{:p}", s.as_ptr())); - assert_eq!(format!("{b:p}"), format!("{:p}", b.as_ptr())); -} - -#[test] -fn test_estimated_capacity() { - assert_eq!(format_args!("").estimated_capacity(), 0); - assert_eq!(format_args!("{}", { "" }).estimated_capacity(), 0); - assert_eq!(format_args!("Hello").estimated_capacity(), 5); - assert_eq!(format_args!("Hello, {}!", { "" }).estimated_capacity(), 16); - assert_eq!(format_args!("{}, hello!", { "World" }).estimated_capacity(), 0); - assert_eq!(format_args!("{}. 16-bytes piece", { "World" }).estimated_capacity(), 32); -} - -#[test] -fn pad_integral_resets() { - struct Bar; - - impl core::fmt::Display for Bar { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - "1".fmt(f)?; - f.pad_integral(true, "", "5")?; - "1".fmt(f) - } - } - - assert_eq!(format!("{Bar:<03}"), "1 0051 "); -} - -#[test] -fn test_maybe_uninit_short() { - // Ensure that the trimmed `MaybeUninit` Debug implementation doesn't break - let x = core::mem::MaybeUninit::new(0u32); - assert_eq!(format!("{x:?}"), "MaybeUninit"); -} - -#[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)] { - for alternate in [true, false] { - for sign_aware_zero_pad in [true, false] { - for debug_as_hex in [None, Some(DebugAsHex::Lower), Some(DebugAsHex::Upper)] { - let mut formatting_options = FormattingOptions::new(); - formatting_options - .sign(sign) - .sign_aware_zero_pad(sign_aware_zero_pad) - .alternate(alternate) - .debug_as_hex(debug_as_hex); - - assert_eq!(formatting_options.get_sign(), sign); - assert_eq!(formatting_options.get_alternate(), alternate); - assert_eq!(formatting_options.get_sign_aware_zero_pad(), sign_aware_zero_pad); - assert_eq!(formatting_options.get_debug_as_hex(), debug_as_hex); - } - } - } - } -} diff --git a/coretests/tests/macros.rs b/coretests/tests/macros.rs deleted file mode 100644 index b30a40b7df28e..0000000000000 --- a/coretests/tests/macros.rs +++ /dev/null @@ -1,206 +0,0 @@ -#![allow(unused_must_use)] - -#[allow(dead_code)] -trait Trait { - fn blah(&self); -} - -#[allow(dead_code)] -struct Struct; - -impl Trait for Struct { - cfg_match! { - feature = "blah" => { - fn blah(&self) { - unimplemented!(); - } - } - _ => { - fn blah(&self) { - unimplemented!(); - } - } - } -} - -#[test] -fn assert_eq_trailing_comma() { - assert_eq!(1, 1,); -} - -#[test] -fn assert_escape() { - assert!(r#"☃\backslash"#.contains("\\")); -} - -#[test] -fn assert_ne_trailing_comma() { - assert_ne!(1, 2,); -} - -#[rustfmt::skip] -#[test] -fn matches_leading_pipe() { - matches!(1, | 1 | 2 | 3); -} - -#[test] -fn cfg_match_basic() { - cfg_match! { - target_pointer_width = "64" => { fn f0_() -> bool { true }} - } - - cfg_match! { - unix => { fn f1_() -> bool { true } } - any(target_os = "macos", target_os = "linux") => { fn f1_() -> bool { false }} - } - - cfg_match! { - target_pointer_width = "32" => { fn f2_() -> bool { false } } - target_pointer_width = "64" => { fn f2_() -> bool { true } } - } - - cfg_match! { - target_pointer_width = "16" => { fn f3_() -> i32 { 1 } } - _ => { fn f3_() -> i32 { 2 }} - } - - #[cfg(target_pointer_width = "64")] - assert!(f0_()); - - #[cfg(unix)] - assert!(f1_()); - - #[cfg(target_pointer_width = "32")] - assert!(!f2_()); - #[cfg(target_pointer_width = "64")] - assert!(f2_()); - - #[cfg(not(target_pointer_width = "16"))] - assert_eq!(f3_(), 2); -} - -#[test] -fn cfg_match_debug_assertions() { - cfg_match! { - debug_assertions => { - assert!(cfg!(debug_assertions)); - assert_eq!(4, 2+2); - } - _ => { - assert!(cfg!(not(debug_assertions))); - assert_eq!(10, 5+5); - } - } -} - -#[cfg(target_pointer_width = "64")] -#[test] -fn cfg_match_no_duplication_on_64() { - cfg_match! { - windows => { - fn foo() {} - } - unix => { - fn foo() {} - } - target_pointer_width = "64" => { - fn foo() {} - } - } - foo(); -} - -#[test] -fn cfg_match_options() { - cfg_match! { - test => { - use core::option::Option as Option2; - fn works1() -> Option2 { Some(1) } - } - _ => { fn works1() -> Option { None } } - } - - cfg_match! { - feature = "foo" => { fn works2() -> bool { false } } - test => { fn works2() -> bool { true } } - _ => { fn works2() -> bool { false } } - } - - cfg_match! { - feature = "foo" => { fn works3() -> bool { false } } - _ => { fn works3() -> bool { true } } - } - - cfg_match! { - test => { - use core::option::Option as Option3; - fn works4() -> Option3 { Some(1) } - } - } - - cfg_match! { - feature = "foo" => { fn works5() -> bool { false } } - test => { fn works5() -> bool { true } } - } - - assert!(works1().is_some()); - assert!(works2()); - assert!(works3()); - assert!(works4().is_some()); - assert!(works5()); -} - -#[test] -fn cfg_match_two_functions() { - cfg_match! { - target_pointer_width = "64" => { - fn foo1() {} - fn bar1() {} - } - _ => { - fn foo2() {} - fn bar2() {} - } - } - - #[cfg(target_pointer_width = "64")] - { - foo1(); - bar1(); - } - #[cfg(not(target_pointer_width = "64"))] - { - foo2(); - bar2(); - } -} - -fn _accepts_expressions() -> i32 { - cfg_match! { - unix => { 1 } - _ => { 2 } - } -} - -// The current implementation expands to a macro call, which allows the use of expression -// statements. -fn _allows_stmt_expr_attributes() { - let one = 1; - let two = 2; - cfg_match! { - unix => { one * two; } - _ => { one + two; } - } -} - -fn _expression() { - let _ = cfg_match!({ - windows => { - " XP" - } - _ => { - "" - } - }); -} diff --git a/coretests/tests/num/i128.rs b/coretests/tests/num/i128.rs deleted file mode 100644 index 745fee05164c9..0000000000000 --- a/coretests/tests/num/i128.rs +++ /dev/null @@ -1 +0,0 @@ -int_module!(i128, u128); diff --git a/coretests/tests/num/i16.rs b/coretests/tests/num/i16.rs deleted file mode 100644 index 6acb8371b87d8..0000000000000 --- a/coretests/tests/num/i16.rs +++ /dev/null @@ -1 +0,0 @@ -int_module!(i16, u16); diff --git a/coretests/tests/num/i64.rs b/coretests/tests/num/i64.rs deleted file mode 100644 index f8dd5f9be7fe2..0000000000000 --- a/coretests/tests/num/i64.rs +++ /dev/null @@ -1 +0,0 @@ -int_module!(i64, u64); diff --git a/coretests/tests/num/i8.rs b/coretests/tests/num/i8.rs deleted file mode 100644 index a10906618c937..0000000000000 --- a/coretests/tests/num/i8.rs +++ /dev/null @@ -1 +0,0 @@ -int_module!(i8, u8); diff --git a/panic_unwind/Cargo.toml b/panic_unwind/Cargo.toml index c2abb79192e9f..6d1f9764efbfd 100644 --- a/panic_unwind/Cargo.toml +++ b/panic_unwind/Cargo.toml @@ -23,4 +23,7 @@ libc = { version = "0.2", default-features = false } [lints.rust.unexpected_cfgs] level = "warn" -check-cfg = ['cfg(emscripten_wasm_eh)'] +check-cfg = [ + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', +] diff --git a/panic_unwind/src/dummy.rs b/panic_unwind/src/dummy.rs index a0d6876691833..a4bcd216c60f0 100644 --- a/panic_unwind/src/dummy.rs +++ b/panic_unwind/src/dummy.rs @@ -6,10 +6,10 @@ use alloc::boxed::Box; use core::any::Any; use core::intrinsics; -pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { +pub unsafe fn cleanup(_ptr: *mut u8) -> Box { intrinsics::abort() } -pub(crate) unsafe fn panic(_data: Box) -> u32 { +pub unsafe fn panic(_data: Box) -> u32 { intrinsics::abort() } diff --git a/panic_unwind/src/emcc.rs b/panic_unwind/src/emcc.rs index 86906b46d66ba..b986fc1c2a829 100644 --- a/panic_unwind/src/emcc.rs +++ b/panic_unwind/src/emcc.rs @@ -64,7 +64,7 @@ struct Exception { data: Option>, } -pub(crate) unsafe fn cleanup(ptr: *mut u8) -> Box { +pub unsafe fn cleanup(ptr: *mut u8) -> Box { // intrinsics::try actually gives us a pointer to this structure. #[repr(C)] struct CatchData { @@ -93,19 +93,16 @@ pub(crate) unsafe fn cleanup(ptr: *mut u8) -> Box { out } -pub(crate) unsafe fn panic(data: Box) -> u32 { +pub unsafe fn panic(data: Box) -> u32 { let exception = __cxa_allocate_exception(mem::size_of::()) as *mut Exception; 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); } diff --git a/panic_unwind/src/gcc.rs b/panic_unwind/src/gcc.rs index e478f6c5fc86c..925af6c08322e 100644 --- a/panic_unwind/src/gcc.rs +++ b/panic_unwind/src/gcc.rs @@ -5,7 +5,7 @@ //! documents linked from it. //! These are also good reads: //! * -//! * +//! * //! * //! //! ## A brief summary @@ -58,7 +58,7 @@ struct Exception { cause: Box, } -pub(crate) unsafe fn panic(data: Box) -> u32 { +pub unsafe fn panic(data: Box) -> u32 { let exception = Box::new(Exception { _uwe: uw::_Unwind_Exception { exception_class: RUST_EXCEPTION_CLASS, @@ -82,7 +82,7 @@ pub(crate) unsafe fn panic(data: Box) -> u32 { } } -pub(crate) unsafe fn cleanup(ptr: *mut u8) -> Box { +pub unsafe fn cleanup(ptr: *mut u8) -> Box { let exception = ptr as *mut uw::_Unwind_Exception; if (*exception).exception_class != RUST_EXCEPTION_CLASS { uw::_Unwind_DeleteException(exception); diff --git a/panic_unwind/src/hermit.rs b/panic_unwind/src/hermit.rs index 7e08ad66577f1..69b9edb77c564 100644 --- a/panic_unwind/src/hermit.rs +++ b/panic_unwind/src/hermit.rs @@ -5,16 +5,16 @@ use alloc::boxed::Box; use core::any::Any; -pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { +pub unsafe fn cleanup(_ptr: *mut u8) -> Box { extern "C" { - fn __rust_abort() -> !; + pub fn __rust_abort() -> !; } __rust_abort(); } -pub(crate) unsafe fn panic(_data: Box) -> u32 { +pub unsafe fn panic(_data: Box) -> u32 { extern "C" { - fn __rust_abort() -> !; + pub fn __rust_abort() -> !; } __rust_abort(); } diff --git a/panic_unwind/src/lib.rs b/panic_unwind/src/lib.rs index d682816419565..1981675f40922 100644 --- a/panic_unwind/src/lib.rs +++ b/panic_unwind/src/lib.rs @@ -25,15 +25,13 @@ // `real_imp` is unused with Miri, so silence warnings. #![cfg_attr(miri, allow(dead_code))] #![allow(internal_features)] -#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))] -#![warn(unreachable_pub)] use alloc::boxed::Box; use core::any::Any; use core::panic::PanicPayload; cfg_if::cfg_if! { - if #[cfg(all(target_os = "emscripten", not(emscripten_wasm_eh)))] { + if #[cfg(target_os = "emscripten")] { #[path = "emcc.rs"] mod imp; } else if #[cfg(target_os = "hermit")] { @@ -48,7 +46,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(any(target_os = "espidf", target_os = "nuttx"))), + all(target_family = "unix", not(any(target_os = "espidf", target_os = "rtems", target_os = "nuttx"))), all(target_vendor = "fortanix", target_env = "sgx"), target_family = "wasm", ))] { diff --git a/panic_unwind/src/miri.rs b/panic_unwind/src/miri.rs index a86f0e91eefcc..695adadd59b55 100644 --- a/panic_unwind/src/miri.rs +++ b/panic_unwind/src/miri.rs @@ -12,14 +12,14 @@ extern "Rust" { fn miri_start_unwind(payload: *mut u8) -> !; } -pub(crate) unsafe fn panic(payload: Box) -> u32 { +pub unsafe fn panic(payload: Box) -> u32 { // The payload we pass to `miri_start_unwind` will be exactly the argument we get // in `cleanup` below. So we just box it up once, to get something pointer-sized. let payload_box: Payload = Box::new(payload); miri_start_unwind(Box::into_raw(payload_box) as *mut u8) } -pub(crate) unsafe fn cleanup(payload_box: *mut u8) -> Box { +pub unsafe fn cleanup(payload_box: *mut u8) -> Box { // Recover the underlying `Box`. let payload_box: Payload = Box::from_raw(payload_box as *mut _); *payload_box diff --git a/panic_unwind/src/seh.rs b/panic_unwind/src/seh.rs index 21bfe74e1a259..565a2b8c573b4 100644 --- a/panic_unwind/src/seh.rs +++ b/panic_unwind/src/seh.rs @@ -111,18 +111,18 @@ struct Exception { mod imp { #[repr(transparent)] #[derive(Copy, Clone)] - pub(super) struct ptr_t(*mut u8); + pub struct ptr_t(*mut u8); impl ptr_t { - pub(super) const fn null() -> Self { + pub const fn null() -> Self { Self(core::ptr::null_mut()) } - pub(super) const fn new(ptr: *mut u8) -> Self { + pub const fn new(ptr: *mut u8) -> Self { Self(ptr) } - pub(super) const fn raw(self) -> *mut u8 { + pub const fn raw(self) -> *mut u8 { self.0 } } @@ -133,18 +133,18 @@ mod imp { // On 64-bit systems, SEH represents pointers as 32-bit offsets from `__ImageBase`. #[repr(transparent)] #[derive(Copy, Clone)] - pub(super) struct ptr_t(u32); + pub struct ptr_t(u32); extern "C" { - static __ImageBase: u8; + pub static __ImageBase: u8; } impl ptr_t { - pub(super) const fn null() -> Self { + pub const fn null() -> Self { Self(0) } - pub(super) fn new(ptr: *mut u8) -> Self { + pub fn new(ptr: *mut u8) -> Self { // We need to expose the provenance of the pointer because it is not carried by // the `u32`, while the FFI needs to have this provenance to excess our statics. // @@ -159,7 +159,7 @@ mod imp { Self(offset as u32) } - pub(super) const fn raw(self) -> u32 { + pub const fn raw(self) -> u32 { self.0 } } @@ -168,7 +168,7 @@ mod imp { use imp::ptr_t; #[repr(C)] -struct _ThrowInfo { +pub struct _ThrowInfo { pub attributes: c_uint, pub pmfnUnwind: ptr_t, pub pForwardCompat: ptr_t, @@ -176,13 +176,13 @@ struct _ThrowInfo { } #[repr(C)] -struct _CatchableTypeArray { +pub struct _CatchableTypeArray { pub nCatchableTypes: c_int, pub arrayOfCatchableTypes: [ptr_t; 1], } #[repr(C)] -struct _CatchableType { +pub struct _CatchableType { pub properties: c_uint, pub pType: ptr_t, pub thisDisplacement: _PMD, @@ -191,14 +191,14 @@ struct _CatchableType { } #[repr(C)] -struct _PMD { +pub struct _PMD { pub mdisp: c_int, pub pdisp: c_int, pub vdisp: c_int, } #[repr(C)] -struct _TypeDescriptor { +pub struct _TypeDescriptor { pub pVFTable: *const u8, pub spare: *mut u8, pub name: [u8; 11], @@ -288,7 +288,9 @@ cfg_if::cfg_if! { } } -pub(crate) unsafe fn panic(data: Box) -> u32 { +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#[allow(static_mut_refs)] +pub unsafe fn panic(data: Box) -> u32 { use core::intrinsics::atomic_store_seqcst; // _CxxThrowException executes entirely on this stack frame, so there's no @@ -350,7 +352,7 @@ pub(crate) unsafe fn panic(data: Box) -> u32 { _CxxThrowException(throw_ptr, (&raw mut THROW_INFO) as *mut _); } -pub(crate) unsafe fn cleanup(payload: *mut u8) -> Box { +pub unsafe fn cleanup(payload: *mut u8) -> Box { // A null payload here means that we got here from the catch (...) of // __rust_try. This happens when a non-Rust foreign exception is caught. if payload.is_null() { diff --git a/portable-simd/crates/core_simd/src/vendor/arm.rs b/portable-simd/crates/core_simd/src/vendor/arm.rs index 3dc54481b6fd4..f8878d11f094d 100644 --- a/portable-simd/crates/core_simd/src/vendor/arm.rs +++ b/portable-simd/crates/core_simd/src/vendor/arm.rs @@ -48,6 +48,17 @@ mod neon { from_transmute! { unsafe u64x2 => poly64x2_t } } +#[cfg(any( + all(target_feature = "v5te", not(target_feature = "mclass")), + all(target_feature = "mclass", target_feature = "dsp"), +))] +mod dsp { + use super::*; + + from_transmute! { unsafe Simd => uint16x2_t } + from_transmute! { unsafe Simd => int16x2_t } +} + #[cfg(any( all(target_feature = "v6", not(target_feature = "mclass")), all(target_feature = "mclass", target_feature = "dsp"), @@ -57,8 +68,6 @@ mod simd32 { from_transmute! { unsafe Simd => uint8x4_t } from_transmute! { unsafe Simd => int8x4_t } - from_transmute! { unsafe Simd => uint16x2_t } - from_transmute! { unsafe Simd => int16x2_t } } #[cfg(all( diff --git a/proc_macro/src/bridge/arena.rs b/proc_macro/src/bridge/arena.rs index 29636e793f614..1d5986093c8a4 100644 --- a/proc_macro/src/bridge/arena.rs +++ b/proc_macro/src/bridge/arena.rs @@ -102,7 +102,7 @@ impl Arena { #[allow(clippy::mut_from_ref)] // arena allocator pub(crate) fn alloc_str<'a>(&'a self, string: &str) -> &'a mut str { let alloc = self.alloc_raw(string.len()); - let bytes = alloc.write_copy_of_slice(string.as_bytes()); + let bytes = MaybeUninit::copy_from_slice(alloc, string.as_bytes()); // SAFETY: we convert from `&str` to `&[u8]`, clone it into the arena, // and immediately convert the clone back to `&str`. diff --git a/proc_macro/src/bridge/closure.rs b/proc_macro/src/bridge/closure.rs index 524fdf53d6b7e..d371ae3cea098 100644 --- a/proc_macro/src/bridge/closure.rs +++ b/proc_macro/src/bridge/closure.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; #[repr(C)] -pub(super) struct Closure<'a, A, R> { +pub 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(super) fn call(&mut self, arg: A) -> R { + pub fn call(&mut self, arg: A) -> R { unsafe { (self.call)(self.env, arg) } } } diff --git a/proc_macro/src/bridge/fxhash.rs b/proc_macro/src/bridge/fxhash.rs index 5f6b3d1b929e4..74a41451825ff 100644 --- a/proc_macro/src/bridge/fxhash.rs +++ b/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(super) type FxHashMap = HashMap>; +pub type FxHashMap = HashMap>; /// 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 @@ -22,8 +22,7 @@ pub(super) type FxHashMap = HashMap>; /// out-performs an FNV-based hash within rustc itself -- the collision rate is /// 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(super) struct FxHasher { +pub struct FxHasher { hash: usize, } @@ -32,6 +31,13 @@ const K: usize = 0x9e3779b9; #[cfg(target_pointer_width = "64")] const K: usize = 0x517cc1b727220a95; +impl Default for FxHasher { + #[inline] + fn default() -> FxHasher { + FxHasher { hash: 0 } + } +} + impl FxHasher { #[inline] fn add_to_hash(&mut self, i: usize) { diff --git a/proc_macro/src/bridge/rpc.rs b/proc_macro/src/bridge/rpc.rs index 85fd7d138585c..202a8e04543b2 100644 --- a/proc_macro/src/bridge/rpc.rs +++ b/proc_macro/src/bridge/rpc.rs @@ -67,7 +67,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* + $(pub 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(crate) const $variant: u8 = Tag::$variant as u8;)* + $(pub const $variant: u8 = Tag::$variant as u8;)* } match u8::decode(r, s) { diff --git a/proc_macro/src/bridge/selfless_reify.rs b/proc_macro/src/bridge/selfless_reify.rs index 312a79152e23b..907ad256e4b43 100644 --- a/proc_macro/src/bridge/selfless_reify.rs +++ b/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(super) const fn $name< + $(pub const fn $name< $($($param,)*)? F: Fn($($arg_ty),*) -> $ret_ty + Copy >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { diff --git a/proc_macro/src/bridge/symbol.rs b/proc_macro/src/bridge/symbol.rs index 6a1cecd69fb5f..edad6e7ac393f 100644 --- a/proc_macro/src/bridge/symbol.rs +++ b/proc_macro/src/bridge/symbol.rs @@ -91,6 +91,12 @@ impl fmt::Debug for Symbol { } } +impl ToString for Symbol { + fn to_string(&self) -> String { + self.with(|s| s.to_owned()) + } +} + impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.with(|s| fmt::Display::fmt(s, f)) diff --git a/proc_macro/src/lib.rs b/proc_macro/src/lib.rs index 6611ce30a1b01..15770248b3106 100644 --- a/proc_macro/src/lib.rs +++ b/proc_macro/src/lib.rs @@ -19,6 +19,10 @@ )] #![doc(rust_logo)] #![feature(rustdoc_internals)] +// This library is copied into rust-analyzer to allow loading rustc compiled proc macros. +// Please avoid unstable features where possible to minimize the amount of changes necessary +// to make it compile with rust-analyzer on stable. +#![feature(rustc_allow_const_fn_unstable)] #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] @@ -27,12 +31,12 @@ #![feature(panic_can_unwind)] #![feature(restricted_std)] #![feature(rustc_attrs)] +#![feature(min_specialization)] #![feature(extend_one)] #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] #![warn(rustdoc::unescaped_backticks)] -#![warn(unreachable_pub)] #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] @@ -182,6 +186,16 @@ impl FromStr for TokenStream { } } +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[doc(hidden)] +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for TokenStream { + fn to_string(&self) -> String { + self.0.as_ref().map(|t| t.to_string()).unwrap_or_default() + } +} + /// Prints the token stream as a string that is supposed to be losslessly convertible back /// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s /// with `Delimiter::None` delimiters and negative numeric literals. @@ -197,10 +211,7 @@ impl FromStr for TokenStream { impl fmt::Display for TokenStream { #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0 { - Some(ts) => write!(f, "{}", ts.to_string()), - None => Ok(()), - } + f.write_str(&self.to_string()) } } @@ -420,7 +431,7 @@ pub mod token_stream { /// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. /// To quote `$` itself, use `$$`. #[unstable(feature = "proc_macro_quote", issue = "54722")] -#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals, proc_macro_totokens)] +#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals)] #[rustc_builtin_macro] pub macro quote($($t:tt)*) { /* compiler built-in */ @@ -746,6 +757,21 @@ impl From for TokenTree { } } +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[doc(hidden)] +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for TokenTree { + fn to_string(&self) -> String { + match *self { + TokenTree::Group(ref t) => t.to_string(), + TokenTree::Ident(ref t) => t.to_string(), + TokenTree::Punct(ref t) => t.to_string(), + TokenTree::Literal(ref t) => t.to_string(), + } + } +} + /// Prints the token tree as a string that is supposed to be losslessly convertible back /// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s /// with `Delimiter::None` delimiters and negative numeric literals. @@ -761,12 +787,7 @@ impl From for TokenTree { impl fmt::Display for TokenTree { #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TokenTree::Group(t) => write!(f, "{t}"), - TokenTree::Ident(t) => write!(f, "{t}"), - TokenTree::Punct(t) => write!(f, "{t}"), - TokenTree::Literal(t) => write!(f, "{t}"), - } + f.write_str(&self.to_string()) } } @@ -892,6 +913,16 @@ impl Group { } } +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +#[doc(hidden)] +#[stable(feature = "proc_macro_lib", since = "1.15.0")] +impl ToString for Group { + fn to_string(&self) -> String { + TokenStream::from(TokenTree::from(self.clone())).to_string() + } +} + /// Prints the group as a string that should be losslessly convertible back /// into the same group (modulo spans), except for possibly `TokenTree::Group`s /// with `Delimiter::None` delimiters. @@ -899,7 +930,7 @@ impl Group { impl fmt::Display for Group { #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", TokenStream::from(TokenTree::from(self.clone()))) + f.write_str(&self.to_string()) } } @@ -1005,6 +1036,14 @@ impl Punct { } } +#[doc(hidden)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl ToString for Punct { + fn to_string(&self) -> String { + self.as_char().to_string() + } +} + /// Prints the punctuation character as a string that should be losslessly convertible /// back into the same character. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] @@ -1100,6 +1139,14 @@ impl Ident { } } +#[doc(hidden)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl ToString for Ident { + fn to_string(&self) -> String { + self.0.sym.with(|sym| if self.0.is_raw { ["r#", sym].concat() } else { sym.to_owned() }) + } +} + /// Prints the identifier as a string that should be losslessly convertible back /// into the same identifier. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] @@ -1474,6 +1521,14 @@ impl FromStr for Literal { } } +#[doc(hidden)] +#[stable(feature = "proc_macro_lib2", since = "1.29.0")] +impl ToString for Literal { + fn to_string(&self) -> String { + self.with_stringify_parts(|parts| parts.concat()) + } +} + /// Prints the literal as a string that should be losslessly convertible /// back into the same literal (except for possible rounding for floating point literals). #[stable(feature = "proc_macro_lib2", since = "1.29.0")] diff --git a/proc_macro/src/quote.rs b/proc_macro/src/quote.rs index bcb15912bb65e..04fa696d5e6be 100644 --- a/proc_macro/src/quote.rs +++ b/proc_macro/src/quote.rs @@ -4,14 +4,12 @@ //! This quasiquoter uses macros 2.0 hygiene to reliably access //! items from `proc_macro`, to build a `proc_macro::TokenStream`. -use crate::{ - Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree, -}; +use crate::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; -macro_rules! minimal_quote_tt { - (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, minimal_quote!($($t)*)) }; - ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, minimal_quote!($($t)*)) }; - ({$($t:tt)*}) => { Group::new(Delimiter::Brace, minimal_quote!($($t)*)) }; +macro_rules! quote_tt { + (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) }; + ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) }; + ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) }; (,) => { Punct::new(',', Spacing::Alone) }; (.) => { Punct::new('.', Spacing::Alone) }; (;) => { Punct::new(';', Spacing::Alone) }; @@ -23,20 +21,21 @@ macro_rules! minimal_quote_tt { ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; } -macro_rules! minimal_quote_ts { +macro_rules! quote_ts { ((@ $($t:tt)*)) => { $($t)* }; (::) => { - { - let mut c = ( - TokenTree::from(Punct::new(':', Spacing::Joint)), - TokenTree::from(Punct::new(':', Spacing::Alone)) - ); - c.0.set_span(Span::def_site()); - c.1.set_span(Span::def_site()); - [c.0, c.1].into_iter().collect::() - } + [ + TokenTree::from(Punct::new(':', Spacing::Joint)), + TokenTree::from(Punct::new(':', Spacing::Alone)), + ].iter() + .cloned() + .map(|mut x| { + x.set_span(Span::def_site()); + x + }) + .collect::() }; - ($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) }; + ($t:tt) => { TokenTree::from(quote_tt!($t)) }; } /// Simpler version of the real `quote!` macro, implemented solely @@ -47,14 +46,12 @@ macro_rules! minimal_quote_ts { /// /// Note: supported tokens are a subset of the real `quote!`, but /// unquoting is different: instead of `$x`, this uses `(@ expr)`. -macro_rules! minimal_quote { +macro_rules! quote { + () => { TokenStream::new() }; ($($t:tt)*) => { - { - #[allow(unused_mut)] // In case the expansion is empty - let mut ts = TokenStream::new(); - $(ToTokens::to_tokens(&minimal_quote_ts!($t), &mut ts);)* - ts - } + [ + $(TokenStream::from(quote_ts!($t)),)* + ].iter().cloned().collect::() }; } @@ -65,66 +62,52 @@ macro_rules! minimal_quote { #[unstable(feature = "proc_macro_quote", issue = "54722")] pub fn quote(stream: TokenStream) -> TokenStream { if stream.is_empty() { - return minimal_quote!(crate::TokenStream::new()); + return quote!(crate::TokenStream::new()); } - let proc_macro_crate = minimal_quote!(crate); + let proc_macro_crate = quote!(crate); let mut after_dollar = false; - - let mut tokens = crate::TokenStream::new(); - for tree in stream { - if after_dollar { - after_dollar = false; - match tree { - TokenTree::Ident(_) => { - minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);) - .to_tokens(&mut tokens); - continue; + let tokens = stream + .into_iter() + .filter_map(|tree| { + if after_dollar { + after_dollar = false; + match tree { + TokenTree::Ident(_) => { + return Some(quote!(Into::::into( + Clone::clone(&(@ tree))),)); + } + TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} + _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), + } + } else if let TokenTree::Punct(ref tt) = tree { + if tt.as_char() == '$' { + after_dollar = true; + return None; } - TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} - _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), - } - } else if let TokenTree::Punct(ref tt) = tree { - if tt.as_char() == '$' { - after_dollar = true; - continue; } - } - match tree { - TokenTree::Punct(tt) => { - minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new( + Some(quote!(crate::TokenStream::from((@ match tree { + TokenTree::Punct(tt) => quote!(crate::TokenTree::Punct(crate::Punct::new( (@ TokenTree::from(Literal::character(tt.as_char()))), (@ match tt.spacing() { - Spacing::Alone => minimal_quote!(crate::Spacing::Alone), - Spacing::Joint => minimal_quote!(crate::Spacing::Joint), + Spacing::Alone => quote!(crate::Spacing::Alone), + Spacing::Joint => quote!(crate::Spacing::Joint), }), - )), &mut ts);) - } - TokenTree::Group(tt) => { - minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Group(crate::Group::new( + ))), + TokenTree::Group(tt) => quote!(crate::TokenTree::Group(crate::Group::new( (@ match tt.delimiter() { - Delimiter::Parenthesis => minimal_quote!(crate::Delimiter::Parenthesis), - Delimiter::Brace => minimal_quote!(crate::Delimiter::Brace), - Delimiter::Bracket => minimal_quote!(crate::Delimiter::Bracket), - Delimiter::None => minimal_quote!(crate::Delimiter::None), + Delimiter::Parenthesis => quote!(crate::Delimiter::Parenthesis), + Delimiter::Brace => quote!(crate::Delimiter::Brace), + Delimiter::Bracket => quote!(crate::Delimiter::Bracket), + Delimiter::None => quote!(crate::Delimiter::None), }), (@ quote(tt.stream())), - )), &mut ts);) - } - TokenTree::Ident(tt) => { - let literal = tt.to_string(); - let (literal, ctor) = if let Some(stripped) = literal.strip_prefix("r#") { - (stripped, minimal_quote!(crate::Ident::new_raw)) - } else { - (literal.as_str(), minimal_quote!(crate::Ident::new)) - }; - minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Ident((@ ctor)( - (@ TokenTree::from(Literal::string(literal))), + ))), + TokenTree::Ident(tt) => quote!(crate::TokenTree::Ident(crate::Ident::new( + (@ TokenTree::from(Literal::string(&tt.to_string()))), (@ quote_span(proc_macro_crate.clone(), tt.span())), - )), &mut ts);) - } - TokenTree::Literal(tt) => { - minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Literal({ + ))), + TokenTree::Literal(tt) => quote!(crate::TokenTree::Literal({ let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) .parse::() .unwrap() @@ -137,22 +120,16 @@ pub fn quote(stream: TokenStream) -> TokenStream { } else { unreachable!() } - }), &mut ts);) - } - } - .to_tokens(&mut tokens); - } + })) + })),)) + }) + .collect::(); + if after_dollar { panic!("unexpected trailing `$` in `quote!`"); } - minimal_quote! { - { - let mut ts = crate::TokenStream::new(); - (@ tokens) - ts - } - } + quote!([(@ tokens)].iter().cloned().collect::()) } /// Quote a `Span` into a `TokenStream`. @@ -160,5 +137,5 @@ pub fn quote(stream: TokenStream) -> TokenStream { #[unstable(feature = "proc_macro_quote", issue = "54722")] pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream { let id = span.save_span(); - minimal_quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) + quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) } diff --git a/profiler_builtins/Cargo.toml b/profiler_builtins/Cargo.toml index 230e8051602e4..9aadefce3b39e 100644 --- a/profiler_builtins/Cargo.toml +++ b/profiler_builtins/Cargo.toml @@ -9,7 +9,8 @@ bench = false doc = false [dependencies] +core = { path = "../core" } +compiler_builtins = { version = "0.1.0", features = ['rustc-dep-of-std'] } [build-dependencies] -# Pinned so `cargo update` bumps don't cause breakage -cc = "=1.2.0" +cc = "1.2" diff --git a/profiler_builtins/src/lib.rs b/profiler_builtins/src/lib.rs index a258f7d31a191..ac685b18c2911 100644 --- a/profiler_builtins/src/lib.rs +++ b/profiler_builtins/src/lib.rs @@ -1,15 +1,11 @@ -// tidy-alphabetical-start -#![allow(internal_features)] -#![feature(no_core)] +#![no_std] #![feature(profiler_runtime)] -#![feature(staged_api)] -// tidy-alphabetical-end - -// Other attributes: -#![no_core] #![profiler_runtime] #![unstable( feature = "profiler_runtime_lib", reason = "internal implementation detail of rustc right now", issue = "none" )] +#![allow(unused_features)] +#![allow(internal_features)] +#![feature(staged_api)] diff --git a/rtstartup/rsbegin.rs b/rtstartup/rsbegin.rs index d3ff5c14aa4a8..9a3d95bd8ddfb 100644 --- a/rtstartup/rsbegin.rs +++ b/rtstartup/rsbegin.rs @@ -19,7 +19,6 @@ #![no_core] #![allow(non_camel_case_types)] #![allow(internal_features)] -#![warn(unreachable_pub)] #[lang = "sized"] trait Sized {} diff --git a/rtstartup/rsend.rs b/rtstartup/rsend.rs index 81acfbed4477d..2514eb0034402 100644 --- a/rtstartup/rsend.rs +++ b/rtstartup/rsend.rs @@ -6,7 +6,6 @@ #![crate_type = "rlib"] #![no_core] #![allow(internal_features)] -#![warn(unreachable_pub)] #[lang = "sized"] trait Sized {} diff --git a/std/Cargo.toml b/std/Cargo.toml index aa391a4b317ac..c1ab70b714a4c 100644 --- a/std/Cargo.toml +++ b/std/Cargo.toml @@ -7,7 +7,6 @@ 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"] @@ -18,7 +17,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.145" } +compiler_builtins = { version = "=0.1.138" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', @@ -31,11 +30,11 @@ std_detect = { path = "../stdarch/crates/std_detect", default-features = false, rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] -miniz_oxide = { version = "0.8.0", optional = true, default-features = false } -addr2line = { version = "0.24.0", optional = true, default-features = false } +miniz_oxide = { version = "0.7.0", optional = true, default-features = false } +addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.169", default-features = false, features = [ +libc = { version = "0.2.162", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } @@ -131,18 +130,6 @@ 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" @@ -152,10 +139,11 @@ test = true level = "warn" check-cfg = [ 'cfg(bootstrap)', - '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"))', + 'cfg(target_arch, values("xtensa"))', # 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 'cfg(feature, values(any()))', + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', ] diff --git a/std/benches/lib.rs b/std/benches/lib.rs index e749d9c0f7998..1b21c230a0bf2 100644 --- a/std/benches/lib.rs +++ b/std/benches/lib.rs @@ -5,5 +5,3 @@ extern crate test; mod hash; -mod path; -mod time; diff --git a/std/benches/path.rs b/std/benches/path.rs deleted file mode 100644 index 094c00894a8ee..0000000000000 --- a/std/benches/path.rs +++ /dev/null @@ -1,114 +0,0 @@ -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/std/benches/time.rs b/std/benches/time.rs deleted file mode 100644 index 552481cad928a..0000000000000 --- a/std/benches/time.rs +++ /dev/null @@ -1,47 +0,0 @@ -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/std/src/bstr.rs b/std/src/bstr.rs deleted file mode 100644 index dd49177162833..0000000000000 --- a/std/src/bstr.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! The `ByteStr` and `ByteString` types and trait implementations. - -#[unstable(feature = "bstr", issue = "134915")] -pub use alloc::bstr::{ByteStr, ByteString}; diff --git a/std/src/collections/hash/map.rs b/std/src/collections/hash/map.rs index 6a0ff3a29e08f..24bbc2f32cf6d 100644 --- a/std/src/collections/hash/map.rs +++ b/std/src/collections/hash/map.rs @@ -204,25 +204,6 @@ use crate::ops::Index; /// println!("{viking:?} has {health} hp"); /// } /// ``` -/// -/// # Usage in `const` and `static` -/// -/// As explained above, `HashMap` is randomly seeded: each `HashMap` instance uses a different seed, -/// which means that `HashMap::new` cannot be used in const context. To construct a `HashMap` in the -/// initializer of a `const` or `static` item, you will have to use a different hasher that does not -/// involve a random seed, as demonstrated in the following example. **A `HashMap` constructed this -/// way is not resistant against HashDoS!** -/// -/// ```rust -/// use std::collections::HashMap; -/// use std::hash::{BuildHasherDefault, DefaultHasher}; -/// use std::sync::Mutex; -/// -/// const EMPTY_MAP: HashMap, BuildHasherDefault> = -/// HashMap::with_hasher(BuildHasherDefault::new()); -/// static MAP: Mutex, BuildHasherDefault>> = -/// Mutex::new(HashMap::with_hasher(BuildHasherDefault::new())); -/// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "HashMap")] #[stable(feature = "rust1", since = "1.0.0")] @@ -254,7 +235,7 @@ impl HashMap { /// /// The hash map will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash map will not allocate. + /// `capacity`. If `capacity` is 0, the hash map will not allocate. /// /// # Examples /// @@ -282,7 +263,7 @@ impl HashMap { /// 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 /// @@ -296,7 +277,7 @@ impl HashMap { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_collections_with_hasher", issue = "102575")] pub const fn with_hasher(hash_builder: S) -> HashMap { HashMap { base: base::HashMap::with_hasher(hash_builder) } } @@ -306,7 +287,7 @@ impl HashMap { /// /// The hash map will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash map will not allocate. + /// `capacity`. If `capacity` is 0, the hash map will not allocate. /// /// Warning: `hasher` is normally randomly generated, and /// is designed to allow HashMaps to be resistant to attacks that @@ -314,7 +295,7 @@ impl HashMap { /// 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,6 +950,7 @@ where /// # Examples /// /// ``` + /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -978,13 +960,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // Get Athenæum and Bodleian Library - /// let [Some(a), Some(b)] = libraries.get_disjoint_mut([ + /// let [Some(a), Some(b)] = libraries.get_many_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) else { panic!() }; /// /// // Assert values of Athenæum and Library of Congress - /// let got = libraries.get_disjoint_mut([ + /// let got = libraries.get_many_mut([ /// "Athenæum", /// "Library of Congress", /// ]); @@ -997,7 +979,7 @@ where /// ); /// /// // Missing keys result in None - /// let got = libraries.get_disjoint_mut([ + /// let got = libraries.get_many_mut([ /// "Athenæum", /// "New York Public Library", /// ]); @@ -1011,24 +993,21 @@ 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_disjoint_mut([ + /// let got = libraries.get_many_mut([ /// "Athenæum", /// "Athenæum", /// ]); /// ``` #[inline] - #[doc(alias = "get_many_mut")] - #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] - pub fn get_disjoint_mut( - &mut self, - ks: [&Q; N], - ) -> [Option<&'_ mut V>; N] + #[unstable(feature = "map_many_mut", issue = "97601")] + pub fn get_many_mut(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N] where K: Borrow, Q: Hash + Eq, @@ -1042,7 +1021,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_disjoint_mut`](`HashMap::get_disjoint_mut`). + /// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`). /// /// # Safety /// @@ -1054,6 +1033,7 @@ where /// # Examples /// /// ``` + /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -1063,13 +1043,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // SAFETY: The keys do not overlap. - /// let [Some(a), Some(b)] = (unsafe { libraries.get_disjoint_unchecked_mut([ + /// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) }) else { panic!() }; /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ + /// let got = unsafe { libraries.get_many_unchecked_mut([ /// "Athenæum", /// "Library of Congress", /// ]) }; @@ -1082,7 +1062,7 @@ where /// ); /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ + /// let got = unsafe { libraries.get_many_unchecked_mut([ /// "Athenæum", /// "New York Public Library", /// ]) }; @@ -1090,9 +1070,8 @@ where /// assert_eq!(got, [Some(&mut 1807), None]); /// ``` #[inline] - #[doc(alias = "get_many_unchecked_mut")] - #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] - pub unsafe fn get_disjoint_unchecked_mut( + #[unstable(feature = "map_many_mut", issue = "97601")] + pub unsafe fn get_many_unchecked_mut( &mut self, ks: [&Q; N], ) -> [Option<&'_ mut V>; N] @@ -1283,7 +1262,7 @@ impl HashMap 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 @@ -1298,13 +1277,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 @@ -1320,7 +1299,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 @@ -1445,11 +1424,6 @@ impl From<[(K, V); N]> for HashMap where K: Eq + Hash, { - /// Converts a `[(K, V); N]` into a `HashMap`. - /// - /// If any entries in the array have equal keys, - /// all but one of the corresponding values will be dropped. - /// /// # Examples /// /// ``` @@ -3223,10 +3197,6 @@ where K: Eq + Hash, S: BuildHasher + Default, { - /// Constructs a `HashMap` from an iterator of key-value pairs. - /// - /// If the iterator produces any pairs with equal keys, - /// all but one of the corresponding values will be dropped. fn from_iter>(iter: T) -> HashMap { let mut map = HashMap::with_hasher(Default::default()); map.extend(iter); diff --git a/std/src/collections/hash/set.rs b/std/src/collections/hash/set.rs index c265d42d06a9f..f86bcdb4796ec 100644 --- a/std/src/collections/hash/set.rs +++ b/std/src/collections/hash/set.rs @@ -101,25 +101,6 @@ use crate::ops::{BitAnd, BitOr, BitXor, Sub}; /// [`HashMap`]: crate::collections::HashMap /// [`RefCell`]: crate::cell::RefCell /// [`Cell`]: crate::cell::Cell -/// -/// # Usage in `const` and `static` -/// -/// Like `HashMap`, `HashSet` is randomly seeded: each `HashSet` instance uses a different seed, -/// which means that `HashSet::new` cannot be used in const context. To construct a `HashSet` in the -/// initializer of a `const` or `static` item, you will have to use a different hasher that does not -/// involve a random seed, as demonstrated in the following example. **A `HashSet` constructed this -/// way is not resistant against HashDoS!** -/// -/// ```rust -/// use std::collections::HashSet; -/// use std::hash::{BuildHasherDefault, DefaultHasher}; -/// use std::sync::Mutex; -/// -/// const EMPTY_SET: HashSet> = -/// HashSet::with_hasher(BuildHasherDefault::new()); -/// static SET: Mutex>> = -/// Mutex::new(HashSet::with_hasher(BuildHasherDefault::new())); -/// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "HashSet")] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashSet { @@ -149,7 +130,7 @@ impl HashSet { /// /// The hash set will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash set will not allocate. + /// `capacity`. If `capacity` is 0, the hash set will not allocate. /// /// # Examples /// @@ -374,7 +355,7 @@ impl HashSet { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. + /// the HashMap to be useful, see its documentation for details. /// /// # Examples /// @@ -388,7 +369,7 @@ impl HashSet { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] + #[rustc_const_unstable(feature = "const_collections_with_hasher", issue = "102575")] pub const fn with_hasher(hasher: S) -> HashSet { HashSet { base: base::HashSet::with_hasher(hasher) } } @@ -398,7 +379,7 @@ impl HashSet { /// /// The hash set will be able to hold at least `capacity` elements without /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash set will not allocate. + /// `capacity`. If `capacity` is 0, the hash set will not allocate. /// /// Warning: `hasher` is normally randomly generated, and /// is designed to allow `HashSet`s to be resistant to attacks that @@ -406,7 +387,7 @@ impl HashSet { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. + /// the HashMap to be useful, see its documentation for details. /// /// # Examples /// @@ -1088,11 +1069,6 @@ impl From<[T; N]> for HashSet where T: Eq + Hash, { - /// Converts a `[T; N]` into a `HashSet`. - /// - /// If the array contains any equal values, - /// all but one will be dropped. - /// /// # Examples /// /// ``` diff --git a/std/src/env.rs b/std/src/env.rs index c665dfd36247f..27f4daba44bf6 100644 --- a/std/src/env.rs +++ b/std/src/env.rs @@ -10,6 +10,9 @@ #![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}; @@ -333,10 +336,7 @@ impl Error for VarError { /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// -/// To pass an environment variable to a child process, you can instead use [`Command::env`]. -/// /// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs -/// [`Command::env`]: crate::process::Command::env /// /// # Panics /// @@ -396,12 +396,7 @@ pub unsafe fn set_var, V: AsRef>(key: K, value: V) { /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// -/// To prevent a child process from inheriting an environment variable, you can -/// instead use [`Command::env_remove`] or [`Command::env_clear`]. -/// /// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs -/// [`Command::env_remove`]: crate::process::Command::env_remove -/// [`Command::env_clear`]: crate::process::Command::env_clear /// /// # Panics /// @@ -602,13 +597,6 @@ impl Error for JoinPathsError { /// Returns the path of the current user's home directory if known. /// -/// This may return `None` if getting the directory fails or if the platform does not have user home directories. -/// -/// For storing user data and configuration it is often preferable to use more specific directories. -/// For example, [XDG Base Directories] on Unix or the `LOCALAPPDATA` and `APPDATA` environment variables on Windows. -/// -/// [XDG Base Directories]: https://specifications.freedesktop.org/basedir-spec/latest/ -/// /// # Unix /// /// - Returns the value of the 'HOME' environment variable if it is set @@ -620,16 +608,20 @@ impl Error for JoinPathsError { /// /// # Windows /// -/// - Returns the value of the 'USERPROFILE' environment variable if it is set, and is not an empty string. -/// - Otherwise, [`GetUserProfileDirectory`][msdn] is used to return the path. This may change in the future. +/// - Returns the value of the 'HOME' environment variable if it is set +/// (including to an empty string). +/// - Otherwise, returns the value of the 'USERPROFILE' environment variable if it is set +/// (including to an empty string). +/// - If both do not exist, [`GetUserProfileDirectory`][msdn] is used to return the path. /// /// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/userenv/nf-userenv-getuserprofiledirectorya /// -/// In UWP (Universal Windows Platform) targets this function is unimplemented and always returns `None`. +/// # Deprecation /// -/// Before Rust 1.85.0, this function used to return the value of the 'HOME' environment variable -/// on Windows, which in Cygwin or Mingw environments could return non-standard paths like `/home/you` -/// instead of `C:\Users\you`. +/// This function is deprecated because the behavior on Windows is not correct. +/// The 'HOME' environment variable is not standard on Windows, and may not produce +/// desired results; for instance, under Cygwin or Mingw it will return `/home/you` +/// when it should return `C:\Users\you`. /// /// # Examples /// diff --git a/std/src/env/tests.rs b/std/src/env/tests.rs new file mode 100644 index 0000000000000..d021726106872 --- /dev/null +++ b/std/src/env/tests.rs @@ -0,0 +1,120 @@ +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::>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() + } + + 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::>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() + } + + 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::>()), + format!("{:?}", args()) + ); +} + +#[test] +fn args_os_debug() { + assert_eq!( + format!("ArgsOs {{ inner: {:?} }}", args_os().collect::>()), + format!("{:?}", args_os()) + ); +} + +#[test] +fn vars_debug() { + assert_eq!( + format!("Vars {{ inner: {:?} }}", vars().collect::>()), + format!("{:?}", vars()) + ); +} + +#[test] +fn vars_os_debug() { + assert_eq!( + format!("VarsOs {{ inner: {:?} }}", vars_os().collect::>()), + format!("{:?}", vars_os()) + ); +} diff --git a/std/src/error.rs b/std/src/error.rs index def5f984c88e4..b3e63aaf1c567 100644 --- a/std/src/error.rs +++ b/std/src/error.rs @@ -1,6 +1,9 @@ #![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/std/tests/error.rs b/std/src/error/tests.rs similarity index 98% rename from std/tests/error.rs rename to std/src/error/tests.rs index 8fd6eb3c02065..88a9f33c07908 100644 --- a/std/tests/error.rs +++ b/std/src/error/tests.rs @@ -1,8 +1,7 @@ -#![feature(error_generic_member_access, error_reporter)] +use core::error::Request; -use std::backtrace::Backtrace; -use std::error::{Error, Report, Request}; -use std::fmt; +use super::Error; +use crate::fmt; #[derive(Debug, PartialEq)] struct A; @@ -39,6 +38,9 @@ fn downcasting() { } } +use crate::backtrace::Backtrace; +use crate::error::Report; + #[derive(Debug)] struct SuperError { source: SuperErrorSideKick, diff --git a/std/src/f128.rs b/std/src/f128.rs index 89612fa747551..e93e915159e40 100644 --- a/std/src/f128.rs +++ b/std/src/f128.rs @@ -4,6 +4,9 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. +#[cfg(test)] +mod tests; + #[unstable(feature = "f128", issue = "116909")] pub use core::f128::consts; @@ -224,7 +227,6 @@ impl f128 { /// ``` #[inline] #[rustc_allow_incoherent_impl] - #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f128, b: f128) -> f128 { @@ -321,20 +323,6 @@ 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")] @@ -358,10 +346,8 @@ 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); + /// assert!(abs_difference <= f128::EPSILON); /// # } /// ``` #[inline] @@ -398,7 +384,6 @@ impl f128 { /// # } /// ``` #[inline] - #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/std/tests/floats/f128.rs b/std/src/f128/tests.rs similarity index 99% rename from std/tests/floats/f128.rs rename to std/src/f128/tests.rs index d0e8b157e6b6f..cbcf9f96239bb 100644 --- a/std/tests/floats/f128.rs +++ b/std/src/f128/tests.rs @@ -1,11 +1,11 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f128)] -use std::f128::consts; -use std::num::FpCategory as Fp; +use crate::f128::consts; +use crate::num::FpCategory as Fp; #[cfg(reliable_f128_math)] -use std::ops::Rem; -use std::ops::{Add, Div, Mul, Sub}; +use crate::ops::Rem; +use crate::ops::{Add, Div, Mul, Sub}; // Note these tolerances make sense around zero, but not for more extreme exponents. @@ -762,6 +762,8 @@ 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/std/src/f16.rs b/std/src/f16.rs index cc523c93b4de7..5b7fcaa28e064 100644 --- a/std/src/f16.rs +++ b/std/src/f16.rs @@ -4,6 +4,9 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. +#[cfg(test)] +mod tests; + #[unstable(feature = "f16", issue = "116909")] pub use core::f16::consts; @@ -225,7 +228,6 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] - #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f16, b: f16) -> f16 { unsafe { intrinsics::fmaf16(self, a, b) } @@ -321,20 +323,6 @@ 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")] @@ -358,10 +346,8 @@ 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); + /// assert!(abs_difference <= f16::EPSILON); /// # } /// ``` #[inline] @@ -398,7 +384,6 @@ impl f16 { /// # } /// ``` #[inline] - #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/std/tests/floats/f16.rs b/std/src/f16/tests.rs similarity index 99% rename from std/tests/floats/f16.rs rename to std/src/f16/tests.rs index 5180f3d40f3a7..684ee3f3855b8 100644 --- a/std/tests/floats/f16.rs +++ b/std/src/f16/tests.rs @@ -1,8 +1,8 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f16)] -use std::f16::consts; -use std::num::FpCategory as Fp; +use crate::f16::consts; +use crate::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() { - crate::test_num(10f16, 2f16); + test_num(10f16, 2f16); } #[test] @@ -734,6 +734,7 @@ 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/std/src/f32.rs b/std/src/f32.rs index 260c499b7f4b9..7cb285bbff5f7 100644 --- a/std/src/f32.rs +++ b/std/src/f32.rs @@ -12,6 +12,9 @@ #![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::{ @@ -207,7 +210,6 @@ impl f32 { /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); /// ``` #[rustc_allow_incoherent_impl] - #[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -303,9 +305,8 @@ 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); + /// assert!(abs_difference <= f32::EPSILON); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -327,10 +328,8 @@ 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); + /// assert!(abs_difference <= f32::EPSILON); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -361,7 +360,6 @@ impl f32 { /// assert!(negative.sqrt().is_nan()); /// assert!(negative_zero.sqrt() == negative_zero); /// ``` - #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/std/tests/floats/f32.rs b/std/src/f32/tests.rs similarity index 99% rename from std/tests/floats/f32.rs rename to std/src/f32/tests.rs index bf7641986ada8..99cfcfb231dad 100644 --- a/std/tests/floats/f32.rs +++ b/std/src/f32/tests.rs @@ -1,5 +1,5 @@ -use std::f32::consts; -use std::num::FpCategory as Fp; +use crate::f32::consts; +use crate::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() { - crate::test_num(10f32, 2f32); + test_num(10f32, 2f32); } #[test] @@ -700,6 +700,8 @@ 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/std/src/f64.rs b/std/src/f64.rs index 7af646f8cfd60..47163c272de32 100644 --- a/std/src/f64.rs +++ b/std/src/f64.rs @@ -12,6 +12,9 @@ #![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::{ @@ -207,7 +210,6 @@ impl f64 { /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); /// ``` #[rustc_allow_incoherent_impl] - #[doc(alias = "fma", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -303,9 +305,8 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// assert!(abs_difference <= f64::EPSILON); /// - /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); + /// assert!(abs_difference < 1e-10); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -327,10 +328,8 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// assert!(abs_difference <= f64::EPSILON); /// - /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); - /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); + /// assert!(abs_difference < 1e-10); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -361,7 +360,6 @@ impl f64 { /// assert!(negative.sqrt().is_nan()); /// assert!(negative_zero.sqrt() == negative_zero); /// ``` - #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/std/tests/floats/f64.rs b/std/src/f64/tests.rs similarity index 98% rename from std/tests/floats/f64.rs rename to std/src/f64/tests.rs index cbbfcd15efd26..3fac2efe0d76c 100644 --- a/std/tests/floats/f64.rs +++ b/std/src/f64/tests.rs @@ -1,5 +1,5 @@ -use std::f64::consts; -use std::num::FpCategory as Fp; +use crate::f64::consts; +use crate::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() { - crate::test_num(10f64, 2f64); + test_num(10f64, 2f64); } #[test] @@ -112,6 +112,7 @@ 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; @@ -164,6 +165,7 @@ 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; @@ -181,6 +183,7 @@ 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; @@ -680,6 +683,7 @@ 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/std/src/ffi/mod.rs b/std/src/ffi/mod.rs index 7d7cce09a3f09..469136be8838a 100644 --- a/std/src/ffi/mod.rs +++ b/std/src/ffi/mod.rs @@ -179,19 +179,19 @@ pub use core::ffi::{ c_ulong, c_ulonglong, c_ushort, }; -#[doc(inline)] +#[doc(no_inline)] #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] pub use self::c_str::FromBytesUntilNulError; -#[doc(inline)] +#[doc(no_inline)] #[stable(feature = "cstr_from_bytes", since = "1.10.0")] pub use self::c_str::FromBytesWithNulError; -#[doc(inline)] +#[doc(no_inline)] #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub use self::c_str::FromVecWithNulError; -#[doc(inline)] +#[doc(no_inline)] #[stable(feature = "cstring_into", since = "1.7.0")] pub use self::c_str::IntoStringError; -#[doc(inline)] +#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use self::c_str::NulError; #[doc(inline)] diff --git a/std/src/ffi/os_str.rs b/std/src/ffi/os_str.rs index c4c8dbccd7a44..328185d1f2b0c 100644 --- a/std/src/ffi/os_str.rs +++ b/std/src/ffi/os_str.rs @@ -203,8 +203,8 @@ impl OsString { self } - /// Converts the `OsString` into a byte vector. To convert the byte vector back into an - /// `OsString`, use the [`OsString::from_encoded_bytes_unchecked`] function. + /// Converts the `OsString` into a byte slice. To convert the byte slice back into an + /// `OsString`, use the [`OsStr::from_encoded_bytes_unchecked`] function. /// /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit @@ -550,7 +550,7 @@ impl OsString { OsStr::from_inner_mut(self.inner.leak()) } - /// Truncate the `OsString` to the specified length. + /// Truncate the the `OsString` to the specified length. /// /// # Panics /// Panics if `len` does not lie on a valid `OsStr` boundary @@ -1229,7 +1229,7 @@ impl From<&OsStr> for Box { } } -#[stable(feature = "box_from_mut_slice", since = "1.84.0")] +#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut OsStr> for Box { /// Copies the string into a newly allocated [Box]<[OsStr]>. #[inline] @@ -1309,7 +1309,7 @@ impl From<&OsStr> for Arc { } } -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut OsStr> for Arc { /// Copies the string into a newly allocated [Arc]<[OsStr]>. #[inline] @@ -1339,7 +1339,7 @@ impl From<&OsStr> for Rc { } } -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut OsStr> for Rc { /// Copies the string into a newly allocated [Rc]<[OsStr]>. #[inline] diff --git a/std/src/ffi/os_str/tests.rs b/std/src/ffi/os_str/tests.rs index 2572b71fd9ac6..cbec44c862646 100644 --- a/std/src/ffi/os_str/tests.rs +++ b/std/src/ffi/os_str/tests.rs @@ -295,7 +295,7 @@ fn clone_to_uninit() { let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() }); + assert_eq!(a.as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) }); let mut b: Box = OsStr::new("world.exe").into(); assert_eq!(size_of_val::(a), size_of_val::(&b)); diff --git a/std/src/fs.rs b/std/src/fs.rs index 0871a9e22d386..d846a4e5f0916 100644 --- a/std/src/fs.rs +++ b/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/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 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 the file not open for writing, it is unspecified whether this function returns an error. /// @@ -639,9 +639,6 @@ 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, @@ -674,22 +671,19 @@ impl File { self.inner.lock() } - /// Acquire a shared (non-exclusive) advisory lock on the file. Blocks until the lock can be acquired. + /// Acquire a shared 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 at the same time. + /// none may hold an exclusive lock. /// - /// 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. + /// 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. /// /// 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, @@ -722,18 +716,14 @@ impl File { self.inner.lock_shared() } - /// 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). + /// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked. /// /// This acquires an exclusive advisory lock; no other file handle to this file may acquire /// another lock. /// - /// 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 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 the file not open for writing, it is unspecified whether this function returns an error. /// @@ -741,9 +731,6 @@ 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 @@ -777,25 +764,20 @@ impl File { self.inner.try_lock() } - /// 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). + /// Acquire a shared advisory lock on the file. + /// Returns `Ok(false)` if the file is exclusively locked. /// /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but - /// none may hold an exclusive lock at the same time. + /// none may hold an exclusive 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 `Ok(true)`, then it has acquired a shared lock. + /// 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`], 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 @@ -831,12 +813,7 @@ impl File { /// Release all locks on the file. /// - /// 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. + /// All remaining locks are released when the file handle, and all clones of it, are dropped. /// /// # Platform-specific behavior /// @@ -1892,10 +1869,8 @@ impl Permissions { /// /// # Note /// - /// This function does not take Access Control Lists (ACLs), Unix group - /// membership and other nuances into account. - /// Therefore the return value of this function cannot be relied upon - /// to predict whether attempts to read or write the file will actually succeed. + /// This function does not take Access Control Lists (ACLs) or Unix group + /// membership into account. /// /// # Windows /// @@ -1910,13 +1885,10 @@ impl Permissions { /// # Unix (including macOS) /// /// On Unix-based platforms this checks if *any* of the owner, group or others - /// write permission bits are set. It does not consider anything else, including: - /// - /// * Whether the current user is in the file's assigned group. - /// * Permissions granted by ACL. - /// * That `root` user can write to files that do not have any write bits set. - /// * Writable files on a filesystem that is mounted read-only. - /// + /// write permission bits are set. It does not check if the current + /// user is in the file's assigned group. It also does not check ACLs. + /// Therefore the return value of this function cannot be relied upon + /// to predict whether attempts to read or write the file will actually succeed. /// The [`PermissionsExt`] trait gives direct access to the permission bits but /// also does not read ACLs. /// @@ -2307,8 +2279,8 @@ impl AsInner for DirEntry { /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `unlink` function on Unix. -/// On Windows, `DeleteFile` is used or `CreateFileW` and `SetInformationByHandle` for readonly files. +/// This function currently corresponds to the `unlink` function on Unix +/// and the `DeleteFile` function on Windows. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -2425,14 +2397,12 @@ pub fn symlink_metadata>(path: P) -> io::Result { /// # Platform-specific behavior /// /// This function currently corresponds to the `rename` function on Unix -/// and the `SetFileInformationByHandle` function on Windows. +/// and the `MoveFileEx` function with the `MOVEFILE_REPLACE_EXISTING` flag on Windows. /// /// Because of this, the behavior when both `from` and `to` exist differs. On /// Unix, if `from` is a directory, `to` must also be an (empty) directory. If -/// `from` is not a directory, `to` must also be not a directory. The behavior -/// on Windows is the same on Windows 10 1607 and higher if `FileRenameInfoEx` -/// is supported by the filesystem; otherwise, `from` can be anything, but -/// `to` must *not* be a directory. +/// `from` is not a directory, `to` must also be not a directory. In contrast, +/// on Windows, `from` can be anything, but `to` must *not* be a directory. /// /// Note that, this [may change in the future][changes]. /// @@ -2552,7 +2522,6 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// limited to just these cases: /// /// * The `original` path is not a file or doesn't exist. -/// * The 'link' path already exists. /// /// # Examples /// @@ -3051,7 +3020,7 @@ impl DirBuilder { match path.parent() { Some(p) => self.create_dir_all(p)?, None => { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::Uncategorized, "failed to create whole tree", )); diff --git a/std/src/fs/tests.rs b/std/src/fs/tests.rs index 8e307f57cf9d2..018e19586418e 100644 --- a/std/src/fs/tests.rs +++ b/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::test_helpers::{TempDir, tmpdir}; +use crate::sys_common::io::test::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; use crate::{env, str, thread}; @@ -1384,7 +1384,7 @@ fn file_try_clone() { } #[test] -#[cfg(not(target_vendor = "win7"))] +#[cfg(not(windows))] fn unlink_readonly() { let tmpdir = tmpdir(); let path = tmpdir.join("file"); @@ -1912,73 +1912,3 @@ fn test_hidden_file_truncation() { let metadata = file.metadata().unwrap(); assert_eq!(metadata.len(), 0); } - -#[cfg(windows)] -#[test] -fn test_rename_file_over_open_file() { - // Make sure that std::fs::rename works if the target file is already opened with FILE_SHARE_DELETE. See #123985. - let tmpdir = tmpdir(); - - // Create source with test data to read. - let source_path = tmpdir.join("source_file.txt"); - fs::write(&source_path, b"source hello world").unwrap(); - - // Create target file with test data to read; - let target_path = tmpdir.join("target_file.txt"); - fs::write(&target_path, b"target hello world").unwrap(); - - // Open target file - let target_file = fs::File::open(&target_path).unwrap(); - - // Rename source - fs::rename(source_path, &target_path).unwrap(); - - core::mem::drop(target_file); - assert_eq!(fs::read(target_path).unwrap(), b"source hello world"); -} - -#[test] -#[cfg(windows)] -fn test_rename_directory_to_non_empty_directory() { - // Renaming a directory over a non-empty existing directory should fail on Windows. - let tmpdir: TempDir = tmpdir(); - - let source_path = tmpdir.join("source_directory"); - let target_path = tmpdir.join("target_directory"); - - fs::create_dir(&source_path).unwrap(); - fs::create_dir(&target_path).unwrap(); - - fs::write(target_path.join("target_file.txt"), b"target hello world").unwrap(); - - error!(fs::rename(source_path, target_path), 145); // ERROR_DIR_NOT_EMPTY -} - -#[test] -fn test_rename_symlink() { - let tmpdir = tmpdir(); - let original = tmpdir.join("original"); - let dest = tmpdir.join("dest"); - let not_exist = Path::new("does not exist"); - - symlink_file(not_exist, &original).unwrap(); - fs::rename(&original, &dest).unwrap(); - // Make sure that renaming `original` to `dest` preserves the symlink. - assert_eq!(fs::read_link(&dest).unwrap().as_path(), not_exist); -} - -#[test] -#[cfg(windows)] -fn test_rename_junction() { - let tmpdir = tmpdir(); - let original = tmpdir.join("original"); - let dest = tmpdir.join("dest"); - let not_exist = Path::new("does not exist"); - - junction_point(¬_exist, &original).unwrap(); - fs::rename(&original, &dest).unwrap(); - - // Make sure that renaming `original` to `dest` preserves the junction point. - // Junction links are always absolute so we just check the file name is correct. - assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str())); -} diff --git a/std/src/io/buffered/bufreader/buffer.rs b/std/src/io/buffered/bufreader/buffer.rs index 5251cc302cb49..52fe49985c65a 100644 --- a/std/src/io/buffered/bufreader/buffer.rs +++ b/std/src/io/buffered/bufreader/buffer.rs @@ -41,7 +41,7 @@ impl Buffer { match Box::try_new_uninit_slice(capacity) { Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }), Err(_) => { - Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer")) + Err(io::const_io_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer")) } } } @@ -50,7 +50,7 @@ impl Buffer { pub fn buffer(&self) -> &[u8] { // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and // that region is initialized because those are all invariants of this type. - unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() } + unsafe { MaybeUninit::slice_assume_init_ref(self.buf.get_unchecked(self.pos..self.filled)) } } #[inline] diff --git a/std/src/io/buffered/bufwriter.rs b/std/src/io/buffered/bufwriter.rs index 574eb83dc5649..c41bae2aa4e81 100644 --- a/std/src/io/buffered/bufwriter.rs +++ b/std/src/io/buffered/bufwriter.rs @@ -96,7 +96,7 @@ impl BufWriter { pub(crate) fn try_new_buffer() -> io::Result> { Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| { - io::const_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer") + io::const_io_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer") }) } @@ -238,7 +238,7 @@ impl BufWriter { match r { Ok(0) => { - return Err(io::const_error!( + return Err(io::const_io_error!( ErrorKind::WriteZero, "failed to write the buffered data", )); diff --git a/std/src/io/buffered/linewritershim.rs b/std/src/io/buffered/linewritershim.rs index 5ebeada59bb53..3d04ccd1c7d81 100644 --- a/std/src/io/buffered/linewritershim.rs +++ b/std/src/io/buffered/linewritershim.rs @@ -119,14 +119,7 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { // the buffer? // - If not, scan for the last newline that *does* fit in the buffer let tail = if flushed >= newline_idx { - let tail = &buf[flushed..]; - // Avoid unnecessary short writes by not splitting the remaining - // bytes if they're larger than the buffer. - // They can be written in full by the next call to write. - if tail.len() >= self.buffer.capacity() { - return Ok(flushed); - } - tail + &buf[flushed..] } else if newline_idx - flushed <= self.buffer.capacity() { &buf[flushed..newline_idx] } else { diff --git a/std/src/io/buffered/tests.rs b/std/src/io/buffered/tests.rs index 17f6107aa030c..bff0f823c4b5a 100644 --- a/std/src/io/buffered/tests.rs +++ b/std/src/io/buffered/tests.rs @@ -847,7 +847,8 @@ fn long_line_flushed() { } /// Test that, given a very long partial line *after* successfully -/// flushing a complete line, no additional writes take place. This assures +/// flushing a complete line, the very long partial line is buffered +/// unconditionally, and no additional writes take place. This assures /// the property that `write` should make at-most-one attempt to write /// new data. #[test] @@ -855,22 +856,13 @@ fn line_long_tail_not_flushed() { let writer = ProgrammableSink::default(); let mut writer = LineWriter::with_capacity(5, writer); - // Assert that Line 1\n is flushed and the long tail isn't. - let bytes = b"Line 1\n0123456789"; - writer.write(bytes).unwrap(); + // Assert that Line 1\n is flushed, and 01234 is buffered + assert_eq!(writer.write(b"Line 1\n0123456789").unwrap(), 12); assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); -} - -// Test that appending to a full buffer emits a single write, flushing the buffer. -#[test] -fn line_full_buffer_flushed() { - let writer = ProgrammableSink::default(); - let mut writer = LineWriter::with_capacity(5, writer); - assert_eq!(writer.write(b"01234").unwrap(), 5); // Because the buffer is full, this subsequent write will flush it assert_eq!(writer.write(b"5").unwrap(), 1); - assert_eq!(&writer.get_ref().buffer, b"01234"); + assert_eq!(&writer.get_ref().buffer, b"Line 1\n01234"); } /// Test that, if an attempt to pre-flush buffered data returns Ok(0), diff --git a/std/src/io/copy/tests.rs b/std/src/io/copy/tests.rs index 25b1ece2745b9..2e0eb6cdce666 100644 --- a/std/src/io/copy/tests.rs +++ b/std/src/io/copy/tests.rs @@ -126,7 +126,6 @@ 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/std/src/io/cursor.rs b/std/src/io/cursor.rs index 606099c8bc67a..fbfdb4fa02323 100644 --- a/std/src/io/cursor.rs +++ b/std/src/io/cursor.rs @@ -153,7 +153,7 @@ impl Cursor { /// let reference = buff.get_mut(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_mut_cursor", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] pub const fn get_mut(&mut self) -> &mut T { &mut self.inner } @@ -201,7 +201,7 @@ impl Cursor { /// assert_eq!(buff.position(), 4); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_mut_cursor", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] pub const fn set_position(&mut self, pos: u64) { self.pos = pos; } @@ -304,7 +304,7 @@ where self.pos = n; Ok(self.pos) } - None => Err(io::const_error!( + None => Err(io::const_io_error!( ErrorKind::InvalidInput, "invalid seek to a negative or overflowing position", )), @@ -446,7 +446,7 @@ fn reserve_and_pad( buf_len: usize, ) -> io::Result { let pos: usize = (*pos_mut).try_into().map_err(|_| { - io::const_error!( + io::const_io_error!( ErrorKind::InvalidInput, "cursor position exceeds maximum possible vector length", ) diff --git a/std/src/io/error.rs b/std/src/io/error.rs index 38b723366175f..5d7adcace5247 100644 --- a/std/src/io/error.rs +++ b/std/src/io/error.rs @@ -76,31 +76,31 @@ impl fmt::Debug for Error { #[allow(dead_code)] impl Error { pub(crate) const INVALID_UTF8: Self = - const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); + const_io_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); pub(crate) const READ_EXACT_EOF: Self = - const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); + const_io_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!( + pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_io_error!( ErrorKind::NotFound, "The number of hardware threads is not known for the target platform" ); pub(crate) const UNSUPPORTED_PLATFORM: Self = - const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); + const_io_error!(ErrorKind::Unsupported, "operation not supported on this platform"); pub(crate) const WRITE_ALL_EOF: Self = - const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); + const_io_error!(ErrorKind::WriteZero, "failed to write whole buffer"); pub(crate) const ZERO_TIMEOUT: Self = - const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); + const_io_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); } #[stable(feature = "rust1", since = "1.0.0")] impl From for Error { /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. fn from(_: alloc::ffi::NulError) -> Error { - const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") + const_io_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") } } @@ -151,38 +151,27 @@ pub type RawOsError = sys::RawOsError; // (For the sake of being explicit: the alignment requirement here only matters // if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't // matter at all) -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] #[repr(align(4))] #[derive(Debug)] -pub struct SimpleMessage { - pub kind: ErrorKind, - pub message: &'static str, +pub(crate) struct SimpleMessage { + kind: ErrorKind, + message: &'static str, } -/// Creates a new I/O error from a known kind of error and a string literal. -/// -/// Contrary to [`Error::new`], this macro does not allocate and can be used in -/// `const` contexts. -/// -/// # Example -/// ``` -/// #![feature(io_const_error)] -/// use std::io::{const_error, Error, ErrorKind}; -/// -/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); -/// -/// fn not_here() -> Result<(), Error> { -/// Err(FAIL) -/// } -/// ``` -#[rustc_macro_transparency = "semitransparent"] -#[unstable(feature = "io_const_error", issue = "133448")] -#[allow_internal_unstable(hint_must_use, io_const_error_internals)] -pub macro const_error($kind:expr, $message:expr $(,)?) { - $crate::hint::must_use($crate::io::Error::from_static_message( - const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, - )) +impl SimpleMessage { + pub(crate) const fn new(kind: ErrorKind, message: &'static str) -> Self { + Self { kind, message } + } +} + +/// Creates and returns an `io::Error` for a given `ErrorKind` and constant +/// message. This doesn't allocate. +pub(crate) macro const_io_error($kind:expr, $message:expr $(,)?) { + $crate::io::error::Error::from_static_message({ + const MESSAGE_DATA: $crate::io::error::SimpleMessage = + $crate::io::error::SimpleMessage::new($kind, $message); + &MESSAGE_DATA + }) } // As with `SimpleMessage`: `#[repr(align(4))]` here is just because @@ -338,9 +327,9 @@ pub enum ErrorKind { /// example, on Unix, a named pipe opened with `File::open`. #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] NotSeekable, - /// Filesystem quota or some other kind of quota was exceeded. - #[stable(feature = "io_error_quota_exceeded", since = "1.85.0")] - QuotaExceeded, + /// Filesystem quota was exceeded. + #[unstable(feature = "io_error_more", issue = "86442")] + FilesystemQuotaExceeded, /// File larger than allowed or supported. /// /// This might arise from a hard limit of the underlying filesystem or file access API, or from @@ -364,7 +353,7 @@ pub enum ErrorKind { #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] Deadlock, /// Cross-device or cross-filesystem (hard) link or rename. - #[stable(feature = "io_error_crosses_devices", since = "1.85.0")] + #[unstable(feature = "io_error_more", issue = "86442")] CrossesDevices, /// Too many (hard) links to the same filesystem object. /// @@ -446,8 +435,8 @@ pub enum ErrorKind { impl ErrorKind { pub(crate) fn as_str(&self) -> &'static str { use ErrorKind::*; + // tidy-alphabetical-start match *self { - // tidy-alphabetical-start AddrInUse => "address in use", AddrNotAvailable => "address not available", AlreadyExists => "entity already exists", @@ -460,11 +449,12 @@ impl ErrorKind { Deadlock => "deadlock", DirectoryNotEmpty => "directory not empty", ExecutableFileBusy => "executable file busy", - FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", FileTooLarge => "file too large", + FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", + FilesystemQuotaExceeded => "filesystem quota exceeded", HostUnreachable => "host unreachable", - InProgress => "in progress", Interrupted => "operation interrupted", + InProgress => "in progress", InvalidData => "invalid data", InvalidFilename => "invalid filename", InvalidInput => "invalid input parameter", @@ -478,7 +468,6 @@ impl ErrorKind { Other => "other error", OutOfMemory => "out of memory", PermissionDenied => "permission denied", - QuotaExceeded => "quota exceeded", ReadOnlyFilesystem => "read-only filesystem or storage medium", ResourceBusy => "resource busy", StaleNetworkFileHandle => "stale network file handle", @@ -490,8 +479,8 @@ impl ErrorKind { Unsupported => "unsupported", WouldBlock => "operation would block", WriteZero => "write zero", - // tidy-alphabetical-end } + // tidy-alphabetical-end } } @@ -603,15 +592,13 @@ impl Error { /// /// This function does not allocate. /// - /// You should not use this directly, and instead use the `const_error!` - /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. + /// You should not use this directly, and instead use the `const_io_error!` + /// macro: `io::const_io_error!(ErrorKind::Something, "some_message")`. /// /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. #[inline] - #[doc(hidden)] - #[unstable(feature = "io_const_error_internals", issue = "none")] - pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { + pub(crate) const fn from_static_message(msg: &'static SimpleMessage) -> Error { Self { repr: Repr::new_simple_message(msg) } } diff --git a/std/src/io/error/repr_bitpacked.rs b/std/src/io/error/repr_bitpacked.rs index 716da37168d01..a839a2fbac117 100644 --- a/std/src/io/error/repr_bitpacked.rs +++ b/std/src/io/error/repr_bitpacked.rs @@ -103,8 +103,7 @@ //! the time. use core::marker::PhantomData; -use core::num::NonZeroUsize; -use core::ptr::NonNull; +use core::ptr::{self, NonNull}; use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; @@ -177,7 +176,7 @@ impl Repr { let utagged = ((code as usize) << 32) | TAG_OS; // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), + unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will @@ -194,7 +193,7 @@ impl Repr { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), + unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will @@ -336,7 +335,7 @@ fn kind_from_prim(ek: u32) -> Option { WriteZero, StorageFull, NotSeekable, - QuotaExceeded, + FilesystemQuotaExceeded, FileTooLarge, ResourceBusy, ExecutableFileBusy, diff --git a/std/src/io/error/tests.rs b/std/src/io/error/tests.rs index edac6563478cd..00d04984a3854 100644 --- a/std/src/io/error/tests.rs +++ b/std/src/io/error/tests.rs @@ -1,4 +1,4 @@ -use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; +use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_io_error}; use crate::assert_matches::assert_matches; use crate::mem::size_of; use crate::sys::decode_error_kind; @@ -60,7 +60,7 @@ fn test_downcasting() { #[test] fn test_const() { - const E: Error = const_error!(ErrorKind::NotFound, "hello"); + const E: Error = const_io_error!(ErrorKind::NotFound, "hello"); assert_eq!(E.kind(), ErrorKind::NotFound); assert_eq!(E.to_string(), "hello"); @@ -110,13 +110,13 @@ fn test_simple_message_packing() { }}; } - let not_static = const_error!(Uncategorized, "not a constant!"); + let not_static = const_io_error!(Uncategorized, "not a constant!"); check_simple_msg!(not_static, Uncategorized, "not a constant!"); - const CONST: Error = const_error!(NotFound, "definitely a constant!"); + const CONST: Error = const_io_error!(NotFound, "definitely a constant!"); check_simple_msg!(CONST, NotFound, "definitely a constant!"); - static STATIC: Error = const_error!(BrokenPipe, "a constant, sort of!"); + static STATIC: Error = const_io_error!(BrokenPipe, "a constant, sort of!"); check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!"); } diff --git a/std/src/io/mod.rs b/std/src/io/mod.rs index 0ffad2c27a4d5..21e7077495450 100644 --- a/std/src/io/mod.rs +++ b/std/src/io/mod.rs @@ -301,17 +301,12 @@ mod tests; pub use core::io::{BorrowedBuf, BorrowedCursor}; use core::slice::memchr; +pub(crate) use error::const_io_error; + #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -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; @@ -339,12 +334,11 @@ 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::io::DEFAULT_BUF_SIZE; +const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub(crate) use stdio::cleanup; @@ -1086,7 +1080,7 @@ pub trait Read { /// let f = BufReader::new(File::open("foo.txt")?); /// /// for byte in f.bytes() { - /// println!("{}", byte?); + /// println!("{}", byte.unwrap()); /// } /// Ok(()) /// } @@ -1998,16 +1992,15 @@ pub trait Seek { /// .write(true) /// .read(true) /// .create(true) - /// .open("foo.txt")?; + /// .open("foo.txt").unwrap(); /// /// let hello = "Hello!\n"; - /// write!(f, "{hello}")?; - /// f.rewind()?; + /// write!(f, "{hello}").unwrap(); + /// f.rewind().unwrap(); /// /// let mut buf = String::new(); - /// f.read_to_string(&mut buf)?; + /// f.read_to_string(&mut buf).unwrap(); /// assert_eq!(&buf, hello); - /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "seek_rewind", since = "1.55.0")] fn rewind(&mut self) -> Result<()> { @@ -2216,9 +2209,8 @@ fn skip_until(r: &mut R, delim: u8) -> Result { /// /// let stdin = io::stdin(); /// for line in stdin.lock().lines() { -/// println!("{}", line?); +/// println!("{}", line.unwrap()); /// } -/// # std::io::Result::Ok(()) /// ``` /// /// If you have something that implements [`Read`], you can use the [`BufReader` @@ -2241,8 +2233,7 @@ fn skip_until(r: &mut R, delim: u8) -> Result { /// let f = BufReader::new(f); /// /// for line in f.lines() { -/// let line = line?; -/// println!("{line}"); +/// println!("{}", line.unwrap()); /// } /// /// Ok(()) @@ -2280,7 +2271,7 @@ pub trait BufRead: Read { /// let stdin = io::stdin(); /// let mut stdin = stdin.lock(); /// - /// let buffer = stdin.fill_buf()?; + /// let buffer = stdin.fill_buf().unwrap(); /// /// // work with buffer /// println!("{buffer:?}"); @@ -2288,7 +2279,6 @@ pub trait BufRead: Read { /// // ensure the bytes we worked with aren't returned again later /// let length = buffer.len(); /// stdin.consume(length); - /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn fill_buf(&mut self) -> Result<&[u8]>; @@ -2334,13 +2324,12 @@ pub trait BufRead: Read { /// let stdin = io::stdin(); /// let mut stdin = stdin.lock(); /// - /// while stdin.has_data_left()? { + /// while stdin.has_data_left().unwrap() { /// let mut line = String::new(); - /// stdin.read_line(&mut line)?; + /// stdin.read_line(&mut line).unwrap(); /// // work with line /// println!("{line:?}"); /// } - /// # std::io::Result::Ok(()) /// ``` #[unstable(feature = "buf_read_has_data_left", reason = "recently added", issue = "86423")] fn has_data_left(&mut self) -> Result { diff --git a/std/src/io/pipe.rs b/std/src/io/pipe.rs deleted file mode 100644 index 266c7bc96389b..0000000000000 --- a/std/src/io/pipe.rs +++ /dev/null @@ -1,260 +0,0 @@ -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.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.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 { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - 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) -> io::Result { - 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 { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - 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) -> io::Result { - 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 { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - 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 { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} diff --git a/std/src/io/stdio.rs b/std/src/io/stdio.rs index 318c350822168..35b38ed783ff2 100644 --- a/std/src/io/stdio.rs +++ b/std/src/io/stdio.rs @@ -1200,7 +1200,6 @@ pub trait IsTerminal: crate::sealed::Sealed { /// /// [changes]: io#platform-specific-behavior /// [`Stdin`]: crate::io::Stdin - #[doc(alias = "isatty")] #[stable(feature = "is_terminal", since = "1.70.0")] fn is_terminal(&self) -> bool; } diff --git a/std/src/io/stdio/tests.rs b/std/src/io/stdio/tests.rs index e68d8c29fbce2..bf8f3a5adfb6f 100644 --- a/std/src/io/stdio/tests.rs +++ b/std/src/io/stdio/tests.rs @@ -159,8 +159,7 @@ 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/std/src/io/tests.rs b/std/src/io/tests.rs index f64f034cce779..89e806c08911c 100644 --- a/std/src/io/tests.rs +++ b/std/src/io/tests.rs @@ -7,6 +7,7 @@ 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(); @@ -224,12 +225,12 @@ fn take_eof() { impl Read for R { fn read(&mut self, _: &mut [u8]) -> io::Result { - Err(io::const_error!(io::ErrorKind::Other, "")) + Err(io::const_io_error!(io::ErrorKind::Other, "")) } } impl BufRead for R { fn fill_buf(&mut self) -> io::Result<&[u8]> { - Err(io::const_error!(io::ErrorKind::Other, "")) + Err(io::const_io_error!(io::ErrorKind::Other, "")) } fn consume(&mut self, _amt: usize) {} } @@ -358,6 +359,7 @@ 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(|| { diff --git a/std/src/keyword_docs.rs b/std/src/keyword_docs.rs index 1d26bf37f4d28..4302e24781ee8 100644 --- a/std/src/keyword_docs.rs +++ b/std/src/keyword_docs.rs @@ -807,6 +807,64 @@ mod in_keyword {} /// [Reference]: ../reference/statements.html#let-statements mod let_keyword {} +#[doc(keyword = "while")] +// +/// Loop while a condition is upheld. +/// +/// A `while` expression is used for predicate loops. The `while` expression runs the conditional +/// expression before running the loop body, then runs the loop body if the conditional +/// expression evaluates to `true`, or exits the loop otherwise. +/// +/// ```rust +/// let mut counter = 0; +/// +/// while counter < 10 { +/// println!("{counter}"); +/// counter += 1; +/// } +/// ``` +/// +/// Like the [`for`] expression, we can use `break` and `continue`. A `while` expression +/// cannot break with a value and always evaluates to `()` unlike [`loop`]. +/// +/// ```rust +/// let mut i = 1; +/// +/// while i < 100 { +/// i *= 2; +/// if i == 64 { +/// break; // Exit when `i` is 64. +/// } +/// } +/// ``` +/// +/// As `if` expressions have their pattern matching variant in `if let`, so too do `while` +/// expressions with `while let`. The `while let` expression matches the pattern against the +/// expression, then runs the loop body if pattern matching succeeds, or exits the loop otherwise. +/// We can use `break` and `continue` in `while let` expressions just like in `while`. +/// +/// ```rust +/// let mut counter = Some(0); +/// +/// while let Some(i) = counter { +/// if i == 10 { +/// counter = None; +/// } else { +/// println!("{i}"); +/// counter = Some (i + 1); +/// } +/// } +/// ``` +/// +/// For more information on `while` and loops in general, see the [reference]. +/// +/// See also, [`for`], [`loop`]. +/// +/// [`for`]: keyword.for.html +/// [`loop`]: keyword.loop.html +/// [reference]: ../reference/expressions/loop-expr.html#predicate-loops +mod while_keyword {} + #[doc(keyword = "loop")] // /// Loop indefinitely. @@ -1263,10 +1321,10 @@ mod return_keyword {} /// [Reference]: ../reference/items/associated-items.html#methods mod self_keyword {} -// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we can replace -// these two lines with `#[doc(keyword = "Self")]` and update `is_doc_keyword` in -// `CheckAttrVisitor`. +// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we can remove the +// three next lines and put back: `#[doc(keyword = "Self")]`. #[doc(alias = "Self")] +#[allow(rustc::existing_doc_keyword)] #[doc(keyword = "SelfTy")] // /// The implementing type within a [`trait`] or [`impl`] block, or the current type within a type @@ -2285,64 +2343,6 @@ mod use_keyword {} /// [RFC]: https://github.com/rust-lang/rfcs/blob/master/text/0135-where.md mod where_keyword {} -#[doc(keyword = "while")] -// -/// Loop while a condition is upheld. -/// -/// A `while` expression is used for predicate loops. The `while` expression runs the conditional -/// expression before running the loop body, then runs the loop body if the conditional -/// expression evaluates to `true`, or exits the loop otherwise. -/// -/// ```rust -/// let mut counter = 0; -/// -/// while counter < 10 { -/// println!("{counter}"); -/// counter += 1; -/// } -/// ``` -/// -/// Like the [`for`] expression, we can use `break` and `continue`. A `while` expression -/// cannot break with a value and always evaluates to `()` unlike [`loop`]. -/// -/// ```rust -/// let mut i = 1; -/// -/// while i < 100 { -/// i *= 2; -/// if i == 64 { -/// break; // Exit when `i` is 64. -/// } -/// } -/// ``` -/// -/// As `if` expressions have their pattern matching variant in `if let`, so too do `while` -/// expressions with `while let`. The `while let` expression matches the pattern against the -/// expression, then runs the loop body if pattern matching succeeds, or exits the loop otherwise. -/// We can use `break` and `continue` in `while let` expressions just like in `while`. -/// -/// ```rust -/// let mut counter = Some(0); -/// -/// while let Some(i) = counter { -/// if i == 10 { -/// counter = None; -/// } else { -/// println!("{i}"); -/// counter = Some (i + 1); -/// } -/// } -/// ``` -/// -/// For more information on `while` and loops in general, see the [reference]. -/// -/// See also, [`for`], [`loop`]. -/// -/// [`for`]: keyword.for.html -/// [`loop`]: keyword.loop.html -/// [reference]: ../reference/expressions/loop-expr.html#predicate-loops -mod while_keyword {} - // 2018 Edition keywords #[doc(alias = "promise")] @@ -2387,12 +2387,13 @@ mod async_keyword {} /// [`async`]: ../std/keyword.async.html mod await_keyword {} +// FIXME(dyn_compat_renaming): Update URL and link text. #[doc(keyword = "dyn")] // /// `dyn` is a prefix of a [trait object]'s type. /// /// The `dyn` keyword is used to highlight that calls to methods on the associated `Trait` -/// are [dynamically dispatched]. To use the trait this way, it must be *dyn compatible*[^1]. +/// are [dynamically dispatched]. To use the trait this way, it must be 'dyn-compatible'[^1]. /// /// Unlike generic parameters or `impl Trait`, the compiler does not know the concrete type that /// is being passed. That is, the type has been [erased]. @@ -2405,7 +2406,7 @@ mod await_keyword {} /// the function pointer and then that function pointer is called. /// /// See the Reference for more information on [trait objects][ref-trait-obj] -/// and [dyn compatibility][ref-dyn-compat]. +/// and [object safety][ref-obj-safety]. /// /// ## Trade-offs /// @@ -2418,9 +2419,9 @@ mod await_keyword {} /// [trait object]: ../book/ch17-02-trait-objects.html /// [dynamically dispatched]: https://en.wikipedia.org/wiki/Dynamic_dispatch /// [ref-trait-obj]: ../reference/types/trait-object.html -/// [ref-dyn-compat]: ../reference/items/traits.html#dyn-compatibility +/// [ref-obj-safety]: ../reference/items/traits.html#object-safety /// [erased]: https://en.wikipedia.org/wiki/Type_erasure -/// [^1]: Formerly known as *object safe*. +/// [^1]: Formerly known as 'object safe'. mod dyn_keyword {} #[doc(keyword = "union")] diff --git a/std/src/lib.rs b/std/src/lib.rs index 954a4182fbd6c..ee6fceb024fd7 100644 --- a/std/src/lib.rs +++ b/std/src/lib.rs @@ -89,7 +89,7 @@ //! Check out the Rust contribution guidelines [here]( //! https://rustc-dev-guide.rust-lang.org/contributing.html#writing-documentation). //! The source for this documentation can be found on -//! [GitHub](https://github.com/rust-lang/rust) in the 'library/std/' directory. +//! [GitHub](https://github.com/rust-lang/rust). //! To contribute changes, make sure you read the guidelines first, then submit //! pull-requests for your suggested changes. //! @@ -251,6 +251,7 @@ #![allow(explicit_outlives_requirements)] #![allow(unused_lifetimes)] #![allow(internal_features)] +#![deny(rustc::existing_doc_keyword)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] #![allow(rustdoc::redundant_explicit_links)] @@ -261,6 +262,7 @@ #![allow(unused_features)] // // Features: +#![cfg_attr(not(bootstrap), feature(autodiff))] #![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), @@ -272,12 +274,13 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(strict_provenance))] +#![cfg_attr(not(bootstrap), feature(strict_provenance_lints))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_experimental_arch)] -#![feature(autodiff)] #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] @@ -289,9 +292,9 @@ #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] +#![feature(extended_varargs_abi_support)] #![feature(f128)] #![feature(f16)] -#![feature(formatting_options)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(lang_items)] @@ -311,7 +314,6 @@ #![feature(rustdoc_internals)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] -#![feature(strict_provenance_lints)] #![feature(thread_local)] #![feature(try_blocks)] #![feature(type_alias_impl_trait)] @@ -320,8 +322,7 @@ // Library features (core): // tidy-alphabetical-start #![feature(array_chunks)] -#![feature(bstr)] -#![feature(bstr_internals)] +#![feature(build_hasher_default_const_new)] #![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -335,20 +336,20 @@ #![feature(extend_one)] #![feature(float_gamma)] #![feature(float_minimum_maximum)] +#![feature(float_next_up_down)] #![feature(fmt_internals)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] -#![feature(hint_must_use)] #![feature(ip)] #![feature(lazy_get)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] -#![feature(nonnull_provenance)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] +#![feature(prelude_2024)] #![feature(ptr_as_uninit)] #![feature(ptr_mask)] #![feature(random)] @@ -359,9 +360,7 @@ #![feature(str_internals)] #![feature(strict_provenance_atomic_ptr)] #![feature(sync_unsafe_cell)] -#![feature(temporary_niche_types)] #![feature(ub_checks)] -#![feature(used_with_arg)] // tidy-alphabetical-end // // Library features (alloc): @@ -375,7 +374,6 @@ #![feature(thin_box)] #![feature(try_reserve_kind)] #![feature(try_with_capacity)] -#![feature(unique_rc_arc)] #![feature(vec_into_raw_parts)] // tidy-alphabetical-end // @@ -411,7 +409,8 @@ // // Only for const-ness: // tidy-alphabetical-start -#![feature(io_const_error)] +#![feature(const_collections_with_hasher)] +#![feature(thread_local_internals)] // tidy-alphabetical-end // #![default_lib_allocator] @@ -530,8 +529,6 @@ 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")] @@ -549,8 +546,6 @@ pub use core::u64; #[stable(feature = "i128", since = "1.26.0")] #[allow(deprecated, deprecated_in_future)] pub use core::u128; -#[unstable(feature = "unsafe_binders", issue = "130516")] -pub use core::unsafe_binder; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::usize; @@ -585,8 +580,6 @@ 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; @@ -598,9 +591,11 @@ pub mod net; pub mod num; pub mod os; pub mod panic; -#[unstable(feature = "pattern_type_macro", issue = "123646")] +#[unstable(feature = "core_pattern_types", issue = "123646")] pub mod pat; pub mod path; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod pipe; pub mod process; #[unstable(feature = "random", issue = "130703")] pub mod random; @@ -625,6 +620,7 @@ pub mod simd { #[doc(inline)] pub use crate::std_float::StdFloat; } +#[cfg(not(bootstrap))] #[unstable(feature = "autodiff", issue = "124509")] /// This module provides support for automatic differentiation. pub mod autodiff { @@ -739,4 +735,27 @@ mod sealed { #[cfg(test)] #[allow(dead_code)] // Not used in all configurations. -pub(crate) mod test_helpers; +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::>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + rand::SeedableRng::from_seed(seed) + } +} diff --git a/std/src/macros.rs b/std/src/macros.rs index e0f9f0bb5cee4..1b0d7f3dbf2c9 100644 --- a/std/src/macros.rs +++ b/std/src/macros.rs @@ -372,3 +372,18 @@ 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/std/src/net/mod.rs b/std/src/net/mod.rs index ddd3b68dd2d63..3b19c743b1e24 100644 --- a/std/src/net/mod.rs +++ b/std/src/net/mod.rs @@ -84,6 +84,6 @@ where } } Err(last_err.unwrap_or_else(|| { - io::const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses") + io::const_io_error!(ErrorKind::InvalidInput, "could not resolve to any addresses") })) } diff --git a/std/src/net/socket_addr.rs b/std/src/net/socket_addr.rs index e8355cc31d7a5..ba9c948a2e96f 100644 --- a/std/src/net/socket_addr.rs +++ b/std/src/net/socket_addr.rs @@ -6,7 +6,8 @@ mod tests; pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::sys::net::{LookupHost, netc as c}; +use crate::sys::net::netc as c; +use crate::sys_common::net::LookupHost; use crate::sys_common::{FromInner, IntoInner}; use crate::{io, iter, mem, option, slice, vec}; diff --git a/std/src/net/tcp.rs b/std/src/net/tcp.rs index 9b68f872955c0..67a0f7e439d55 100644 --- a/std/src/net/tcp.rs +++ b/std/src/net/tcp.rs @@ -15,8 +15,7 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys::net as net_imp; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. diff --git a/std/src/net/test.rs b/std/src/net/test.rs index a5c3983cd89ec..d318d457f3569 100644 --- a/std/src/net/test.rs +++ b/std/src/net/test.rs @@ -5,15 +5,14 @@ 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)) } @@ -31,3 +30,31 @@ pub fn tsa(a: A) -> Result, 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/std/src/net/udp.rs b/std/src/net/udp.rs index 3eb798ad34aaa..6df47d7b0e0cd 100644 --- a/std/src/net/udp.rs +++ b/std/src/net/udp.rs @@ -12,8 +12,7 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys::net as net_imp; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; use crate::time::Duration; /// A UDP socket. @@ -204,7 +203,9 @@ impl UdpSocket { pub fn send_to(&self, buf: &[u8], addr: A) -> io::Result { match addr.to_socket_addrs()?.next() { Some(addr) => self.0.send_to(buf, &addr), - None => Err(io::const_error!(ErrorKind::InvalidInput, "no addresses to send data to")), + None => { + Err(io::const_io_error!(ErrorKind::InvalidInput, "no addresses to send data to")) + } } } diff --git a/std/src/num.rs b/std/src/num.rs index ffb8789c906ef..d2f679e7dde54 100644 --- a/std/src/num.rs +++ b/std/src/num.rs @@ -6,6 +6,9 @@ #![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")] @@ -26,3 +29,28 @@ 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(ten: T, two: T) +where + T: PartialEq + + Add + + Sub + + Mul + + Div + + Rem + + 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/std/tests/num.rs b/std/src/num/tests.rs similarity index 98% rename from std/tests/num.rs rename to std/src/num/tests.rs index a7400f1c02df0..df0df3f23f756 100644 --- a/std/tests/num.rs +++ b/std/src/num/tests.rs @@ -1,4 +1,4 @@ -use std::ops::Mul; +use crate::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: &str) -> Option { - std::str::FromStr::from_str(t).ok() +fn from_str(t: &str) -> Option { + crate::str::FromStr::from_str(t).ok() } #[test] diff --git a/std/src/os/darwin/mod.rs b/std/src/os/darwin/mod.rs index 3b1bd974fa313..7a057ddb861b7 100644 --- a/std/src/os/darwin/mod.rs +++ b/std/src/os/darwin/mod.rs @@ -13,7 +13,7 @@ //! `aarch64-apple-darwin` target names, which are mostly named that way for //! legacy reasons. -#![stable(feature = "os_darwin", since = "1.84.0")] +#![stable(feature = "os_darwin", since = "CURRENT_RUSTC_VERSION")] #![doc(cfg(target_vendor = "apple"))] pub mod fs; diff --git a/std/src/os/emscripten/fs.rs b/std/src/os/emscripten/fs.rs index 81f9ef331a5fa..3282b79ac1c81 100644 --- a/std/src/os/emscripten/fs.rs +++ b/std/src/os/emscripten/fs.rs @@ -63,7 +63,7 @@ pub trait MetadataExt { impl MetadataExt for Metadata { #[allow(deprecated)] fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } } fn st_dev(&self) -> u64 { self.as_inner().as_inner().st_dev as u64 diff --git a/std/src/os/emscripten/raw.rs b/std/src/os/emscripten/raw.rs index 7ae8c45a6f80a..d23011c738141 100644 --- a/std/src/os/emscripten/raw.rs +++ b/std/src/os/emscripten/raw.rs @@ -1,4 +1,6 @@ //! Emscripten-specific raw type definitions +//! This is basically exactly the same as the linux definitions, +//! except using the musl-specific stat64 structure in liblibc. #![stable(feature = "raw_ext", since = "1.1.0")] #![deprecated( diff --git a/std/src/os/fd/net.rs b/std/src/os/fd/net.rs index 34479ca0e190e..843f45f7f5f98 100644 --- a/std/src/os/fd/net.rs +++ b/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::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{self, 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::net::$t::from_inner(socket)) + net::$t::from_inner(sys_common::net::$t::from_inner(socket)) } } } diff --git a/std/src/os/fd/owned.rs b/std/src/os/fd/owned.rs index 1e814eca3c1a5..388b8a88a1a48 100644 --- a/std/src/os/fd/owned.rs +++ b/std/src/os/fd/owned.rs @@ -11,8 +11,6 @@ use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, fs, io}; -type ValidRawFd = core::num::niche_types::NotAllOnes; - /// A borrowed file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that owns the file @@ -34,10 +32,15 @@ type ValidRawFd = core::num::niche_types::NotAllOnes; /// instead, but this is not supported on all platforms. #[derive(Copy, Clone)] #[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a +// 32-bit c_int. Below is -2, in two's complement, but that only works out +// because c_int is 32 bits. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedFd<'fd> { - fd: ValidRawFd, + fd: RawFd, _phantom: PhantomData<&'fd OwnedFd>, } @@ -53,10 +56,15 @@ pub struct BorrowedFd<'fd> { /// /// You can use [`AsFd::as_fd`] to obtain a [`BorrowedFd`]. #[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a +// 32-bit c_int. Below is -2, in two's complement, but that only works out +// because c_int is 32 bits. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedFd { - fd: ValidRawFd, + fd: RawFd, } impl BorrowedFd<'_> { @@ -72,8 +80,7 @@ impl BorrowedFd<'_> { pub const unsafe fn borrow_raw(fd: RawFd) -> Self { assert!(fd != u32::MAX as RawFd); // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - let fd = unsafe { ValidRawFd::new_unchecked(fd) }; - Self { fd, _phantom: PhantomData } + unsafe { Self { fd, _phantom: PhantomData } } } } @@ -123,7 +130,7 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd.as_inner() + self.fd } } @@ -131,7 +138,7 @@ impl AsRawFd for BorrowedFd<'_> { impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd.as_inner() + self.fd } } @@ -139,7 +146,7 @@ impl AsRawFd for OwnedFd { impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd.as_inner() + ManuallyDrop::new(self).fd } } @@ -157,8 +164,7 @@ impl FromRawFd for OwnedFd { unsafe fn from_raw_fd(fd: RawFd) -> Self { assert_ne!(fd, u32::MAX as RawFd); // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - let fd = unsafe { ValidRawFd::new_unchecked(fd) }; - Self { fd } + unsafe { Self { fd } } } } @@ -181,12 +187,12 @@ impl Drop for OwnedFd { #[cfg(not(target_os = "hermit"))] { #[cfg(unix)] - crate::sys::fs::debug_assert_fd_is_open(self.fd.as_inner()); + crate::sys::fs::debug_assert_fd_is_open(self.fd); - let _ = libc::close(self.fd.as_inner()); + let _ = libc::close(self.fd); } #[cfg(target_os = "hermit")] - let _ = hermit_abi::close(self.fd.as_inner()); + let _ = hermit_abi::close(self.fd); } } } @@ -422,14 +428,6 @@ impl AsFd for crate::rc::Rc { } } -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl AsFd for crate::rc::UniqueRc { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - (**self).as_fd() - } -} - #[stable(feature = "asfd_ptrs", since = "1.64.0")] impl AsFd for Box { #[inline] diff --git a/std/src/os/fd/raw.rs b/std/src/os/fd/raw.rs index 03dff94350dad..0d99d5492a268 100644 --- a/std/src/os/fd/raw.rs +++ b/std/src/os/fd/raw.rs @@ -19,9 +19,11 @@ use crate::sys_common::{AsInner, IntoInner}; use crate::{fs, io}; /// Raw file descriptors. +#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(target_os = "hermit"))] pub type RawFd = raw::c_int; +#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(target_os = "hermit")] pub type RawFd = i32; @@ -31,6 +33,7 @@ pub type RawFd = i32; /// This is only available on unix and WASI platforms and must be imported in /// order to call the method. Windows platforms have a corresponding /// `AsRawHandle` and `AsRawSocket` set of traits. +#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRawFd { /// Extracts the raw file descriptor. @@ -64,6 +67,7 @@ pub trait AsRawFd { /// A trait to express the ability to construct an object from a raw file /// descriptor. +#[rustc_allowed_through_unstable_modules] #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawFd { /// Constructs a new instance of `Self` from the given raw file @@ -108,6 +112,7 @@ pub trait FromRawFd { /// A trait to express the ability to consume an object and acquire ownership of /// its raw file descriptor. +#[rustc_allowed_through_unstable_modules] #[stable(feature = "into_raw_os", since = "1.4.0")] pub trait IntoRawFd { /// Consumes this object, returning the raw underlying file descriptor. @@ -261,14 +266,6 @@ impl AsRawFd for crate::rc::Rc { } } -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl AsRawFd for crate::rc::UniqueRc { - #[inline] - fn as_raw_fd(&self) -> RawFd { - (**self).as_raw_fd() - } -} - #[stable(feature = "asrawfd_ptrs", since = "1.63.0")] impl AsRawFd for Box { #[inline] diff --git a/std/src/os/hermit/io/net.rs b/std/src/os/hermit/io/net.rs index 233bc885fc733..7a774345b231a 100644 --- a/std/src/os/hermit/io/net.rs +++ b/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::net::$t::from_inner(socket)) + net::$t::from_inner(sys_common::net::$t::from_inner(socket)) } } } diff --git a/std/src/os/hurd/fs.rs b/std/src/os/hurd/fs.rs index e3087fa8af1cc..00ff1560f31d9 100644 --- a/std/src/os/hurd/fs.rs +++ b/std/src/os/hurd/fs.rs @@ -298,7 +298,7 @@ pub trait MetadataExt { #[stable(feature = "metadata_ext", since = "1.1.0")] impl MetadataExt for Metadata { fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 + self.as_inner().as_inner().st_fsid as u64 } fn st_ino(&self) -> u64 { self.as_inner().as_inner().st_ino as u64 diff --git a/std/src/os/hurd/mod.rs b/std/src/os/hurd/mod.rs index 6cd50aeada1da..aee86c7f61655 100644 --- a/std/src/os/hurd/mod.rs +++ b/std/src/os/hurd/mod.rs @@ -1,7 +1,6 @@ //! Hurd-specific definitions #![stable(feature = "raw_ext", since = "1.1.0")] -#![forbid(unsafe_op_in_unsafe_fn)] pub mod fs; pub mod raw; diff --git a/std/src/os/solid/io.rs b/std/src/os/solid/io.rs index b8c3440542d00..2d18f33961506 100644 --- a/std/src/os/solid/io.rs +++ b/std/src/os/solid/io.rs @@ -48,15 +48,12 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{self, AsInner, FromInner, IntoInner}; use crate::{fmt, net, sys}; /// Raw file descriptors. pub type RawFd = i32; -// The max of this is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. -type ValidRawFd = core::num::niche_types::NotAllOnes; - /// A borrowed SOLID Sockets file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -72,9 +69,12 @@ type ValidRawFd = core::num::niche_types::NotAllOnes; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct BorrowedFd<'socket> { - fd: ValidRawFd, + fd: RawFd, _phantom: PhantomData<&'socket OwnedFd>, } @@ -87,9 +87,12 @@ pub struct BorrowedFd<'socket> { /// an argument, it is not captured or consumed, and it never has the value /// `SOLID_NET_INVALID_FD`. #[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct OwnedFd { - fd: ValidRawFd, + fd: RawFd, } impl BorrowedFd<'_> { @@ -105,8 +108,7 @@ impl BorrowedFd<'_> { assert!(fd != -1 as RawFd); // SAFETY: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - let fd = unsafe { ValidRawFd::new_unchecked(fd) }; - Self { fd, _phantom: PhantomData } + unsafe { Self { fd, _phantom: PhantomData } } } } @@ -130,21 +132,21 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd.as_inner() + self.fd } } impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd.as_inner() + self.fd } } impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd.as_inner() + ManuallyDrop::new(self).fd } } @@ -160,15 +162,14 @@ impl FromRawFd for OwnedFd { assert_ne!(fd, -1 as RawFd); // SAFETY: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - let fd = unsafe { ValidRawFd::new_unchecked(fd) }; - Self { fd } + unsafe { Self { fd } } } } impl Drop for OwnedFd { #[inline] fn drop(&mut self) { - unsafe { sys::net::netc::close(self.fd.as_inner()) }; + unsafe { sys::net::netc::close(self.fd) }; } } @@ -387,7 +388,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::net::$t::from_inner(socket)) + net::$t::from_inner(sys_common::net::$t::from_inner(socket)) } } )*}; diff --git a/std/src/os/unix/fs.rs b/std/src/os/unix/fs.rs index 04a45fd035a55..ba6481f052cdf 100644 --- a/std/src/os/unix/fs.rs +++ b/std/src/os/unix/fs.rs @@ -987,11 +987,6 @@ impl DirBuilderExt for fs::DirBuilder { /// Changing the group typically requires either being the owner and a member of the group, or /// having privileges. /// -/// Be aware that changing owner clears the `suid` and `sgid` permission bits in most cases -/// according to POSIX, usually even if the user is root. The sgid is not cleared when -/// the file is non-group-executable. See: -/// This call may also clear file capabilities, if there was any. -/// /// If called on a symbolic link, this will change the owner and group of the link target. To /// change the owner and group of the link itself, see [`lchown`]. /// diff --git a/std/src/os/unix/fs/tests.rs b/std/src/os/unix/fs/tests.rs index db9621c8c205c..67f607bd46837 100644 --- a/std/src/os/unix/fs/tests.rs +++ b/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::test_helpers::tmpdir(); + let dir = crate::sys_common::io::test::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::test_helpers::tmpdir(); + let dir = crate::sys_common::io::test::tmpdir(); let filename = dir.join("preadv.txt"); { diff --git a/std/src/os/unix/net/addr.rs b/std/src/os/unix/net/addr.rs index 56789f235fdab..253e1503cf7af 100644 --- a/std/src/os/unix/net/addr.rs +++ b/std/src/os/unix/net/addr.rs @@ -30,14 +30,14 @@ pub(super) fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::s let bytes = path.as_os_str().as_bytes(); if bytes.contains(&0) { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "paths must not contain interior null bytes", )); } if bytes.len() >= addr.sun_path.len() { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "path must be shorter than SUN_LEN", )); @@ -119,7 +119,7 @@ impl SocketAddr { // linux returns zero bytes of address len = SUN_PATH_OFFSET as libc::socklen_t; // i.e., zero-length address } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "file descriptor did not correspond to a Unix socket", )); @@ -273,7 +273,7 @@ impl linux_ext::addr::SocketAddrExt for SocketAddr { addr.sun_family = libc::AF_UNIX as libc::sa_family_t; if name.len() + 1 > addr.sun_path.len() { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "abstract socket name must be shorter than SUN_LEN", )); diff --git a/std/src/os/unix/net/tests.rs b/std/src/os/unix/net/tests.rs index 0398a535eb54a..21e2176185d25 100644 --- a/std/src/os/unix/net/tests.rs +++ b/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::test_helpers::tmpdir; +use crate::sys_common::io::test::tmpdir; use crate::thread; use crate::time::Duration; diff --git a/std/src/os/wasi/fs.rs b/std/src/os/wasi/fs.rs index 42aada131dadc..9ec3e387e2ba9 100644 --- a/std/src/os/wasi/fs.rs +++ b/std/src/os/wasi/fs.rs @@ -261,7 +261,7 @@ impl FileExt for fs::File { a if a == wasi::ADVICE_DONTNEED.raw() => wasi::ADVICE_DONTNEED, a if a == wasi::ADVICE_NOREUSE.raw() => wasi::ADVICE_NOREUSE, _ => { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "invalid parameter 'advice'", )); @@ -560,5 +560,6 @@ pub fn symlink_path, U: AsRef>(old_path: P, new_path: U) -> } fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) + f.to_str() + .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) } diff --git a/std/src/os/wasi/io/fd.rs b/std/src/os/wasi/io/fd.rs new file mode 100644 index 0000000000000..930aca887e3c4 --- /dev/null +++ b/std/src/os/wasi/io/fd.rs @@ -0,0 +1,9 @@ +//! Owned and borrowed file descriptors. + +#![unstable(feature = "wasi_ext", issue = "71213")] + +// Tests for this module +#[cfg(test)] +mod tests; + +pub use crate::os::fd::owned::*; diff --git a/std/src/os/wasi/io/tests.rs b/std/src/os/wasi/io/fd/tests.rs similarity index 100% rename from std/src/os/wasi/io/tests.rs rename to std/src/os/wasi/io/fd/tests.rs diff --git a/std/src/os/wasi/io/mod.rs b/std/src/os/wasi/io/mod.rs index 5f9a735db085e..4e123a1eec8ae 100644 --- a/std/src/os/wasi/io/mod.rs +++ b/std/src/os/wasi/io/mod.rs @@ -4,7 +4,3 @@ #[stable(feature = "io_safety_wasi", since = "1.65.0")] pub use crate::os::fd::*; - -// Tests for this module -#[cfg(test)] -mod tests; diff --git a/std/src/os/wasi/io/raw.rs b/std/src/os/wasi/io/raw.rs new file mode 100644 index 0000000000000..da3b36adad409 --- /dev/null +++ b/std/src/os/wasi/io/raw.rs @@ -0,0 +1,20 @@ +//! WASI-specific extensions to general I/O primitives. + +#![unstable(feature = "wasi_ext", issue = "71213")] + +// NOTE: despite the fact that this module is unstable, +// stable Rust had the capability to access the stable +// re-exported items from os::fd::raw through this +// unstable module. +// In PR #95956 the stability checker was changed to check +// all path segments of an item rather than just the last, +// which caused the aforementioned stable usage to regress +// (see issue #99502). +// As a result, the items in os::fd::raw were given the +// rustc_allowed_through_unstable_modules attribute. +// No regression tests were added to ensure this property, +// as CI is not configured to test wasm32-wasi. +// If this module is stabilized, +// you may want to remove those attributes +// (assuming no other unstable modules need them). +pub use crate::os::fd::raw::*; diff --git a/std/src/os/windows/io/handle.rs b/std/src/os/windows/io/handle.rs index 76f5f549dd244..a4fa94e2b96a4 100644 --- a/std/src/os/windows/io/handle.rs +++ b/std/src/os/windows/io/handle.rs @@ -485,14 +485,6 @@ impl AsHandle for crate::rc::Rc { } } -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl AsHandle for crate::rc::UniqueRc { - #[inline] - fn as_handle(&self) -> BorrowedHandle<'_> { - (**self).as_handle() - } -} - #[stable(feature = "as_windows_ptrs", since = "1.71.0")] impl AsHandle for Box { #[inline] diff --git a/std/src/os/windows/io/raw.rs b/std/src/os/windows/io/raw.rs index c0517fab95068..6658248d574c9 100644 --- a/std/src/os/windows/io/raw.rs +++ b/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::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{self, 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::net::TcpStream::from_inner(sock)) + net::TcpStream::from_inner(sys_common::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::net::TcpListener::from_inner(sock)) + net::TcpListener::from_inner(sys_common::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::net::UdpSocket::from_inner(sock)) + net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) } } } diff --git a/std/src/os/windows/io/socket.rs b/std/src/os/windows/io/socket.rs index 6e13a8b502a73..1fcfb6e73ad03 100644 --- a/std/src/os/windows/io/socket.rs +++ b/std/src/os/windows/io/socket.rs @@ -9,9 +9,6 @@ use crate::mem::{self, ManuallyDrop}; use crate::sys::cvt; use crate::{fmt, io, sys}; -// The max here is -2, in two's complement. -1 is `INVALID_SOCKET`. -type ValidRawSocket = core::num::niche_types::NotAllOnes; - /// A borrowed socket. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -27,10 +24,17 @@ type ValidRawSocket = core::num::niche_types::NotAllOnes; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `INVALID_SOCKET`. +#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] +#[cfg_attr( + target_pointer_width = "64", + rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) +)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedSocket<'socket> { - socket: ValidRawSocket, + socket: RawSocket, _phantom: PhantomData<&'socket OwnedSocket>, } @@ -43,10 +47,17 @@ pub struct BorrowedSocket<'socket> { /// argument or returned as an owned value, and it never has the value /// `INVALID_SOCKET`. #[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `INVALID_SOCKET`. +#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] +#[cfg_attr( + target_pointer_width = "64", + rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) +)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedSocket { - socket: ValidRawSocket, + socket: RawSocket, } impl BorrowedSocket<'_> { @@ -62,8 +73,7 @@ impl BorrowedSocket<'_> { #[stable(feature = "io_safety", since = "1.63.0")] pub const unsafe fn borrow_raw(socket: RawSocket) -> Self { assert!(socket != sys::c::INVALID_SOCKET as RawSocket); - let socket = unsafe { ValidRawSocket::new_unchecked(socket) }; - Self { socket, _phantom: PhantomData } + unsafe { Self { socket, _phantom: PhantomData } } } } @@ -91,7 +101,7 @@ impl OwnedSocket { #[cfg(target_vendor = "uwp")] pub(crate) fn set_no_inherit(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "Unavailable on UWP")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "Unavailable on UWP")) } } @@ -162,7 +172,7 @@ fn last_error() -> io::Error { impl AsRawSocket for BorrowedSocket<'_> { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket.as_inner() + self.socket } } @@ -170,7 +180,7 @@ impl AsRawSocket for BorrowedSocket<'_> { impl AsRawSocket for OwnedSocket { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket.as_inner() + self.socket } } @@ -178,7 +188,7 @@ impl AsRawSocket for OwnedSocket { impl IntoRawSocket for OwnedSocket { #[inline] fn into_raw_socket(self) -> RawSocket { - ManuallyDrop::new(self).socket.as_inner() + ManuallyDrop::new(self).socket } } @@ -186,9 +196,10 @@ impl IntoRawSocket for OwnedSocket { impl FromRawSocket for OwnedSocket { #[inline] unsafe fn from_raw_socket(socket: RawSocket) -> Self { - debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); - let socket = unsafe { ValidRawSocket::new_unchecked(socket) }; - Self { socket } + unsafe { + debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); + Self { socket } + } } } @@ -197,7 +208,7 @@ impl Drop for OwnedSocket { #[inline] fn drop(&mut self) { unsafe { - let _ = sys::c::closesocket(self.socket.as_inner() as sys::c::SOCKET); + let _ = sys::c::closesocket(self.socket as sys::c::SOCKET); } } } @@ -268,14 +279,6 @@ impl AsSocket for crate::rc::Rc { } } -#[unstable(feature = "unique_rc_arc", issue = "112566")] -impl AsSocket for crate::rc::UniqueRc { - #[inline] - fn as_socket(&self) -> BorrowedSocket<'_> { - (**self).as_socket() - } -} - #[stable(feature = "as_windows_ptrs", since = "1.71.0")] impl AsSocket for Box { #[inline] diff --git a/std/src/os/windows/process.rs b/std/src/os/windows/process.rs index 201274cf03aec..c2830d2eb61d1 100644 --- a/std/src/os/windows/process.rs +++ b/std/src/os/windows/process.rs @@ -4,14 +4,13 @@ #![stable(feature = "process_extensions", since = "1.2.0")] -use crate::ffi::{OsStr, c_void}; -use crate::mem::MaybeUninit; +use crate::ffi::OsStr; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; use crate::sealed::Sealed; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; -use crate::{io, marker, process, ptr, sys}; +use crate::{process, sys}; #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawHandle for process::Stdio { @@ -296,25 +295,41 @@ pub trait CommandExt: Sealed { #[unstable(feature = "windows_process_extensions_async_pipes", issue = "98289")] fn async_pipes(&mut self, always_async: bool) -> &mut process::Command; - /// Executes the command as a child process with the given - /// [`ProcThreadAttributeList`], returning a handle to it. + /// Set a raw attribute on the command, providing extended configuration options for Windows + /// processes. /// - /// This method enables the customization of attributes for the spawned - /// child process on Windows systems. - /// Attributes offer extended configurability for process creation, - /// but their usage can be intricate and potentially unsafe. + /// This method allows you to specify custom attributes for a child process on Windows systems + /// using raw attribute values. Raw attributes provide extended configurability for process + /// creation, but their usage can be complex and potentially unsafe. + /// + /// The `attribute` parameter specifies the raw attribute to be set, while the `value` + /// parameter holds the value associated with that attribute. Please refer to the + /// [`windows-rs` documentation] or the [Win32 API documentation] for detailed information + /// about available attributes and their meanings. + /// + /// [`windows-rs` documentation]: https://microsoft.github.io/windows-docs-rs/doc/windows/ + /// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute /// /// # Note /// - /// By default, stdin, stdout, and stderr are inherited from the parent - /// process. + /// The maximum number of raw attributes is the value of [`u32::MAX`]. + /// If this limit is exceeded, the call to [`process::Command::spawn`] will return an `Error` + /// indicating that the maximum number of attributes has been exceeded. + /// + /// # Safety + /// + /// The usage of raw attributes is potentially unsafe and should be done with caution. + /// Incorrect attribute values or improper configuration can lead to unexpected behavior or + /// errors. /// /// # Example /// - /// ``` + /// The following example demonstrates how to create a child process with a specific parent + /// process ID using a raw attribute. + /// + /// ```rust /// #![feature(windows_process_extensions_raw_attribute)] - /// use std::os::windows::io::AsRawHandle; - /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; + /// use std::os::windows::{process::CommandExt, io::AsRawHandle}; /// use std::process::Command; /// /// # struct ProcessDropGuard(std::process::Child); @@ -323,27 +338,36 @@ pub trait CommandExt: Sealed { /// # let _ = self.0.kill(); /// # } /// # } - /// # + /// /// let parent = Command::new("cmd").spawn()?; - /// let parent_process_handle = parent.as_raw_handle(); - /// # let parent = ProcessDropGuard(parent); + /// + /// let mut child_cmd = Command::new("cmd"); /// /// const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; - /// let mut attribute_list = ProcThreadAttributeList::build() - /// .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle) - /// .finish() - /// .unwrap(); /// - /// let mut child = Command::new("cmd").spawn_with_attributes(&attribute_list)?; + /// unsafe { + /// child_cmd.raw_attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, parent.as_raw_handle() as isize); + /// } /// # + /// # let parent = ProcessDropGuard(parent); + /// + /// let mut child = child_cmd.spawn()?; + /// /// # child.kill()?; /// # Ok::<(), std::io::Error>(()) /// ``` + /// + /// # Safety Note + /// + /// Remember that improper use of raw attributes can lead to undefined behavior or security + /// vulnerabilities. Always consult the documentation and ensure proper attribute values are + /// used. #[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] - fn spawn_with_attributes( + unsafe fn raw_attribute( &mut self, - attribute_list: &ProcThreadAttributeList<'_>, - ) -> io::Result; + attribute: usize, + value: T, + ) -> &mut process::Command; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -377,13 +401,13 @@ impl CommandExt for process::Command { self } - fn spawn_with_attributes( + unsafe fn raw_attribute( &mut self, - attribute_list: &ProcThreadAttributeList<'_>, - ) -> io::Result { - self.as_inner_mut() - .spawn_with_attributes(sys::process::Stdio::Inherit, true, Some(attribute_list)) - .map(process::Child::from_inner) + attribute: usize, + value: T, + ) -> &mut process::Command { + unsafe { self.as_inner_mut().raw_attribute(attribute, value) }; + self } } @@ -423,245 +447,3 @@ impl ExitCodeExt for process::ExitCode { process::ExitCode::from_inner(From::from(raw)) } } - -/// A wrapper around windows [`ProcThreadAttributeList`][1]. -/// -/// [1]: -#[derive(Debug)] -#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] -pub struct ProcThreadAttributeList<'a> { - attribute_list: Box<[MaybeUninit]>, - _lifetime_marker: marker::PhantomData<&'a ()>, -} - -#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] -impl<'a> ProcThreadAttributeList<'a> { - /// Creates a new builder for constructing a [`ProcThreadAttributeList`]. - pub fn build() -> ProcThreadAttributeListBuilder<'a> { - ProcThreadAttributeListBuilder::new() - } - - /// Returns a pointer to the underling attribute list. - #[doc(hidden)] - pub fn as_ptr(&self) -> *const MaybeUninit { - self.attribute_list.as_ptr() - } -} - -#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] -impl<'a> Drop for ProcThreadAttributeList<'a> { - /// Deletes the attribute list. - /// - /// This method calls [`DeleteProcThreadAttributeList`][1] to delete the - /// underlying attribute list. - /// - /// [1]: - fn drop(&mut self) { - let lp_attribute_list = self.attribute_list.as_mut_ptr().cast::(); - unsafe { sys::c::DeleteProcThreadAttributeList(lp_attribute_list) } - } -} - -/// Builder for constructing a [`ProcThreadAttributeList`]. -#[derive(Clone, Debug)] -#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] -pub struct ProcThreadAttributeListBuilder<'a> { - attributes: alloc::collections::BTreeMap, - _lifetime_marker: marker::PhantomData<&'a ()>, -} - -#[unstable(feature = "windows_process_extensions_raw_attribute", issue = "114854")] -impl<'a> ProcThreadAttributeListBuilder<'a> { - fn new() -> Self { - ProcThreadAttributeListBuilder { - attributes: alloc::collections::BTreeMap::new(), - _lifetime_marker: marker::PhantomData, - } - } - - /// Sets an attribute on the attribute list. - /// - /// The `attribute` parameter specifies the raw attribute to be set, while - /// the `value` parameter holds the value associated with that attribute. - /// Please refer to the [Windows documentation][1] for a list of valid attributes. - /// - /// # Note - /// - /// The maximum number of attributes is the value of [`u32::MAX`]. If this - /// limit is exceeded, the call to [`Self::finish`] will return an `Error` - /// indicating that the maximum number of attributes has been exceeded. - /// - /// # Safety Note - /// - /// Remember that improper use of attributes can lead to undefined behavior - /// or security vulnerabilities. Always consult the documentation and ensure - /// proper attribute values are used. - /// - /// [1]: - pub fn attribute(self, attribute: usize, value: &'a T) -> Self { - unsafe { - self.raw_attribute( - attribute, - ptr::addr_of!(*value).cast::(), - crate::mem::size_of::(), - ) - } - } - - /// Sets a raw attribute on the attribute list. - /// - /// This function is useful for setting attributes with pointers or sizes - /// that cannot be derived directly from their values. - /// - /// # Safety - /// - /// This function is marked as `unsafe` because it deals with raw pointers - /// and sizes. It is the responsibility of the caller to ensure the value - /// lives longer than the resulting [`ProcThreadAttributeList`] as well as - /// the validity of the size parameter. - /// - /// # Example - /// - /// ``` - /// #![feature(windows_process_extensions_raw_attribute)] - /// use std::ffi::c_void; - /// use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; - /// use std::os::windows::raw::HANDLE; - /// use std::process::Command; - /// - /// #[repr(C)] - /// pub struct COORD { - /// pub X: i16, - /// pub Y: i16, - /// } - /// - /// extern "system" { - /// fn CreatePipe( - /// hreadpipe: *mut HANDLE, - /// hwritepipe: *mut HANDLE, - /// lppipeattributes: *const c_void, - /// nsize: u32, - /// ) -> i32; - /// fn CreatePseudoConsole( - /// size: COORD, - /// hinput: HANDLE, - /// houtput: HANDLE, - /// dwflags: u32, - /// phpc: *mut isize, - /// ) -> i32; - /// fn CloseHandle(hobject: HANDLE) -> i32; - /// } - /// - /// let [mut input_read_side, mut output_write_side, mut output_read_side, mut input_write_side] = - /// [unsafe { std::mem::zeroed::() }; 4]; - /// - /// unsafe { - /// CreatePipe(&mut input_read_side, &mut input_write_side, std::ptr::null(), 0); - /// CreatePipe(&mut output_read_side, &mut output_write_side, std::ptr::null(), 0); - /// } - /// - /// let size = COORD { X: 60, Y: 40 }; - /// let mut h_pc = unsafe { std::mem::zeroed() }; - /// unsafe { CreatePseudoConsole(size, input_read_side, output_write_side, 0, &mut h_pc) }; - /// - /// unsafe { CloseHandle(input_read_side) }; - /// unsafe { CloseHandle(output_write_side) }; - /// - /// const PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE: usize = 131094; - /// - /// let attribute_list = unsafe { - /// ProcThreadAttributeList::build() - /// .raw_attribute( - /// PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, - /// h_pc as *const c_void, - /// std::mem::size_of::(), - /// ) - /// .finish()? - /// }; - /// - /// let mut child = Command::new("cmd").spawn_with_attributes(&attribute_list)?; - /// # - /// # child.kill()?; - /// # Ok::<(), std::io::Error>(()) - /// ``` - pub unsafe fn raw_attribute( - mut self, - attribute: usize, - value_ptr: *const T, - value_size: usize, - ) -> Self { - self.attributes.insert( - attribute, - ProcThreadAttributeValue { ptr: value_ptr.cast::(), size: value_size }, - ); - self - } - - /// Finalizes the construction of the `ProcThreadAttributeList`. - /// - /// # Errors - /// - /// Returns an error if the maximum number of attributes is exceeded - /// or if there is an I/O error during initialization. - pub fn finish(&self) -> io::Result> { - // To initialize our ProcThreadAttributeList, we need to determine - // how many bytes to allocate for it. The Windows API simplifies this - // process by allowing us to call `InitializeProcThreadAttributeList` - // with a null pointer to retrieve the required size. - let mut required_size = 0; - let Ok(attribute_count) = self.attributes.len().try_into() else { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - "maximum number of ProcThreadAttributes exceeded", - )); - }; - unsafe { - sys::c::InitializeProcThreadAttributeList( - ptr::null_mut(), - attribute_count, - 0, - &mut required_size, - ) - }; - - let mut attribute_list = vec![MaybeUninit::uninit(); required_size].into_boxed_slice(); - - // Once we've allocated the necessary memory, it's safe to invoke - // `InitializeProcThreadAttributeList` to properly initialize the list. - sys::cvt(unsafe { - sys::c::InitializeProcThreadAttributeList( - attribute_list.as_mut_ptr().cast::(), - attribute_count, - 0, - &mut required_size, - ) - })?; - - // # Add our attributes to the buffer. - // It's theoretically possible for the attribute count to exceed a u32 - // value. Therefore, we ensure that we don't add more attributes than - // the buffer was initialized for. - for (&attribute, value) in self.attributes.iter().take(attribute_count as usize) { - sys::cvt(unsafe { - sys::c::UpdateProcThreadAttribute( - attribute_list.as_mut_ptr().cast::(), - 0, - attribute, - value.ptr, - value.size, - ptr::null_mut(), - ptr::null_mut(), - ) - })?; - } - - Ok(ProcThreadAttributeList { attribute_list, _lifetime_marker: marker::PhantomData }) - } -} - -/// Wrapper around the value data to be used as a Process Thread Attribute. -#[derive(Clone, Debug)] -struct ProcThreadAttributeValue { - ptr: *const c_void, - size: usize, -} diff --git a/std/src/os/xous/ffi/definitions.rs b/std/src/os/xous/ffi/definitions.rs index 345005bcc78d7..1b16849af03b0 100644 --- a/std/src/os/xous/ffi/definitions.rs +++ b/std/src/os/xous/ffi/definitions.rs @@ -126,42 +126,36 @@ impl From 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/std/src/panic.rs b/std/src/panic.rs index 153189b8b0315..d649357a56d71 100644 --- a/std/src/panic.rs +++ b/std/src/panic.rs @@ -529,3 +529,6 @@ pub fn get_backtrace_style() -> Option { Err(new) => BacktraceStyle::from_u8(new), } } + +#[cfg(test)] +mod tests; diff --git a/std/tests/panic.rs b/std/src/panic/tests.rs similarity index 89% rename from std/tests/panic.rs rename to std/src/panic/tests.rs index f13b931dd222e..b37d74011cc67 100644 --- a/std/tests/panic.rs +++ b/std/src/panic/tests.rs @@ -1,9 +1,9 @@ #![allow(dead_code)] -use std::cell::RefCell; -use std::panic::{AssertUnwindSafe, UnwindSafe}; -use std::rc::Rc; -use std::sync::{Arc, Mutex, RwLock}; +use crate::cell::RefCell; +use crate::panic::{AssertUnwindSafe, UnwindSafe}; +use crate::rc::Rc; +use crate::sync::{Arc, Mutex, RwLock}; struct Foo { a: i32, diff --git a/std/src/panicking.rs b/std/src/panicking.rs index 8e50bf11dd082..ac1f547c9143f 100644 --- a/std/src/panicking.rs +++ b/std/src/panicking.rs @@ -27,22 +27,6 @@ use crate::sys::backtrace; use crate::sys::stdio::panic_output; use crate::{fmt, intrinsics, process, thread}; -// This forces codegen of the function called by panic!() inside the std crate, rather than in -// downstream crates. Primarily this is useful for rustc's codegen tests, which rely on noticing -// complete removal of panic from generated IR. Since begin_panic is inline(never), it's only -// codegen'd once per crate-graph so this pushes that to std rather than our codegen test crates. -// -// (See https://github.com/rust-lang/rust/pull/123244 for more info on why). -// -// If this is causing problems we can also modify those codegen tests to use a crate type like -// cdylib which doesn't export "Rust" symbols to downstream linkage units. -#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")] -#[doc(hidden)] -#[allow(dead_code)] -#[used(compiler)] -pub static EMPTY_PANIC: fn(&'static str) -> ! = - begin_panic::<&'static str> as fn(&'static str) -> !; - // Binary interface to the panic runtime that the standard library depends on. // // The standard library is tagged with `#![needs_panic_runtime]` (introduced in @@ -81,9 +65,7 @@ extern "C" fn __rust_foreign_exception() -> ! { rtabort!("Rust cannot catch foreign exceptions"); } -#[derive(Default)] enum Hook { - #[default] Default, Custom(Box) + 'static + Sync + Send>), } @@ -98,6 +80,13 @@ impl Hook { } } +impl Default for Hook { + #[inline] + fn default() -> Hook { + Hook::Default + } +} + static HOOK: RwLock = RwLock::new(Hook::Default); /// Registers a custom panic hook, replacing the previously registered hook. @@ -258,34 +247,15 @@ fn default_hook(info: &PanicHookInfo<'_>) { let location = info.location().unwrap(); let msg = payload_as_str(info.payload()); + let thread = thread::try_current(); + let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); let write = #[optimize(size)] |err: &mut dyn crate::io::Write| { // Use a lock to prevent mixed output in multithreading context. // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. let mut lock = backtrace::lock(); - - thread::with_current_name(|name| { - let name = name.unwrap_or(""); - - // Try to write the panic message to a buffer first to prevent other concurrent outputs - // interleaving with it. - let mut buffer = [0u8; 512]; - let mut cursor = crate::io::Cursor::new(&mut buffer[..]); - - let write_msg = |dst: &mut dyn crate::io::Write| { - // We add a newline to ensure the panic message appears at the start of a line. - writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}") - }; - - if write_msg(&mut cursor).is_ok() { - let pos = cursor.position() as usize; - let _ = err.write_all(&buffer[0..pos]); - } else { - // The message did not fit into the buffer, write it directly instead. - let _ = write_msg(err); - }; - }); + let _ = writeln!(err, "thread '{name}' panicked at {location}:\n{msg}"); static FIRST_PANIC: AtomicBool = AtomicBool::new(true); @@ -637,7 +607,7 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { // Lazily, the first time this gets called, run the actual string formatting. self.string.get_or_insert_with(|| { let mut s = String::new(); - let mut fmt = fmt::Formatter::new(&mut s, fmt::FormattingOptions::new()); + let mut fmt = fmt::Formatter::new(&mut s); let _err = fmt::Display::fmt(&inner, &mut fmt); s }) diff --git a/std/src/path.rs b/std/src/path.rs index 97e17acadeac7..b0291e3aa196f 100644 --- a/std/src/path.rs +++ b/std/src/path.rs @@ -67,6 +67,9 @@ #![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}; @@ -295,7 +298,7 @@ where } // Detect scheme on Redox -pub(crate) fn has_redox_scheme(s: &[u8]) -> bool { +fn has_redox_scheme(s: &[u8]) -> bool { cfg!(target_os = "redox") && s.contains(&b':') } @@ -1155,7 +1158,6 @@ impl FusedIterator for Ancestors<'_> {} /// Note that `PathBuf` does not always sanitize arguments, for example /// [`push`] allows paths built from strings which include separators: /// -/// ``` /// use std::path::PathBuf; /// /// let mut path = PathBuf::new(); @@ -1164,7 +1166,6 @@ impl FusedIterator for Ancestors<'_> {} /// path.push("windows"); /// path.push(r"..\otherdir"); /// path.push("system32"); -/// ``` /// /// The behavior of `PathBuf` may be changed to a panic on such inputs /// in the future. [`Extend::extend`] should be used to add multi-part paths. @@ -1761,7 +1762,7 @@ impl From<&Path> for Box { } } -#[stable(feature = "box_from_mut_slice", since = "1.84.0")] +#[stable(feature = "box_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut Path> for Box { /// Creates a boxed [`Path`] from a reference. /// @@ -1999,7 +2000,7 @@ impl From<&Path> for Arc { } } -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut Path> for Arc { /// Converts a [`Path`] into an [`Arc`] by copying the [`Path`] data into a new [`Arc`] buffer. #[inline] @@ -2029,7 +2030,7 @@ impl From<&Path> for Rc { } } -#[stable(feature = "shared_from_mut_slice", since = "1.84.0")] +#[stable(feature = "shared_from_mut_slice", since = "CURRENT_RUSTC_VERSION")] impl From<&mut Path> for Rc { /// Converts a [`Path`] into an [`Rc`] by copying the [`Path`] data into a new [`Rc`] buffer. #[inline] @@ -2152,7 +2153,7 @@ impl Path { unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(s)) } } // The following (private!) function reveals the byte encoding used for OsStr. - pub(crate) fn as_u8_slice(&self) -> &[u8] { + fn as_u8_slice(&self) -> &[u8] { self.inner.as_encoded_bytes() } @@ -2320,7 +2321,12 @@ impl Path { #[must_use] #[allow(deprecated)] pub fn is_absolute(&self) -> bool { - sys::path::is_absolute(self) + if cfg!(target_os = "redox") { + // FIXME: Allow Redox prefixes + self.has_root() || has_redox_scheme(self.as_u8_slice()) + } else { + self.has_root() && (cfg!(any(unix, target_os = "wasi")) || self.prefix().is_some()) + } } /// Returns `true` if the `Path` is relative, i.e., not absolute. @@ -2343,7 +2349,7 @@ impl Path { !self.is_absolute() } - pub(crate) fn prefix(&self) -> Option> { + fn prefix(&self) -> Option> { self.components().prefix } @@ -2498,7 +2504,6 @@ impl Path { /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new(""))); /// /// assert!(path.strip_prefix("test").is_err()); - /// assert!(path.strip_prefix("/te").is_err()); /// assert!(path.strip_prefix("/haha").is_err()); /// /// let prefix = PathBuf::from("/test/"); @@ -3575,7 +3580,7 @@ impl Error for StripPrefixError { pub fn absolute>(path: P) -> io::Result { let path = path.as_ref(); if path.as_os_str().is_empty() { - Err(io::const_error!(io::ErrorKind::InvalidInput, "cannot make an empty path absolute",)) + Err(io::const_io_error!(io::ErrorKind::InvalidInput, "cannot make an empty path absolute",)) } else { sys::path::absolute(path) } diff --git a/std/tests/path.rs b/std/src/path/tests.rs similarity index 93% rename from std/tests/path.rs rename to std/src/path/tests.rs index 978402b6fdaea..ff3f7151bb834 100644 --- a/std/tests/path.rs +++ b/std/src/path/tests.rs @@ -1,19 +1,10 @@ -#![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; +use core::hint::black_box; + +use super::*; +use crate::collections::{BTreeSet, HashSet}; +use crate::hash::DefaultHasher; +use crate::mem::MaybeUninit; +use crate::ptr; #[allow(unknown_lints, unused_macro_rules)] macro_rules! t ( @@ -119,7 +110,7 @@ macro_rules! t ( #[test] fn into() { - use std::borrow::Cow; + use crate::borrow::Cow; let static_path = Path::new("/home/foo"); let static_cow_path: Cow<'static, Path> = static_path.into(); @@ -1534,7 +1525,7 @@ pub fn test_with_added_extension() { #[test] fn test_eq_receivers() { - use std::borrow::Cow; + use crate::borrow::Cow; let borrowed: &Path = Path::new("foo/bar"); let mut owned: PathBuf = PathBuf::new(); @@ -1559,7 +1550,7 @@ fn test_eq_receivers() { #[test] pub fn test_compare() { - use std::hash::{DefaultHasher, Hash, Hasher}; + use crate::hash::{DefaultHasher, Hash, Hasher}; fn hash(t: T) -> u64 { let mut s = DefaultHasher::new(); @@ -1876,12 +1867,12 @@ fn test_ord() { #[test] #[cfg(any(unix, target_os = "wasi"))] fn test_unix_absolute() { - use std::path::absolute; + use crate::path::absolute; assert!(absolute("").is_err()); let relative = "a/b"; - let mut expected = std::env::current_dir().unwrap(); + let mut expected = crate::env::current_dir().unwrap(); expected.push(relative); assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); @@ -1897,7 +1888,7 @@ fn test_unix_absolute() { ); // Test leading `.` and `..` components - let curdir = std::env::current_dir().unwrap(); + let curdir = crate::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 } @@ -1905,12 +1896,12 @@ fn test_unix_absolute() { #[test] #[cfg(windows)] fn test_windows_absolute() { - use std::path::absolute; + use crate::path::absolute; // An empty path is an error. assert!(absolute("").is_err()); let relative = r"a\b"; - let mut expected = std::env::current_dir().unwrap(); + let mut expected = crate::env::current_dir().unwrap(); expected.push(relative); assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); @@ -1962,13 +1953,125 @@ 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"); let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { storage.assume_init_ref() }); + assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { + MaybeUninit::slice_assume_init_ref(&storage) + }); let mut b: Box = Path::new("world.exe").into(); assert_eq!(size_of_val::(a), size_of_val::(&b)); diff --git a/std/src/pipe.rs b/std/src/pipe.rs new file mode 100644 index 0000000000000..891032e94a669 --- /dev/null +++ b/std/src/pipe.rs @@ -0,0 +1,128 @@ +//! Module for anonymous pipe +//! +//! ``` +//! #![feature(anonymous_pipe)] +//! +//! # #[cfg(miri)] fn main() {} +//! # #[cfg(not(miri))] +//! # fn main() -> std::io::Result<()> { +//! let (reader, writer) = std::pipe::pipe()?; +//! # Ok(()) +//! # } +//! ``` + +use crate::io; +use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; + +/// Create anonymous pipe that is close-on-exec and blocking. +#[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 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. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + 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 { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + 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) -> io::Result { + 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 { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + 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) -> io::Result { + 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 { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + 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 { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} diff --git a/std/src/io/pipe/tests.rs b/std/src/pipe/tests.rs similarity index 78% rename from std/src/io/pipe/tests.rs rename to std/src/pipe/tests.rs index f113b157459d3..9c38e10678752 100644 --- a/std/src/io/pipe/tests.rs +++ b/std/src/pipe/tests.rs @@ -1,7 +1,8 @@ -use crate::io::{Read, Write, pipe}; +use crate::io::{Read, Write}; +use crate::pipe::pipe; #[test] -#[cfg(all(any(unix, windows), not(miri)))] +#[cfg(all(windows, unix, not(miri)))] fn pipe_creation_clone_and_rw() { let (rx, tx) = pipe().unwrap(); diff --git a/std/src/prelude/common.rs b/std/src/prelude/common.rs index 0f2d8334fca79..e4731280ffe35 100644 --- a/std/src/prelude/common.rs +++ b/std/src/prelude/common.rs @@ -12,7 +12,7 @@ pub use crate::marker::{Send, Sized, Sync, Unpin}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; -#[stable(feature = "async_closure", since = "1.85.0")] +#[unstable(feature = "async_closure", issue = "62290")] #[doc(no_inline)] pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; diff --git a/std/src/prelude/mod.rs b/std/src/prelude/mod.rs index 14e6c2715df0b..0c610ba67e65c 100644 --- a/std/src/prelude/mod.rs +++ b/std/src/prelude/mod.rs @@ -25,7 +25,6 @@ //! //! # Prelude contents //! -//! The items included in the prelude depend on the edition of the crate. //! The first version of the prelude is used in Rust 2015 and Rust 2018, //! and lives in [`std::prelude::v1`]. //! [`std::prelude::rust_2015`] and [`std::prelude::rust_2018`] re-export this prelude. @@ -33,9 +32,8 @@ //! //! * [std::marker]::{[Copy], [Send], [Sized], [Sync], [Unpin]}, //! marker traits that indicate fundamental properties of types. -//! * [std::ops]::{[Fn], [FnMut], [FnOnce]}, and their analogous -//! async traits, [std::ops]::{[AsyncFn], [AsyncFnMut], [AsyncFnOnce]}. -//! * [std::ops]::[Drop], for implementing destructors. +//! * [std::ops]::{[Drop], [Fn], [FnMut], [FnOnce]}, various +//! operations for both destructors and overloading `()`. //! * [std::mem]::[drop], a convenience function for explicitly //! dropping a value. //! * [std::mem]::{[size_of], [size_of_val]}, to get the size of @@ -69,21 +67,15 @@ //! The prelude used in Rust 2021, [`std::prelude::rust_2021`], includes all of the above, //! and in addition re-exports: //! -//! * [std::convert]::{[TryFrom], [TryInto]}. +//! * [std::convert]::{[TryFrom], [TryInto]}, //! * [std::iter]::[FromIterator]. //! -//! The prelude used in Rust 2024, [`std::prelude::rust_2024`], includes all of the above, -//! and in addition re-exports: -//! -//! * [std::future]::{[Future], [IntoFuture]}. -//! //! [std::borrow]: crate::borrow //! [std::boxed]: crate::boxed //! [std::clone]: crate::clone //! [std::cmp]: crate::cmp //! [std::convert]: crate::convert //! [std::default]: crate::default -//! [std::future]: crate::future //! [std::iter]: crate::iter //! [std::marker]: crate::marker //! [std::mem]: crate::mem @@ -93,7 +85,6 @@ //! [`std::prelude::rust_2015`]: rust_2015 //! [`std::prelude::rust_2018`]: rust_2018 //! [`std::prelude::rust_2021`]: rust_2021 -//! [`std::prelude::rust_2024`]: rust_2024 //! [std::result]: crate::result //! [std::slice]: crate::slice //! [std::string]: crate::string @@ -103,8 +94,6 @@ //! [book-dtor]: ../../book/ch15-03-drop.html //! [book-enums]: ../../book/ch06-01-defining-an-enum.html //! [book-iter]: ../../book/ch13-02-iterators.html -//! [Future]: crate::future::Future -//! [IntoFuture]: crate::future::IntoFuture // No formatting: this file is nothing but re-exports, and their order is worth preserving. #![cfg_attr(rustfmt, rustfmt::skip)] @@ -120,6 +109,16 @@ 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. @@ -159,12 +158,12 @@ pub mod rust_2021 { /// The 2024 version of the prelude of The Rust Standard Library. /// /// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2024", since = "1.85.0")] +#[unstable(feature = "prelude_2024", issue = "121042")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - #[stable(feature = "prelude_2024", since = "1.85.0")] + #[unstable(feature = "prelude_2024", issue = "121042")] #[doc(no_inline)] pub use core::prelude::rust_2024::*; } diff --git a/std/src/process.rs b/std/src/process.rs index fd0fd1cb755e0..6933528cdbd0a 100644 --- a/std/src/process.rs +++ b/std/src/process.rs @@ -224,7 +224,7 @@ pub struct Child { /// has been captured. You might find it helpful to do /// /// ```ignore (incomplete) - /// let stdin = child.stdin.take().expect("handle present"); + /// let stdin = child.stdin.take().unwrap(); /// ``` /// /// to avoid partially moving the `child` and thus blocking yourself from calling @@ -236,7 +236,7 @@ pub struct Child { /// has been captured. You might find it helpful to do /// /// ```ignore (incomplete) - /// let stdout = child.stdout.take().expect("handle present"); + /// let stdout = child.stdout.take().unwrap(); /// ``` /// /// to avoid partially moving the `child` and thus blocking yourself from calling @@ -248,7 +248,7 @@ pub struct Child { /// has been captured. You might find it helpful to do /// /// ```ignore (incomplete) - /// let stderr = child.stderr.take().expect("handle present"); + /// let stderr = child.stderr.take().unwrap(); /// ``` /// /// to avoid partially moving the `child` and thus blocking yourself from calling @@ -868,17 +868,13 @@ impl Command { /// /// # Examples /// - /// Prevent any inherited `GIT_DIR` variable from changing the target of the `git` command, - /// while allowing all other variables, like `GIT_AUTHOR_NAME`. - /// /// ```no_run /// use std::process::Command; /// - /// Command::new("git") - /// .arg("commit") - /// .env_remove("GIT_DIR") - /// .spawn()?; - /// # std::io::Result::Ok(()) + /// Command::new("ls") + /// .env_remove("PATH") + /// .spawn() + /// .expect("ls command failed to start"); /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_remove>(&mut self, key: K) -> &mut Command { @@ -900,17 +896,13 @@ impl Command { /// /// # Examples /// - /// The behavior of `sort` is affected by `LANG` and `LC_*` environment variables. - /// Clearing the environment makes `sort`'s behavior independent of the parent processes' language. - /// /// ```no_run /// use std::process::Command; /// - /// Command::new("sort") - /// .arg("file.txt") + /// Command::new("ls") /// .env_clear() - /// .spawn()?; - /// # std::io::Result::Ok(()) + /// .spawn() + /// .expect("ls command failed to start"); /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_clear(&mut self) -> &mut Command { @@ -1060,14 +1052,14 @@ impl Command { /// use std::io::{self, Write}; /// let output = Command::new("/bin/cat") /// .arg("file.txt") - /// .output()?; + /// .output() + /// .expect("failed to execute process"); /// /// println!("status: {}", output.status); - /// io::stdout().write_all(&output.stdout)?; - /// io::stderr().write_all(&output.stderr)?; + /// io::stdout().write_all(&output.stdout).unwrap(); + /// io::stderr().write_all(&output.stderr).unwrap(); /// /// assert!(output.status.success()); - /// # io::Result::Ok(()) /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn output(&mut self) -> io::Result { @@ -1291,13 +1283,13 @@ impl fmt::Debug for Output { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let stdout_utf8 = str::from_utf8(&self.stdout); let stdout_debug: &dyn fmt::Debug = match stdout_utf8 { - Ok(ref s) => s, + Ok(ref str) => str, Err(_) => &self.stdout, }; let stderr_utf8 = str::from_utf8(&self.stderr); let stderr_debug: &dyn fmt::Debug = match stderr_utf8 { - Ok(ref s) => s, + Ok(ref str) => str, Err(_) => &self.stderr, }; @@ -1399,11 +1391,11 @@ impl Stdio { /// let output = Command::new("rev") /// .stdin(Stdio::inherit()) /// .stdout(Stdio::piped()) - /// .output()?; + /// .output() + /// .expect("Failed to execute command"); /// /// print!("You piped in the reverse of: "); - /// io::stdout().write_all(&output.stdout)?; - /// # io::Result::Ok(()) + /// io::stdout().write_all(&output.stdout).unwrap(); /// ``` #[must_use] #[stable(feature = "process", since = "1.0.0")] @@ -1583,14 +1575,14 @@ impl From for Stdio { /// use std::process::Command; /// /// // With the `foo.txt` file containing "Hello, world!" - /// let file = File::open("foo.txt")?; + /// let file = File::open("foo.txt").unwrap(); /// /// let reverse = Command::new("rev") /// .stdin(file) // Implicit File conversion into a Stdio - /// .output()?; + /// .output() + /// .expect("failed reverse command"); /// /// assert_eq!(reverse.stdout, b"!dlrow ,olleH"); - /// # std::io::Result::Ok(()) /// ``` fn from(file: fs::File) -> Stdio { Stdio::from_inner(file.into_inner().into()) @@ -2187,7 +2179,7 @@ impl Child { /// ```no_run /// use std::process::Command; /// - /// let mut child = Command::new("ls").spawn()?; + /// let mut child = Command::new("ls").spawn().unwrap(); /// /// match child.try_wait() { /// Ok(Some(status)) => println!("exited with: {status}"), @@ -2198,7 +2190,6 @@ impl Child { /// } /// Err(e) => println!("error attempting to wait: {e}"), /// } - /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "process_try_wait", since = "1.18.0")] pub fn try_wait(&mut self) -> io::Result> { @@ -2318,10 +2309,14 @@ 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. 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. +/// 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. /// /// This is in contrast to the default behavior of [`panic!`] which unwinds /// the current thread's stack and calls all destructors. diff --git a/std/src/process/tests.rs b/std/src/process/tests.rs index 69273d863ebbd..fb0b495961c36 100644 --- a/std/src/process/tests.rs +++ b/std/src/process/tests.rs @@ -391,6 +391,143 @@ 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; + 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"); + + unsafe { + child_cmd + .raw_attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, parent.0.as_raw_handle() as isize); + } + + let child = ProcessDropGuard(child_cmd.spawn().unwrap()); + + let h_snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; + + let mut process_entry = PROCESSENTRY32W { + dwSize: mem::size_of::() 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) {} @@ -549,7 +686,7 @@ fn debug_print() { #[test] #[cfg(windows)] fn run_bat_script() { - let tempdir = crate::test_helpers::tmpdir(); + let tempdir = crate::sys_common::io::test::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); @@ -568,7 +705,7 @@ fn run_bat_script() { #[test] #[cfg(windows)] fn run_canonical_bat_script() { - let tempdir = crate::test_helpers::tmpdir(); + let tempdir = crate::sys_common::io::test::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); diff --git a/std/src/rt.rs b/std/src/rt.rs index 3a22a16cb165e..b2492238bd37b 100644 --- a/std/src/rt.rs +++ b/std/src/rt.rs @@ -23,7 +23,7 @@ pub use core::panicking::{panic_display, panic_fmt}; #[rustfmt::skip] use crate::any::Any; use crate::sync::Once; -use crate::thread::{self, main_thread}; +use crate::thread::{self, Thread}; use crate::{mem, panic, sys}; // Prints to the "panic output", depending on the platform this may be: @@ -32,14 +32,9 @@ use crate::{mem, panic, sys}; // - nothing (so this macro is a no-op) macro_rules! rtprintpanic { ($($t:tt)*) => { - #[cfg(not(feature = "panic_immediate_abort"))] if let Some(mut out) = crate::sys::stdio::panic_output() { let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*)); } - #[cfg(feature = "panic_immediate_abort")] - { - let _ = format_args!($($t)*); - } } } @@ -72,7 +67,7 @@ macro_rules! rtunwrap { }; } -fn handle_rt_panic(e: Box) -> T { +fn handle_rt_panic(e: Box) { mem::forget(e); rtabort!("initialization or cleanup bug"); } @@ -107,9 +102,24 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { sys::init(argc, argv, sigpipe) }; - // Remember the main thread ID to give it the correct name. - // SAFETY: this is the only time and place where we call this function. - unsafe { main_thread::set(thread::current_id()) }; + // Set up the current thread handle to give it the right name. + // + // When code running before main uses `ReentrantLock` (for example by + // using `println!`), the thread ID can become initialized before we + // create this handle. Since `set_current` fails when the ID of the + // handle does not match the current ID, we should attempt to use the + // current thread ID here instead of unconditionally creating a new + // one. Also see #130210. + let thread = unsafe { Thread::new_main(thread::current_id()) }; + if let Err(_thread) = thread::set_current(thread) { + // `thread::current` will create a new handle if none has been set yet. + // Thus, if someone uses it before main, this call will fail. That's a + // bad idea though, as we then cannot set the main thread name here. + // + // FIXME: detect the main thread in `thread::current` and use the + // correct name there. + rtabort!("code running before main must not use thread::current"); + } } /// Clean up the thread-local runtime state. This *should* be run after all other @@ -147,7 +157,7 @@ fn lang_start_internal( argc: isize, argv: *const *const u8, sigpipe: u8, -) -> isize { +) -> Result { // Guard against the code called by this function from unwinding outside of the Rust-controlled // code, which is UB. This is a requirement imposed by a combination of how the // `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking @@ -158,33 +168,19 @@ fn lang_start_internal( // panic is a std implementation bug. A quite likely one too, as there isn't any way to // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. - // - // We use `catch_unwind` with `handle_rt_panic` instead of `abort_unwind` to make the error in - // case of a panic a bit nicer. - panic::catch_unwind(move || { - // SAFETY: Only called once during runtime initialization. - unsafe { init(argc, argv, sigpipe) }; - - let ret_code = panic::catch_unwind(main).unwrap_or_else(move |payload| { - // Carefully dispose of the panic payload. - let payload = panic::AssertUnwindSafe(payload); - panic::catch_unwind(move || drop({ payload }.0)).unwrap_or_else(move |e| { - mem::forget(e); // do *not* drop the 2nd payload - rtabort!("drop of the panic payload panicked"); - }); - // Return error code for panicking programs. - 101 + // SAFETY: Only called once during runtime initialization. + panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }) + .unwrap_or_else(handle_rt_panic); + let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize) + .map_err(move |e| { + mem::forget(e); + rtabort!("drop of the panic payload panicked"); }); - let ret_code = ret_code as isize; - - cleanup(); - // Guard against multiple threads calling `libc::exit` concurrently. - // See the documentation for `unique_thread_exit` for more information. - crate::sys::exit_guard::unique_thread_exit(); - - ret_code - }) - .unwrap_or_else(handle_rt_panic) + panic::catch_unwind(cleanup).unwrap_or_else(handle_rt_panic); + // Guard against multiple threads calling `libc::exit` concurrently. + // See the documentation for `unique_thread_exit` for more information. + panic::catch_unwind(crate::sys::exit_guard::unique_thread_exit).unwrap_or_else(handle_rt_panic); + ret_code } #[cfg(not(any(test, doctest)))] @@ -195,10 +191,11 @@ fn lang_start( argv: *const *const u8, sigpipe: u8, ) -> isize { - lang_start_internal( + let Ok(v) = lang_start_internal( &move || crate::sys::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), argc, argv, sigpipe, - ) + ); + v } diff --git a/std/src/sync/barrier.rs b/std/src/sync/barrier.rs index 067ff66d9af73..82cc13a74b7f1 100644 --- a/std/src/sync/barrier.rs +++ b/std/src/sync/barrier.rs @@ -1,5 +1,7 @@ +#[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}; /// A barrier enables multiple threads to synchronize the beginning @@ -8,22 +10,26 @@ use crate::sync::{Condvar, Mutex}; /// # Examples /// /// ``` -/// use std::sync::Barrier; +/// use std::sync::{Arc, Barrier}; /// use std::thread; /// /// let n = 10; -/// let barrier = Barrier::new(n); -/// thread::scope(|s| { -/// for _ in 0..n { -/// // The same messages will be printed together. -/// // You will NOT see any interleaving. -/// s.spawn(|| { -/// println!("before wait"); -/// barrier.wait(); -/// println!("after wait"); -/// }); -/// } -/// }); +/// let mut handles = Vec::with_capacity(n); +/// let barrier = Arc::new(Barrier::new(n)); +/// for _ in 0..n { +/// let c = Arc::clone(&barrier); +/// // The same messages will be printed together. +/// // You will NOT see any interleaving. +/// handles.push(thread::spawn(move || { +/// println!("before wait"); +/// c.wait(); +/// println!("after wait"); +/// })); +/// } +/// // Wait for other threads to finish. +/// for handle in handles { +/// handle.join().unwrap(); +/// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Barrier { @@ -99,22 +105,26 @@ impl Barrier { /// # Examples /// /// ``` - /// use std::sync::Barrier; + /// use std::sync::{Arc, Barrier}; /// use std::thread; /// /// let n = 10; - /// let barrier = Barrier::new(n); - /// thread::scope(|s| { - /// for _ in 0..n { - /// // The same messages will be printed together. - /// // You will NOT see any interleaving. - /// s.spawn(|| { - /// println!("before wait"); - /// barrier.wait(); - /// println!("after wait"); - /// }); - /// } - /// }); + /// let mut handles = Vec::with_capacity(n); + /// let barrier = Arc::new(Barrier::new(n)); + /// for _ in 0..n { + /// let c = Arc::clone(&barrier); + /// // The same messages will be printed together. + /// // You will NOT see any interleaving. + /// handles.push(thread::spawn(move || { + /// println!("before wait"); + /// c.wait(); + /// println!("after wait"); + /// })); + /// } + /// // Wait for other threads to finish. + /// for handle in handles { + /// handle.join().unwrap(); + /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn wait(&self) -> BarrierWaitResult { diff --git a/std/tests/sync/barrier.rs b/std/src/sync/barrier/tests.rs similarity index 89% rename from std/tests/sync/barrier.rs rename to std/src/sync/barrier/tests.rs index 8aefff9d5071c..0fbcd9988127b 100644 --- a/std/tests/sync/barrier.rs +++ b/std/src/sync/barrier/tests.rs @@ -1,6 +1,6 @@ -use std::sync::mpsc::{TryRecvError, channel}; -use std::sync::{Arc, Barrier}; -use std::thread; +use crate::sync::mpsc::{TryRecvError, channel}; +use crate::sync::{Arc, Barrier}; +use crate::thread; #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads diff --git a/std/src/sync/poison/condvar.rs b/std/src/sync/condvar.rs similarity index 99% rename from std/src/sync/poison/condvar.rs rename to std/src/sync/condvar.rs index 7f0f3f652bcb7..44ffcb528d937 100644 --- a/std/src/sync/poison/condvar.rs +++ b/std/src/sync/condvar.rs @@ -1,5 +1,8 @@ +#[cfg(test)] +mod tests; + use crate::fmt; -use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex}; +use crate::sync::{LockResult, MutexGuard, PoisonError, mutex, poison}; use crate::sys::sync as sys; use crate::time::{Duration, Instant}; @@ -13,8 +16,6 @@ use crate::time::{Duration, Instant}; #[stable(feature = "wait_timeout", since = "1.5.0")] pub struct WaitTimeoutResult(bool); -// FIXME(sync_nonpoison): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. -// Should we take advantage of this fact? impl WaitTimeoutResult { /// Returns `true` if the wait was known to have timed out. /// diff --git a/std/tests/sync/condvar.rs b/std/src/sync/condvar/tests.rs similarity index 97% rename from std/tests/sync/condvar.rs rename to std/src/sync/condvar/tests.rs index 834de6bb1c295..f9e9066bc92a2 100644 --- a/std/tests/sync/condvar.rs +++ b/std/src/sync/condvar/tests.rs @@ -1,8 +1,8 @@ -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::mpsc::channel; -use std::sync::{Arc, Condvar, Mutex}; -use std::thread; -use std::time::Duration; +use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::mpsc::channel; +use crate::sync::{Arc, Condvar, Mutex}; +use crate::thread; +use crate::time::Duration; #[test] fn smoke() { diff --git a/std/src/sync/lazy_lock.rs b/std/src/sync/lazy_lock.rs index 78cf8841efefb..40510f5613450 100644 --- a/std/src/sync/lazy_lock.rs +++ b/std/src/sync/lazy_lock.rs @@ -1,4 +1,4 @@ -use super::poison::once::ExclusiveState; +use super::once::ExclusiveState; use crate::cell::UnsafeCell; use crate::mem::ManuallyDrop; use crate::ops::Deref; @@ -31,7 +31,7 @@ union Data { /// ``` /// use std::sync::LazyLock; /// -/// // Note: static items do not call [`Drop`] on program termination, so this won't be deallocated. +/// // n.b. static items do not call [`Drop`] on program termination, so this won't be deallocated. /// // this is fine, as the OS can deallocate the terminated program faster than we can free memory /// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional. /// static DEEP_THOUGHT: LazyLock = LazyLock::new(|| { @@ -63,7 +63,6 @@ union Data { /// ``` #[stable(feature = "lazy_cell", since = "1.80.0")] pub struct LazyLock T> { - // FIXME(nonpoison_once): if possible, switch to nonpoison version once it is available once: Once, data: UnsafeCell>, } @@ -350,3 +349,6 @@ unsafe impl Sync for LazyLock {} impl RefUnwindSafe for LazyLock {} #[stable(feature = "lazy_cell", since = "1.80.0")] impl UnwindSafe for LazyLock {} + +#[cfg(test)] +mod tests; diff --git a/std/tests/sync/lazy_lock.rs b/std/src/sync/lazy_lock/tests.rs similarity index 93% rename from std/tests/sync/lazy_lock.rs rename to std/src/sync/lazy_lock/tests.rs index 6c14b79f2ce7c..7d7dde5434990 100644 --- a/std/tests/sync/lazy_lock.rs +++ b/std/src/sync/lazy_lock/tests.rs @@ -1,8 +1,8 @@ -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}; +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}; fn spawn_and_wait(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::::new(|| panic!()); - panic::catch_unwind(panic::AssertUnwindSafe(|| { + crate::panic::catch_unwind(crate::panic::AssertUnwindSafe(|| { let _ = LazyLock::force_mut(&mut lazy); })) .unwrap_err(); diff --git a/std/src/sync/mod.rs b/std/src/sync/mod.rs index 5b50a3c6ccf90..0fb77331293fe 100644 --- a/std/src/sync/mod.rs +++ b/std/src/sync/mod.rs @@ -167,10 +167,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -// No formatting: this file is just re-exports, and their order is worth preserving. -#![cfg_attr(rustfmt, rustfmt::skip)] - -// These come from `core` & `alloc` and only in one flavor: no poisoning. #[unstable(feature = "exclusive_wrapper", issue = "98407")] pub use core::sync::Exclusive; #[stable(feature = "rust1", since = "1.0.0")] @@ -179,54 +175,40 @@ pub use core::sync::atomic; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::sync::{Arc, Weak}; -// FIXME(sync_nonpoison,sync_poison_mod): remove all `#[doc(inline)]` once the modules are stabilized. - -// These exist only in one flavor: no poisoning. #[stable(feature = "rust1", since = "1.0.0")] pub use self::barrier::{Barrier, BarrierWaitResult}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::condvar::{Condvar, WaitTimeoutResult}; #[stable(feature = "lazy_cell", since = "1.80.0")] pub use self::lazy_lock::LazyLock; +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +pub use self::mutex::MappedMutexGuard; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::mutex::{Mutex, MutexGuard}; +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +pub use self::once::{ONCE_INIT, Once, OnceState}; #[stable(feature = "once_cell", since = "1.70.0")] pub use self::once_lock::OnceLock; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult}; #[unstable(feature = "reentrant_lock", issue = "121440")] pub use self::reentrant_lock::{ReentrantLock, ReentrantLockGuard}; - -// These make sense and exist only with poisoning. -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use self::poison::{LockResult, PoisonError}; - -// These (should) exist in both flavors: with and without poisoning. -// FIXME(sync_nonpoison): implement nonpoison versions: -// * Mutex (nonpoison_mutex) -// * Condvar (nonpoison_condvar) -// * Once (nonpoison_once) -// * RwLock (nonpoison_rwlock) -// The historical default is the version with poisoning. -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -pub use self::poison::{ - Mutex, MutexGuard, TryLockError, TryLockResult, - Condvar, WaitTimeoutResult, - Once, OnceState, - RwLock, RwLockReadGuard, RwLockWriteGuard, -}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(inline)] -#[expect(deprecated)] -pub use self::poison::ONCE_INIT; #[unstable(feature = "mapped_lock_guards", issue = "117108")] -#[doc(inline)] -pub use self::poison::{MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWriteGuard}; +pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; #[unstable(feature = "mpmc_channel", issue = "126840")] pub mod mpmc; pub mod mpsc; -#[unstable(feature = "sync_poison_mod", issue = "134646")] -pub mod poison; - mod barrier; +mod condvar; mod lazy_lock; +mod mutex; +pub(crate) mod once; mod once_lock; +mod poison; mod reentrant_lock; +mod rwlock; diff --git a/std/src/sync/mpmc/mod.rs b/std/src/sync/mpmc/mod.rs index 00966ee3ecffd..0cf4902d6d59b 100644 --- a/std/src/sync/mpmc/mod.rs +++ b/std/src/sync/mpmc/mod.rs @@ -18,7 +18,7 @@ //! infinite buffer. //! //! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(Sender, Receiver)` tuple where the storage for pending +//! return a `(SyncSender, 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,17 +360,9 @@ impl Sender { /// 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`]. 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. + /// hang up immediately after this function returns [`Ok`]. /// - /// 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. + /// This method will never block the current thread. /// /// # Examples /// @@ -658,7 +650,7 @@ impl fmt::Debug for Sender { } /// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// Different threads can share this [`Receiver`] by cloning it. +/// Different threads can share this [`Sender`] by cloning it. /// /// Messages sent to the channel can be retrieved using [`recv`]. /// diff --git a/std/tests/sync/mpmc.rs b/std/src/sync/mpmc/tests.rs similarity index 99% rename from std/tests/sync/mpmc.rs rename to std/src/sync/mpmc/tests.rs index 81b92297f76a3..ab14050df6c98 100644 --- a/std/tests/sync/mpmc.rs +++ b/std/src/sync/mpmc/tests.rs @@ -1,6 +1,5 @@ -use std::sync::mpmc::*; -use std::time::{Duration, Instant}; -use std::{env, thread}; +use super::*; +use crate::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/std/src/sync/mpsc.rs b/std/src/sync/mpsc/mod.rs similarity index 99% rename from std/src/sync/mpsc.rs rename to std/src/sync/mpsc/mod.rs index f942937c14d11..c86b546e01169 100644 --- a/std/src/sync/mpsc.rs +++ b/std/src/sync/mpsc/mod.rs @@ -137,6 +137,12 @@ #![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, @@ -731,10 +737,9 @@ impl SyncSender { // 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 only used for tests. - #[unstable(issue = "none", feature = "std_internals")] - #[doc(hidden)] - pub fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError> { + // This method is currently private and only used for tests. + #[allow(unused)] + fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError> { self.inner.send_timeout(t, timeout) } } diff --git a/std/tests/sync/mpsc_sync.rs b/std/src/sync/mpsc/sync_tests.rs similarity index 99% rename from std/tests/sync/mpsc_sync.rs rename to std/src/sync/mpsc/sync_tests.rs index a7f326d201b00..49b65c8efe692 100644 --- a/std/tests/sync/mpsc_sync.rs +++ b/std/src/sync/mpsc/sync_tests.rs @@ -1,8 +1,7 @@ -use std::rc::Rc; -use std::sync::mpmc::SendTimeoutError; -use std::sync::mpsc::*; -use std::time::Duration; -use std::{env, thread}; +use super::*; +use crate::rc::Rc; +use crate::sync::mpmc::SendTimeoutError; +use crate::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/std/tests/sync/mpsc.rs b/std/src/sync/mpsc/tests.rs similarity index 99% rename from std/tests/sync/mpsc.rs rename to std/src/sync/mpsc/tests.rs index 1d8edfde44bed..13892fa0d18e4 100644 --- a/std/tests/sync/mpsc.rs +++ b/std/src/sync/mpsc/tests.rs @@ -1,6 +1,5 @@ -use std::sync::mpsc::*; -use std::time::{Duration, Instant}; -use std::{env, thread}; +use super::*; +use crate::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/std/src/sync/poison/mutex.rs b/std/src/sync/mutex.rs similarity index 84% rename from std/src/sync/poison/mutex.rs rename to std/src/sync/mutex.rs index 9362c764173a8..fe2aca031a248 100644 --- a/std/src/sync/poison/mutex.rs +++ b/std/src/sync/mutex.rs @@ -1,10 +1,13 @@ +#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] +mod tests; + use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; -use crate::mem::{self, ManuallyDrop}; +use crate::mem::ManuallyDrop; use crate::ops::{Deref, DerefMut}; use crate::ptr::NonNull; -use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison}; +use crate::sync::{LockResult, TryLockError, TryLockResult, poison}; use crate::sys::sync as sys; /// A mutual exclusion primitive useful for protecting shared data @@ -178,29 +181,10 @@ pub struct Mutex { data: UnsafeCell, } -/// `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 +// these are the only places where `T: Send` matters; all other +// functionality works fine on a single thread. #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for Mutex {} - -/// `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`] 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>`, 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 Sync for Mutex {} @@ -227,17 +211,8 @@ 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 !Send for MutexGuard<'_, T> {} - -/// `T` must be `Sync` for a [`MutexGuard`] to be `Sync` -/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). #[stable(feature = "mutexguard", since = "1.19.0")] unsafe impl Sync for MutexGuard<'_, T> {} @@ -298,100 +273,6 @@ impl Mutex { pub const fn new(t: T) -> Mutex { Mutex { inner: sys::Mutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } } - - /// Returns the contained value by cloning it. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error instead. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(7); - /// - /// assert_eq!(mutex.get_cloned().unwrap(), 7); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - pub fn get_cloned(&self) -> Result> - where - T: Clone, - { - match self.lock() { - Ok(guard) => Ok((*guard).clone()), - Err(_) => Err(PoisonError::new(())), - } - } - - /// Sets the contained value. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing the provided `value` instead. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(7); - /// - /// assert_eq!(mutex.get_cloned().unwrap(), 7); - /// mutex.set(11).unwrap(); - /// assert_eq!(mutex.get_cloned().unwrap(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - pub fn set(&self, value: T) -> Result<(), PoisonError> { - if mem::needs_drop::() { - // If the contained value has non-trivial destructor, we - // call that destructor after the lock being released. - self.replace(value).map(drop) - } else { - match self.lock() { - Ok(mut guard) => { - *guard = value; - - Ok(()) - } - Err(_) => Err(PoisonError::new(value)), - } - } - } - - /// Replaces the contained value with `value`, and returns the old contained value. - /// - /// # Errors - /// - /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing the provided `value` instead. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::Mutex; - /// - /// let mut mutex = Mutex::new(7); - /// - /// assert_eq!(mutex.replace(11).unwrap(), 7); - /// assert_eq!(mutex.get_cloned().unwrap(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - pub fn replace(&self, value: T) -> LockResult { - match self.lock() { - Ok(mut guard) => Ok(mem::replace(&mut *guard, value)), - Err(_) => Err(PoisonError::new(value)), - } - } } impl Mutex { @@ -409,8 +290,7 @@ impl Mutex { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error once the mutex is acquired. The acquired - /// mutex guard will be contained in the returned error. + /// this call will return an error once the mutex is acquired. /// /// # Panics /// @@ -451,8 +331,7 @@ impl Mutex { /// /// If another user of this mutex panicked while holding the mutex, then /// this call will return the [`Poisoned`] error if the mutex would - /// otherwise be acquired. An acquired lock guard will be contained - /// in the returned error. + /// otherwise be acquired. /// /// If the mutex could not be acquired because it is already locked, then /// this call will return the [`WouldBlock`] error. @@ -559,8 +438,7 @@ impl Mutex { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing the underlying data - /// instead. + /// this call will return an error instead. /// /// # Examples /// @@ -587,8 +465,7 @@ impl Mutex { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing a mutable reference to the - /// underlying data instead. + /// this call will return an error instead. /// /// # Examples /// diff --git a/std/tests/sync/mutex.rs b/std/src/sync/mutex/tests.rs similarity index 68% rename from std/tests/sync/mutex.rs rename to std/src/sync/mutex/tests.rs index 74c627201073e..19ec096c59334 100644 --- a/std/tests/sync/mutex.rs +++ b/std/src/sync/mutex/tests.rs @@ -1,34 +1,13 @@ -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}; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::mpsc::channel; +use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; +use crate::thread; struct Packet(Arc<(Mutex, Condvar)>); #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); -#[derive(Eq, PartialEq, Debug)] -struct NonCopyNeedsDrop(i32); - -impl Drop for NonCopyNeedsDrop { - fn drop(&mut self) { - hint::black_box(()); - } -} - -#[test] -fn test_needs_drop() { - assert!(!mem::needs_drop::()); - assert!(mem::needs_drop::()); -} - -#[derive(Clone, Eq, PartialEq, Debug)] -struct Cloneable(i32); - #[test] fn smoke() { let m = Mutex::new(()); @@ -78,21 +57,6 @@ fn try_lock() { *m.try_lock().unwrap() = (); } -fn new_poisoned_mutex(value: T) -> Mutex { - let mutex = Mutex::new(value); - - let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { - let _guard = mutex.lock().unwrap(); - - panic!("test panic to poison mutex"); - })); - - assert!(catch_unwind_result.is_err()); - assert!(mutex.is_poisoned()); - - mutex -} - #[test] fn test_into_inner() { let m = Mutex::new(NonCopy(10)); @@ -119,31 +83,21 @@ fn test_into_inner_drop() { #[test] fn test_into_inner_poison() { - let m = new_poisoned_mutex(NonCopy(10)); + let m = Arc::new(Mutex::new(NonCopy(10))); + let m2 = m.clone(); + let _ = thread::spawn(move || { + let _lock = m2.lock().unwrap(); + panic!("test panic in inner thread to poison mutex"); + }) + .join(); - match m.into_inner() { + assert!(m.is_poisoned()); + match Arc::try_unwrap(m).unwrap().into_inner() { Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"), } } -#[test] -fn test_get_cloned() { - let m = Mutex::new(Cloneable(10)); - - assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); -} - -#[test] -fn test_get_cloned_poison() { - let m = new_poisoned_mutex(Cloneable(10)); - - match m.get_cloned() { - Err(e) => assert_eq!(e.into_inner(), ()), - Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"), - } -} - #[test] fn test_get_mut() { let mut m = Mutex::new(NonCopy(10)); @@ -153,90 +107,21 @@ fn test_get_mut() { #[test] fn test_get_mut_poison() { - let mut m = new_poisoned_mutex(NonCopy(10)); + let m = Arc::new(Mutex::new(NonCopy(10))); + let m2 = m.clone(); + let _ = thread::spawn(move || { + let _lock = m2.lock().unwrap(); + panic!("test panic in inner thread to poison mutex"); + }) + .join(); - match m.get_mut() { + assert!(m.is_poisoned()); + match Arc::try_unwrap(m).unwrap().get_mut() { Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"), } } -#[test] -fn test_set() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = Mutex::new(init()); - - assert_eq!(*m.lock().unwrap(), init()); - m.set(value()).unwrap(); - assert_eq!(*m.lock().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_set_poison() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = new_poisoned_mutex(init()); - - match m.set(value()) { - Err(e) => { - assert_eq!(e.into_inner(), value()); - assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); - } - Ok(x) => panic!("set of poisoned Mutex is Ok: {x:?}"), - } - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_replace() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = Mutex::new(init()); - - assert_eq!(*m.lock().unwrap(), init()); - assert_eq!(m.replace(value()).unwrap(), init()); - assert_eq!(*m.lock().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_replace_poison() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = new_poisoned_mutex(init()); - - match m.replace(value()) { - Err(e) => { - assert_eq!(e.into_inner(), value()); - assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); - } - Ok(x) => panic!("replace of poisoned Mutex is Ok: {x:?}"), - } - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - #[test] fn test_mutex_arc_condvar() { let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); @@ -384,7 +269,7 @@ fn test_mapping_mapped_guard() { fn panic_while_mapping_unlocked_poison() { let lock = Mutex::new(()); - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let _guard = MutexGuard::map::<(), _>(guard, |_| panic!()); }); @@ -397,7 +282,7 @@ fn panic_while_mapping_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!()); }); @@ -410,7 +295,7 @@ fn panic_while_mapping_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let guard = MutexGuard::map::<(), _>(guard, |val| val); let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!()); @@ -424,7 +309,7 @@ fn panic_while_mapping_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let guard = MutexGuard::map::<(), _>(guard, |val| val); let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!()); diff --git a/std/src/sync/poison/once.rs b/std/src/sync/once.rs similarity index 98% rename from std/src/sync/poison/once.rs rename to std/src/sync/once.rs index d2938b7a0c12e..27db4b634fb28 100644 --- a/std/src/sync/poison/once.rs +++ b/std/src/sync/once.rs @@ -3,6 +3,9 @@ //! 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; @@ -266,6 +269,8 @@ impl Once { /// # Example /// /// ```rust + /// #![feature(once_wait)] + /// /// use std::sync::Once; /// use std::thread; /// @@ -284,7 +289,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. - #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "once_wait", issue = "127527")] pub fn wait(&self) { if !self.inner.is_completed() { self.inner.wait(false); @@ -293,7 +298,7 @@ impl Once { /// Blocks the current thread until initialization has completed, ignoring /// poisoning. - #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "once_wait", issue = "127527")] pub fn wait_force(&self) { if !self.inner.is_completed() { self.inner.wait(true); diff --git a/std/tests/sync/once.rs b/std/src/sync/once/tests.rs similarity index 94% rename from std/tests/sync/once.rs rename to std/src/sync/once/tests.rs index a3ffc73fe06b9..ce96468aeb6e1 100644 --- a/std/tests/sync/once.rs +++ b/std/src/sync/once/tests.rs @@ -1,9 +1,9 @@ -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}; +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}; #[test] fn smoke_once() { diff --git a/std/src/sync/once_lock.rs b/std/src/sync/once_lock.rs index 21e6b65a744f8..0ae3cf4df3614 100644 --- a/std/src/sync/once_lock.rs +++ b/std/src/sync/once_lock.rs @@ -13,9 +13,6 @@ 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`]: crate::sync::LazyLock /// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new @@ -104,7 +101,6 @@ use crate::sync::Once; /// ``` #[stable(feature = "once_cell", since = "1.70.0")] pub struct OnceLock { - // FIXME(nonpoison_once): switch to nonpoison version once it is available once: Once, // Whether or not the value is initialized is tracked by `once.is_completed()`. value: UnsafeCell>, @@ -129,7 +125,7 @@ pub struct OnceLock { } impl OnceLock { - /// Creates a new uninitialized cell. + /// Creates a new empty cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -144,8 +140,8 @@ impl OnceLock { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is uninitialized, or being initialized. - /// This method never blocks. + /// Returns `None` if the cell is empty, or being initialized. This + /// method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -159,8 +155,7 @@ impl OnceLock { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is uninitialized, or being initialized. - /// This method never blocks. + /// Returns `None` if the cell is empty. This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { @@ -178,6 +173,8 @@ impl OnceLock { /// /// Waiting for a computation on another thread to finish: /// ```rust + /// #![feature(once_wait)] + /// /// use std::thread; /// use std::sync::OnceLock; /// @@ -191,20 +188,19 @@ impl OnceLock { /// }) /// ``` #[inline] - #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] + #[unstable(feature = "once_wait", issue = "127527")] pub fn wait(&self) -> &T { self.once.wait_force(); unsafe { self.get_unchecked() } } - /// Initializes the contents of the cell to `value`. + /// Sets the contents of this 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 was uninitialized and - /// `Err(value)` if the cell was already initialized. + /// Returns `Ok(())` if the cell's value was set by this call. /// /// # Examples /// @@ -233,15 +229,13 @@ impl OnceLock { } } - /// Initializes the contents of the cell to `value` if the cell was uninitialized, - /// then returns a reference to it. + /// Sets the contents of this cell to `value` if the cell was empty, 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 `try_insert` returns, though not necessarily the - /// one provided. + /// guaranteed to contain a value when set returns, though not necessarily the one provided. /// - /// Returns `Ok(&value)` if the cell was uninitialized and - /// `Err((¤t_value, value))` if it was already initialized. + /// Returns `Ok(&value)` if the cell was empty and `Err(¤t_value, value)` if it was full. /// /// # Examples /// @@ -274,8 +268,8 @@ impl OnceLock { } } - /// Gets the contents of the cell, initializing it to `f()` if the cell - /// was uninitialized. + /// Gets the contents of the cell, initializing it with `f` if the cell + /// was empty. /// /// Many threads may call `get_or_init` concurrently with different /// initializing functions, but it is guaranteed that only one function @@ -283,7 +277,7 @@ impl OnceLock { /// /// # 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 @@ -313,13 +307,13 @@ impl OnceLock { } /// Gets the mutable reference of the contents of the cell, initializing - /// it to `f()` if the cell was uninitialized. + /// it with `f` if the cell was empty. /// /// 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 @@ -350,13 +344,13 @@ impl OnceLock { } } - /// 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. + /// 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. /// /// # 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`. @@ -402,14 +396,14 @@ impl OnceLock { } /// Gets the mutable reference of 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. + /// it with `f` if the cell was empty. If the cell was empty 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 @@ -421,7 +415,7 @@ impl OnceLock { /// /// let mut cell: OnceLock = OnceLock::new(); /// - /// // Failed attempts to initialize the cell do not change its contents + /// // Failed initializers do not change the value /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// @@ -445,7 +439,7 @@ impl OnceLock { } /// Consumes the `OnceLock`, returning the wrapped value. Returns - /// `None` if the cell was uninitialized. + /// `None` if the cell was empty. /// /// # Examples /// @@ -467,7 +461,7 @@ impl OnceLock { /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceLock` was uninitialized. + /// Has no effect and returns `None` if the `OnceLock` hasn't been initialized. /// /// Safety is guaranteed by requiring a mutable reference. /// @@ -533,7 +527,7 @@ impl OnceLock { /// # Safety /// - /// The cell must be initialized + /// The value must be initialized #[inline] unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); @@ -542,7 +536,7 @@ impl OnceLock { /// # Safety /// - /// The cell must be initialized + /// The value must be initialized #[inline] unsafe fn get_unchecked_mut(&mut self) -> &mut T { debug_assert!(self.is_initialized()); @@ -567,7 +561,7 @@ impl UnwindSafe for OnceLock {} #[stable(feature = "once_cell", since = "1.70.0")] impl Default for OnceLock { - /// Creates a new uninitialized cell. + /// Creates a new empty cell. /// /// # Example /// @@ -681,3 +675,6 @@ unsafe impl<#[may_dangle] T> Drop for OnceLock { } } } + +#[cfg(test)] +mod tests; diff --git a/std/tests/sync/once_lock.rs b/std/src/sync/once_lock/tests.rs similarity index 91% rename from std/tests/sync/once_lock.rs rename to std/src/sync/once_lock/tests.rs index ac9aaa8892eff..5113d436c3c99 100644 --- a/std/tests/sync/once_lock.rs +++ b/std/src/sync/once_lock/tests.rs @@ -1,8 +1,8 @@ -use std::sync::OnceLock; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering::SeqCst; -use std::sync::mpsc::channel; -use std::{panic, thread}; +use crate::sync::OnceLock; +use crate::sync::atomic::AtomicUsize; +use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::mpsc::channel; +use crate::{panic, thread}; fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() @@ -33,6 +33,15 @@ fn sync_once_cell_get_mut() { assert_eq!(c.get_mut(), Some(&mut 92)); } +#[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() { @@ -79,6 +88,7 @@ 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(())); @@ -164,7 +174,7 @@ fn sync_once_cell_does_not_leak_partially_constructed_boxes() { break; } #[cfg(target_env = "sgx")] - std::thread::yield_now(); + crate::thread::yield_now(); } }); } diff --git a/std/src/sync/poison.rs b/std/src/sync/poison.rs index 1b8809734b8a8..da66a088e51b1 100644 --- a/std/src/sync/poison.rs +++ b/std/src/sync/poison.rs @@ -1,78 +1,3 @@ -//! Synchronization objects that employ poisoning. -//! -//! # Poisoning -//! -//! All synchronization objects in this module implement a strategy called "poisoning" -//! where if a thread panics while holding the exclusive access granted by the primitive, -//! the state of the primitive is set to "poisoned". -//! This information is then propagated to all other threads -//! to signify that the data protected by this primitive is likely tainted -//! (some invariant is not being upheld). -//! -//! The specifics of how this "poisoned" state affects other threads -//! depend on the primitive. See [#Overview] bellow. -//! -//! For the alternative implementations that do not employ poisoning, -//! see `std::sys::nonpoisoning`. -//! -//! # Overview -//! -//! Below is a list of synchronization objects provided by this module -//! with a high-level overview for each object and a description -//! of how it employs "poisoning". -//! -//! - [`Condvar`]: Condition Variable, providing the ability to block -//! a thread while waiting for an event to occur. -//! -//! Condition variables are typically associated with -//! a boolean predicate (a condition) and a mutex. -//! This implementation is associated with [`poison::Mutex`](Mutex), -//! which employs poisoning. -//! For this reason, [`Condvar::wait()`] will return a [`LockResult`], -//! just like [`poison::Mutex::lock()`](Mutex::lock) does. -//! -//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at -//! most one thread at a time is able to access some data. -//! -//! [`Mutex::lock()`] returns a [`LockResult`], -//! providing a way to deal with the poisoned state. -//! See [`Mutex`'s documentation](Mutex#poisoning) for more. -//! -//! - [`Once`]: A thread-safe way to run a piece of code only once. -//! Mostly useful for implementing one-time global initialization. -//! -//! [`Once`] is poisoned if the piece of code passed to -//! [`Once::call_once()`] or [`Once::call_once_force()`] panics. -//! When in poisoned state, subsequent calls to [`Once::call_once()`] will panic too. -//! [`Once::call_once_force()`] can be used to clear the poisoned state. -//! -//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows -//! multiple readers at the same time, while allowing only one -//! writer at a time. In some cases, this can be more efficient than -//! a mutex. -//! -//! This implementation, like [`Mutex`], will become poisoned on a panic. -//! Note, however, that an `RwLock` may only be poisoned if a panic occurs -//! while it is locked exclusively (write mode). If a panic occurs in any reader, -//! then the lock will not be poisoned. - -// FIXME(sync_nonpoison) add links to sync::nonpoison to the doc comment above. - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::condvar::{Condvar, WaitTimeoutResult}; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::mutex::MappedMutexGuard; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::mutex::{Mutex, MutexGuard}; -#[stable(feature = "rust1", since = "1.0.0")] -#[expect(deprecated)] -pub use self::once::ONCE_INIT; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::once::{Once, OnceState}; -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::error::Error; use crate::fmt; #[cfg(panic = "unwind")] @@ -80,13 +5,7 @@ use crate::sync::atomic::{AtomicBool, Ordering}; #[cfg(panic = "unwind")] use crate::thread; -mod condvar; -#[stable(feature = "rust1", since = "1.0.0")] -mod mutex; -pub(crate) mod once; -mod rwlock; - -pub(crate) struct Flag { +pub struct Flag { #[cfg(panic = "unwind")] failed: AtomicBool, } @@ -159,7 +78,7 @@ impl Flag { } #[derive(Clone)] -pub(crate) struct Guard { +pub struct Guard { #[cfg(panic = "unwind")] panicking: bool, } @@ -168,8 +87,8 @@ pub(crate) struct Guard { /// /// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock /// is held. The precise semantics for when a lock is poisoned is documented on -/// each lock. For a lock in the poisoned state, unless the state is cleared manually, -/// all future acquisitions will return this error. +/// each lock, but once a lock is poisoned then all future acquisitions will +/// return this error. /// /// # Examples /// @@ -199,7 +118,7 @@ pub(crate) struct Guard { /// [`RwLock`]: crate::sync::RwLock #[stable(feature = "rust1", since = "1.0.0")] pub struct PoisonError { - data: T, + guard: T, #[cfg(not(panic = "unwind"))] _never: !, } @@ -228,15 +147,14 @@ pub enum TryLockError { /// A type alias for the result of a lock method which can be poisoned. /// /// The [`Ok`] variant of this result indicates that the primitive was not -/// poisoned, and the operation result is contained within. The [`Err`] variant indicates +/// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates /// that the primitive was poisoned. Note that the [`Err`] variant *also* carries -/// an associated value assigned by the lock method, and it can be acquired through the -/// [`into_inner`] method. The semantics of the associated value depends on the corresponding -/// lock method. +/// the associated guard, and it can be acquired through the [`into_inner`] +/// method. /// /// [`into_inner`]: PoisonError::into_inner #[stable(feature = "rust1", since = "1.0.0")] -pub type LockResult = Result>; +pub type LockResult = Result>; /// A type alias for the result of a nonblocking locking method. /// @@ -277,8 +195,8 @@ impl PoisonError { /// This method may panic if std was built with `panic="abort"`. #[cfg(panic = "unwind")] #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn new(data: T) -> PoisonError { - PoisonError { data } + pub fn new(guard: T) -> PoisonError { + PoisonError { guard } } /// Creates a `PoisonError`. @@ -290,12 +208,12 @@ impl PoisonError { #[cfg(not(panic = "unwind"))] #[stable(feature = "sync_poison", since = "1.2.0")] #[track_caller] - pub fn new(_data: T) -> PoisonError { + pub fn new(_guard: T) -> PoisonError { panic!("PoisonError created in a libstd built with panic=\"abort\"") } /// Consumes this error indicating that a lock is poisoned, returning the - /// associated data. + /// underlying guard to allow access regardless. /// /// # Examples /// @@ -320,21 +238,21 @@ impl PoisonError { /// ``` #[stable(feature = "sync_poison", since = "1.2.0")] pub fn into_inner(self) -> T { - self.data + self.guard } /// Reaches into this error indicating that a lock is poisoned, returning a - /// reference to the associated data. + /// reference to the underlying guard to allow access regardless. #[stable(feature = "sync_poison", since = "1.2.0")] pub fn get_ref(&self) -> &T { - &self.data + &self.guard } /// Reaches into this error indicating that a lock is poisoned, returning a - /// mutable reference to the associated data. + /// mutable reference to the underlying guard to allow access regardless. #[stable(feature = "sync_poison", since = "1.2.0")] pub fn get_mut(&mut self) -> &mut T { - &mut self.data + &mut self.guard } } @@ -397,13 +315,13 @@ impl Error for TryLockError { } } -pub(crate) fn map_result(result: LockResult, f: F) -> LockResult +pub fn map_result(result: LockResult, f: F) -> LockResult where F: FnOnce(T) -> U, { match result { Ok(t) => Ok(f(t)), #[cfg(panic = "unwind")] - Err(PoisonError { data }) => Err(PoisonError::new(f(data))), + Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))), } } diff --git a/std/src/sync/reentrant_lock.rs b/std/src/sync/reentrant_lock.rs index e009eb410efc0..0140e0d21299f 100644 --- a/std/src/sync/reentrant_lock.rs +++ b/std/src/sync/reentrant_lock.rs @@ -1,3 +1,6 @@ +#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] +mod tests; + use cfg_if::cfg_if; use crate::cell::UnsafeCell; @@ -321,10 +324,7 @@ impl ReentrantLock { /// Otherwise, an RAII guard is returned. /// /// This function does not block. - // FIXME maybe make it a public part of the API? - #[unstable(issue = "none", feature = "std_internals")] - #[doc(hidden)] - pub fn try_lock(&self) -> Option> { + pub(crate) fn try_lock(&self) -> Option> { 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/std/tests/sync/reentrant_lock.rs b/std/src/sync/reentrant_lock/tests.rs similarity index 91% rename from std/tests/sync/reentrant_lock.rs rename to std/src/sync/reentrant_lock/tests.rs index 2b7b87e36234a..aeef0289d28f8 100644 --- a/std/tests/sync/reentrant_lock.rs +++ b/std/src/sync/reentrant_lock/tests.rs @@ -1,6 +1,7 @@ -use std::cell::RefCell; -use std::sync::{Arc, ReentrantLock}; -use std::thread; +use super::ReentrantLock; +use crate::cell::RefCell; +use crate::sync::Arc; +use crate::thread; #[test] fn smoke() { diff --git a/std/src/sync/poison/rwlock.rs b/std/src/sync/rwlock.rs similarity index 91% rename from std/src/sync/poison/rwlock.rs rename to std/src/sync/rwlock.rs index f9d9321f5f2d8..d55d1c80dcae0 100644 --- a/std/src/sync/poison/rwlock.rs +++ b/std/src/sync/rwlock.rs @@ -1,7 +1,10 @@ +#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] +mod tests; + use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; -use crate::mem::{self, ManuallyDrop, forget}; +use crate::mem::{ManuallyDrop, forget}; use crate::ops::{Deref, DerefMut}; use crate::ptr::NonNull; use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison}; @@ -221,103 +224,6 @@ impl RwLock { pub const fn new(t: T) -> RwLock { RwLock { inner: sys::RwLock::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } } - - /// Returns the contained value by cloning it. - /// - /// # Errors - /// - /// This function will return an error if the `RwLock` is poisoned. An - /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::RwLock; - /// - /// let mut lock = RwLock::new(7); - /// - /// assert_eq!(lock.get_cloned().unwrap(), 7); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - pub fn get_cloned(&self) -> Result> - where - T: Clone, - { - match self.read() { - Ok(guard) => Ok((*guard).clone()), - Err(_) => Err(PoisonError::new(())), - } - } - - /// Sets the contained value. - /// - /// # Errors - /// - /// This function will return an error containing the provided `value` if - /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer - /// panics while holding an exclusive lock. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::RwLock; - /// - /// let mut lock = RwLock::new(7); - /// - /// assert_eq!(lock.get_cloned().unwrap(), 7); - /// lock.set(11).unwrap(); - /// assert_eq!(lock.get_cloned().unwrap(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - pub fn set(&self, value: T) -> Result<(), PoisonError> { - if mem::needs_drop::() { - // If the contained value has non-trivial destructor, we - // call that destructor after the lock being released. - self.replace(value).map(drop) - } else { - match self.write() { - Ok(mut guard) => { - *guard = value; - - Ok(()) - } - Err(_) => Err(PoisonError::new(value)), - } - } - } - - /// Replaces the contained value with `value`, and returns the old contained value. - /// - /// # Errors - /// - /// This function will return an error containing the provided `value` if - /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer - /// panics while holding an exclusive lock. - /// - /// # Examples - /// - /// ``` - /// #![feature(lock_value_accessors)] - /// - /// use std::sync::RwLock; - /// - /// let mut lock = RwLock::new(7); - /// - /// assert_eq!(lock.replace(11).unwrap(), 7); - /// assert_eq!(lock.get_cloned().unwrap(), 11); - /// ``` - #[unstable(feature = "lock_value_accessors", issue = "133407")] - pub fn replace(&self, value: T) -> LockResult { - match self.write() { - Ok(mut guard) => Ok(mem::replace(&mut *guard, value)), - Err(_) => Err(PoisonError::new(value)), - } - } } impl RwLock { @@ -338,8 +244,7 @@ impl RwLock { /// This function will return an error if the `RwLock` is poisoned. An /// `RwLock` is poisoned whenever a writer panics while holding an exclusive /// lock. The failure will occur immediately after the lock has been - /// acquired. The acquired lock guard will be contained in the returned - /// error. + /// acquired. /// /// # Panics /// @@ -387,8 +292,7 @@ impl RwLock { /// This function will return the [`Poisoned`] error if the `RwLock` is /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding /// an exclusive lock. `Poisoned` will only be returned if the lock would - /// have otherwise been acquired. An acquired lock guard will be contained - /// in the returned error. + /// have otherwise been acquired. /// /// This function will return the [`WouldBlock`] error if the `RwLock` could /// not be acquired because it was already locked exclusively. @@ -433,8 +337,7 @@ impl RwLock { /// /// This function will return an error if the `RwLock` is poisoned. An /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. An error will be returned when the lock is acquired. The acquired - /// lock guard will be contained in the returned error. + /// lock. An error will be returned when the lock is acquired. /// /// # Panics /// @@ -477,8 +380,7 @@ impl RwLock { /// This function will return the [`Poisoned`] error if the `RwLock` is /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding /// an exclusive lock. `Poisoned` will only be returned if the lock would - /// have otherwise been acquired. An acquired lock guard will be contained - /// in the returned error. + /// have otherwise been acquired. /// /// This function will return the [`WouldBlock`] error if the `RwLock` could /// not be acquired because it was already locked exclusively. @@ -579,10 +481,10 @@ impl RwLock { /// /// # Errors /// - /// This function will return an error containing the underlying data if - /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer - /// panics while holding an exclusive lock. An error will only be returned - /// if the lock would have otherwise been acquired. + /// This function will return an error if the `RwLock` is poisoned. An + /// `RwLock` is poisoned whenever a writer panics while holding an exclusive + /// lock. An error will only be returned if the lock would have otherwise + /// been acquired. /// /// # Examples /// @@ -612,11 +514,10 @@ impl RwLock { /// /// # Errors /// - /// This function will return an error containing a mutable reference to - /// the underlying data if the `RwLock` is poisoned. An `RwLock` is - /// poisoned whenever a writer panics while holding an exclusive lock. - /// An error will only be returned if the lock would have otherwise been - /// acquired. + /// This function will return an error if the `RwLock` is poisoned. An + /// `RwLock` is poisoned whenever a writer panics while holding an exclusive + /// lock. An error will only be returned if the lock would have otherwise + /// been acquired. /// /// # Examples /// diff --git a/std/tests/sync/rwlock.rs b/std/src/sync/rwlock/tests.rs similarity index 80% rename from std/tests/sync/rwlock.rs rename to std/src/sync/rwlock/tests.rs index bd4bc7a14bc8e..29cad4400f189 100644 --- a/std/tests/sync/rwlock.rs +++ b/std/src/sync/rwlock/tests.rs @@ -1,37 +1,16 @@ -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::{ +use rand::Rng; + +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::mpsc::channel; +use crate::sync::{ Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError, }; -use std::{hint, mem, thread}; - -use rand::Rng; +use crate::thread; #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); -#[derive(Eq, PartialEq, Debug)] -struct NonCopyNeedsDrop(i32); - -impl Drop for NonCopyNeedsDrop { - fn drop(&mut self) { - hint::black_box(()); - } -} - -#[test] -fn test_needs_drop() { - assert!(!mem::needs_drop::()); - assert!(mem::needs_drop::()); -} - -#[derive(Clone, Eq, PartialEq, Debug)] -struct Cloneable(i32); - #[test] fn smoke() { let l = RwLock::new(()); @@ -57,7 +36,7 @@ fn frob() { let tx = tx.clone(); let r = r.clone(); thread::spawn(move || { - let mut rng = crate::common::test_rng(); + let mut rng = crate::test_helpers::test_rng(); for _ in 0..M { if rng.gen_bool(1.0 / (N as f64)) { drop(r.write().unwrap()); @@ -276,21 +255,6 @@ fn test_rwlock_try_write() { drop(mapped_read_guard); } -fn new_poisoned_rwlock(value: T) -> RwLock { - let lock = RwLock::new(value); - - let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { - let _guard = lock.write().unwrap(); - - panic!("test panic to poison RwLock"); - })); - - assert!(catch_unwind_result.is_err()); - assert!(lock.is_poisoned()); - - lock -} - #[test] fn test_into_inner() { let m = RwLock::new(NonCopy(10)); @@ -317,31 +281,21 @@ fn test_into_inner_drop() { #[test] fn test_into_inner_poison() { - let m = new_poisoned_rwlock(NonCopy(10)); + let m = Arc::new(RwLock::new(NonCopy(10))); + let m2 = m.clone(); + let _ = thread::spawn(move || { + let _lock = m2.write().unwrap(); + panic!("test panic in inner thread to poison RwLock"); + }) + .join(); - match m.into_inner() { + assert!(m.is_poisoned()); + match Arc::try_unwrap(m).unwrap().into_inner() { Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {x:?}"), } } -#[test] -fn test_get_cloned() { - let m = RwLock::new(Cloneable(10)); - - assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); -} - -#[test] -fn test_get_cloned_poison() { - let m = new_poisoned_rwlock(Cloneable(10)); - - match m.get_cloned() { - Err(e) => assert_eq!(e.into_inner(), ()), - Ok(x) => panic!("get of poisoned RwLock is Ok: {x:?}"), - } -} - #[test] fn test_get_mut() { let mut m = RwLock::new(NonCopy(10)); @@ -351,90 +305,21 @@ fn test_get_mut() { #[test] fn test_get_mut_poison() { - let mut m = new_poisoned_rwlock(NonCopy(10)); + let m = Arc::new(RwLock::new(NonCopy(10))); + let m2 = m.clone(); + let _ = thread::spawn(move || { + let _lock = m2.write().unwrap(); + panic!("test panic in inner thread to poison RwLock"); + }) + .join(); - match m.get_mut() { + assert!(m.is_poisoned()); + match Arc::try_unwrap(m).unwrap().get_mut() { Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {x:?}"), } } -#[test] -fn test_set() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = RwLock::new(init()); - - assert_eq!(*m.read().unwrap(), init()); - m.set(value()).unwrap(); - assert_eq!(*m.read().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_set_poison() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = new_poisoned_rwlock(init()); - - match m.set(value()) { - Err(e) => { - assert_eq!(e.into_inner(), value()); - assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); - } - Ok(x) => panic!("set of poisoned RwLock is Ok: {x:?}"), - } - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_replace() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = RwLock::new(init()); - - assert_eq!(*m.read().unwrap(), init()); - assert_eq!(m.replace(value()).unwrap(), init()); - assert_eq!(*m.read().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_replace_poison() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = new_poisoned_rwlock(init()); - - match m.replace(value()) { - Err(e) => { - assert_eq!(e.into_inner(), value()); - assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); - } - Ok(x) => panic!("replace of poisoned RwLock is Ok: {x:?}"), - } - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - #[test] fn test_read_guard_covariance() { fn do_stuff<'a>(_: RwLockReadGuard<'_, &'a i32>, _: &'a i32) {} @@ -485,7 +370,7 @@ fn test_mapping_mapped_guard() { fn panic_while_mapping_read_unlocked_no_poison() { let lock = RwLock::new(()); - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.read().unwrap(); let _guard = RwLockReadGuard::map::<(), _>(guard, |_| panic!()); }); @@ -500,7 +385,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } } - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.read().unwrap(); let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); }); @@ -515,7 +400,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } } - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.read().unwrap(); let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockReadGuard::map::<(), _>(guard, |_| panic!()); @@ -531,7 +416,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } } - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.read().unwrap(); let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); @@ -554,7 +439,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { fn panic_while_mapping_write_unlocked_poison() { let lock = RwLock::new(()); - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.write().unwrap(); let _guard = RwLockWriteGuard::map::<(), _>(guard, |_| panic!()); }); @@ -567,7 +452,7 @@ fn panic_while_mapping_write_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.write().unwrap(); let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); }); @@ -582,7 +467,7 @@ fn panic_while_mapping_write_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.write().unwrap(); let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockWriteGuard::map::<(), _>(guard, |_| panic!()); @@ -598,7 +483,7 @@ fn panic_while_mapping_write_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = panic::catch_unwind(|| { + let _ = crate::panic::catch_unwind(|| { let guard = lock.write().unwrap(); let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); @@ -626,15 +511,12 @@ fn test_downgrade_basic() { } #[test] -// FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. -// See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] fn test_downgrade_observe() { // Taken from the test `test_rwlock_downgrade` from: // https://github.com/Amanieu/parking_lot/blob/master/src/rwlock.rs const W: usize = 20; - const N: usize = if cfg!(miri) { 40 } else { 100 }; + const N: usize = 100; // This test spawns `W` writer threads, where each will increment a counter `N` times, ensuring // that the value they wrote has not changed after downgrading. @@ -704,7 +586,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 = std::time::Duration::from_millis(42); + let eternity = crate::time::Duration::from_millis(42); thread::sleep(eternity); // Once everyone is asleep, set the value to `NEW_VALUE`. diff --git a/std/src/sys/alloc/wasm.rs b/std/src/sys/alloc/wasm.rs index 53fbc9529e590..a308fafc68b15 100644 --- a/std/src/sys/alloc/wasm.rs +++ b/std/src/sys/alloc/wasm.rs @@ -1,6 +1,6 @@ //! This is an implementation of a global allocator on wasm targets when -//! 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! +//! 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! //! //! 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/std/src/sys/anonymous_pipe/unix.rs b/std/src/sys/anonymous_pipe/unix.rs index 9e398765634b7..9168024730e67 100644 --- a/std/src/sys/anonymous_pipe/unix.rs +++ b/std/src/sys/anonymous_pipe/unix.rs @@ -1,5 +1,6 @@ -use crate::io::{self, PipeReader, PipeWriter}; +use crate::io; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; use crate::sys::fd::FileDesc; use crate::sys::pipe::anon_pipe; diff --git a/std/src/sys/anonymous_pipe/unsupported.rs b/std/src/sys/anonymous_pipe/unsupported.rs index 4e79ac9c21aad..dd51e70315e96 100644 --- a/std/src/sys/anonymous_pipe/unsupported.rs +++ b/std/src/sys/anonymous_pipe/unsupported.rs @@ -1,4 +1,5 @@ -use crate::io::{self, PipeReader, PipeWriter}; +use crate::io; +use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; pub use crate::sys::pipe::AnonPipe; diff --git a/std/src/sys/anonymous_pipe/windows.rs b/std/src/sys/anonymous_pipe/windows.rs index eb7fa8ec1c9a1..a48198f8a812b 100644 --- a/std/src/sys/anonymous_pipe/windows.rs +++ b/std/src/sys/anonymous_pipe/windows.rs @@ -1,12 +1,12 @@ -use crate::io::{self, PipeReader, PipeWriter}; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; +use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; -use crate::ptr; use crate::sys::c; use crate::sys::handle::Handle; use crate::sys_common::{FromInner, IntoInner}; +use crate::{io, ptr}; pub type AnonPipe = Handle; diff --git a/std/src/sys/backtrace.rs b/std/src/sys/backtrace.rs index efa6a896dad8f..4d939e175cf2e 100644 --- a/std/src/sys/backtrace.rs +++ b/std/src/sys/backtrace.rs @@ -58,8 +58,8 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: let mut res = Ok(()); let mut omitted_count: usize = 0; let mut first_omit = true; - // If we're using a short backtrace, ignore all frames until we're told to start printing. - let mut print = print_fmt != PrintFmt::Short; + // Start immediately if we're not using a short backtrace. + let mut start = print_fmt != PrintFmt::Short; set_image_base(); // SAFETY: we roll our own locking in this town unsafe { @@ -72,25 +72,27 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { hit = true; - // `__rust_end_short_backtrace` means we are done hiding symbols - // for now. Print until we see `__rust_begin_short_backtrace`. + // Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace` + // are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be + // called before the panic hook, so we won't ignore any frames if there is no + // invoke of `__rust_begin_short_backtrace`. if print_fmt == PrintFmt::Short { if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { - if sym.contains("__rust_end_short_backtrace") { - print = true; + if start && sym.contains("__rust_begin_short_backtrace") { + start = false; return; } - if print && sym.contains("__rust_begin_short_backtrace") { - print = false; + if sym.contains("__rust_end_short_backtrace") { + start = true; return; } - if !print { + if !start { omitted_count += 1; } } } - if print { + if start { if omitted_count > 0 { debug_assert!(print_fmt == PrintFmt::Short); // only print the message between the middle of frames @@ -110,7 +112,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: }); #[cfg(target_os = "nto")] if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { - if !hit && print { + if !hit && start { use crate::backtrace_rs::SymbolName; res = bt_fmt.frame().print_raw( frame.ip(), @@ -121,7 +123,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: } return false; } - if !hit && print { + if !hit && start { res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); } diff --git a/std/src/sys/cmath.rs b/std/src/sys/cmath.rs index ee36127cfdf1e..2997e908fa1b2 100644 --- a/std/src/sys/cmath.rs +++ b/std/src/sys/cmath.rs @@ -26,7 +26,6 @@ extern "C" { pub fn tgamma(n: f64) -> f64; pub fn tgammaf(n: f32) -> f32; pub fn lgamma_r(n: f64, s: &mut i32) -> f64; - #[cfg(not(target_os = "aix"))] pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; pub fn acosf128(n: f128) -> f128; @@ -57,13 +56,6 @@ extern "C" { }} } -// On AIX, we don't have lgammaf_r only the f64 version, so we can -// use the f64 version lgamma_r -#[cfg(target_os = "aix")] -pub unsafe fn lgammaf_r(n: f32, s: &mut i32) -> f32 { - lgamma_r(n.into(), s) as f32 -} - // On 32-bit x86 MSVC these functions aren't defined, so we just define shims // which promote everything to f64, perform the calculation, and then demote // back to f32. While not precisely correct should be "correct enough" for now. diff --git a/std/src/sys/io/is_terminal/hermit.rs b/std/src/sys/io/is_terminal/hermit.rs deleted file mode 100644 index 61bdb6f0a5440..0000000000000 --- a/std/src/sys/io/is_terminal/hermit.rs +++ /dev/null @@ -1,6 +0,0 @@ -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/std/src/sys/io/is_terminal/isatty.rs b/std/src/sys/io/is_terminal/isatty.rs deleted file mode 100644 index 6e0b46211b907..0000000000000 --- a/std/src/sys/io/is_terminal/isatty.rs +++ /dev/null @@ -1,6 +0,0 @@ -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/std/src/sys/io/is_terminal/unsupported.rs b/std/src/sys/io/is_terminal/unsupported.rs deleted file mode 100644 index cee4add32fbfc..0000000000000 --- a/std/src/sys/io/is_terminal/unsupported.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn is_terminal(_: &T) -> bool { - false -} diff --git a/std/src/sys/io/mod.rs b/std/src/sys/io/mod.rs deleted file mode 100644 index e00b479109f39..0000000000000 --- a/std/src/sys/io/mod.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![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/std/src/sys/mod.rs b/std/src/sys/mod.rs index 1032fcba5e2e1..f17dd47decedc 100644 --- a/std/src/sys/mod.rs +++ b/std/src/sys/mod.rs @@ -12,8 +12,6 @@ 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/std/src/sys/net/mod.rs b/std/src/sys/net/mod.rs deleted file mode 100644 index 646679a1cc8b9..0000000000000 --- a/std/src/sys/net/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -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/std/src/sys/pal/common/small_c_string.rs b/std/src/sys/pal/common/small_c_string.rs index f54505a856e05..3c96714b5c58c 100644 --- a/std/src/sys/pal/common/small_c_string.rs +++ b/std/src/sys/pal/common/small_c_string.rs @@ -11,7 +11,7 @@ const MAX_STACK_ALLOCATION: usize = 384; const MAX_STACK_ALLOCATION: usize = 32; const NUL_ERR: io::Error = - io::const_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte"); + io::const_io_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte"); #[inline] pub fn run_path_with_cstr(path: &Path, f: &dyn Fn(&CStr) -> io::Result) -> io::Result { diff --git a/std/src/sys/pal/hermit/fs.rs b/std/src/sys/pal/hermit/fs.rs index 783623552bb17..17d15ed2e5045 100644 --- a/std/src/sys/pal/hermit/fs.rs +++ b/std/src/sys/pal/hermit/fs.rs @@ -3,7 +3,7 @@ use super::hermit_abi::{ self, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, dirent64, stat as stat_struct, }; -use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; @@ -135,7 +135,7 @@ impl FileAttr { S_IFREG => DT_REG, _ => DT_UNKNOWN, }; - FileType { mode } + FileType { mode: mode } } } @@ -204,7 +204,7 @@ impl Iterator for ReadDir { // the size of dirent64. The file name is always a C string and terminated by `\0`. // Consequently, we are able to ignore the last byte. let name_bytes = - unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const c_char).to_bytes() }; + unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const i8).to_bytes() }; let entry = DirEntry { root: self.inner.root.clone(), ino: dir.d_ino, @@ -294,7 +294,7 @@ impl OpenOptions { (false, _, true) => Ok(O_WRONLY | O_APPEND), (true, _, true) => Ok(O_RDWR | O_APPEND), (false, false, false) => { - Err(io::const_error!(ErrorKind::InvalidInput, "invalid access mode")) + Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid access mode")) } } } @@ -304,16 +304,18 @@ impl OpenOptions { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err( - io::const_error!(ErrorKind::InvalidInput, "invalid creation mode",), - ); + return Err(io::const_io_error!( + ErrorKind::InvalidInput, + "invalid creation mode", + )); } } (_, true) => { if self.truncate && !self.create_new { - return Err( - io::const_error!(ErrorKind::InvalidInput, "invalid creation mode",), - ); + return Err(io::const_io_error!( + ErrorKind::InvalidInput, + "invalid creation mode", + )); } } } @@ -445,7 +447,7 @@ impl DirBuilder { pub fn mkdir(&self, path: &Path) -> io::Result<()> { run_path_with_cstr(path, &|path| { - cvt(unsafe { hermit_abi::mkdir(path.as_ptr().cast(), self.mode.into()) }).map(|_| ()) + cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode.into()) }).map(|_| ()) }) } diff --git a/std/src/sys/io/io_slice/iovec.rs b/std/src/sys/pal/hermit/io.rs similarity index 90% rename from std/src/sys/io/io_slice/iovec.rs rename to std/src/sys/pal/hermit/io.rs index 072191315f7c5..0424a1ac55a29 100644 --- a/std/src/sys/io/io_slice/iovec.rs +++ b/std/src/sys/pal/hermit/io.rs @@ -1,13 +1,8 @@ -#[cfg(target_os = "hermit")] -use hermit_abi::iovec; -#[cfg(target_family = "unix")] -use libc::iovec; +use hermit_abi::{c_void, iovec}; -use crate::ffi::c_void; use crate::marker::PhantomData; +use crate::os::hermit::io::{AsFd, AsRawFd}; use crate::slice; -#[cfg(target_os = "solid_asp3")] -use crate::sys::pal::abi::sockets::iovec; #[derive(Copy, Clone)] #[repr(transparent)] @@ -85,3 +80,8 @@ 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(); + hermit_abi::isatty(fd.as_raw_fd()) +} diff --git a/std/src/sys/pal/hermit/mod.rs b/std/src/sys/pal/hermit/mod.rs index 3d555ad5050c2..b62afb40a615f 100644 --- a/std/src/sys/pal/hermit/mod.rs +++ b/std/src/sys/pal/hermit/mod.rs @@ -23,6 +23,8 @@ 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; @@ -40,7 +42,7 @@ pub fn unsupported() -> crate::io::Result { } pub fn unsupported_err() -> crate::io::Error { - crate::io::const_error!( + crate::io::const_io_error!( crate::io::ErrorKind::Unsupported, "operation not supported on HermitCore yet", ) @@ -83,7 +85,7 @@ pub unsafe extern "C" fn runtime_entry( } // initialize environment - os::init_environment(env); + os::init_environment(env as *const *const i8); let result = unsafe { main(argc as isize, argv) }; diff --git a/std/src/sys/net/connection/socket/hermit.rs b/std/src/sys/pal/hermit/net.rs similarity index 97% rename from std/src/sys/net/connection/socket/hermit.rs rename to std/src/sys/pal/hermit/net.rs index 42179dcc9156d..d9baa091a2321 100644 --- a/std/src/sys/net/connection/socket/hermit.rs +++ b/std/src/sys/pal/hermit/net.rs @@ -2,20 +2,21 @@ use core::ffi::c_int; -pub(crate) use hermit_abi as netc; - +use super::fd::FileDesc; 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; -pub use crate::sys::{cvt, cvt_r}; +use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem}; -#[expect(non_camel_case_types)] +#[allow(unused_extern_crates)] +pub extern crate hermit_abi as netc; + +pub use crate::sys::{cvt, cvt_r}; + pub type wrlen_t = usize; pub fn cvt_gai(err: i32) -> io::Result<()> { @@ -86,7 +87,7 @@ impl Socket { loop { let elapsed = start.elapsed(); if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); + return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")); } let timeout = timeout - elapsed; @@ -113,7 +114,7 @@ impl Socket { // for POLLHUP rather than read readiness if pollfd.revents & netc::POLLHUP != 0 { let e = self.take_error()?.unwrap_or_else(|| { - io::const_error!( + io::const_io_error!( io::ErrorKind::Uncategorized, "no error set after POLLHUP", ) diff --git a/std/src/sys/pal/hermit/os.rs b/std/src/sys/pal/hermit/os.rs index 791cdb1e57e7d..f8ea80afa43f1 100644 --- a/std/src/sys/pal/hermit/os.rs +++ b/std/src/sys/pal/hermit/os.rs @@ -3,7 +3,7 @@ use core::slice::memchr; use super::hermit_abi; use crate::collections::HashMap; use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString, c_char}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::marker::PhantomData; use crate::os::hermit::ffi::OsStringExt; use crate::path::{self, PathBuf}; @@ -70,7 +70,7 @@ pub fn current_exe() -> io::Result { static ENV: Mutex>> = Mutex::new(None); -pub fn init_environment(env: *const *const c_char) { +pub fn init_environment(env: *const *const i8) { let mut guard = ENV.lock().unwrap(); let map = guard.insert(HashMap::new()); diff --git a/std/src/sys/pal/hermit/thread.rs b/std/src/sys/pal/hermit/thread.rs index 4a7afddbec107..41f2c3e212355 100644 --- a/std/src/sys/pal/hermit/thread.rs +++ b/std/src/sys/pal/hermit/thread.rs @@ -41,9 +41,9 @@ impl Thread { unsafe { drop(Box::from_raw(p)); } - Err(io::const_error!(io::ErrorKind::Uncategorized, "Unable to create thread!")) + Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Unable to create thread!")) } else { - Ok(Thread { tid }) + Ok(Thread { tid: tid }) }; extern "C" fn thread_start(main: usize) { diff --git a/std/src/sys/pal/hermit/time.rs b/std/src/sys/pal/hermit/time.rs index f76a5f96c8750..e0b6eb76b03af 100644 --- a/std/src/sys/pal/hermit/time.rs +++ b/std/src/sys/pal/hermit/time.rs @@ -22,7 +22,7 @@ impl Timespec { const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC); // SAFETY: The assert above checks tv_nsec is within the valid range - Timespec { t: timespec { tv_sec, tv_nsec } } + Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } } fn sub_timespec(&self, other: &Timespec) -> Result { diff --git a/std/src/sys/pal/itron/time/tests.rs b/std/src/sys/pal/itron/time/tests.rs index d14035d9da49f..28db4f8b6799f 100644 --- a/std/src/sys/pal/itron/time/tests.rs +++ b/std/src/sys/pal/itron/time/tests.rs @@ -8,26 +8,24 @@ fn reltim2dur(t: u64) -> Duration { fn test_dur2reltims() { assert_eq!(dur2reltims(reltim2dur(0)).collect::>(), vec![]); assert_eq!(dur2reltims(reltim2dur(42)).collect::>(), vec![42]); - assert_eq!( - dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), - vec![abi::TMAX_RELTIM] - ); - assert_eq!( - dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), - vec![abi::TMAX_RELTIM, 10000] - ); + assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), vec![ + abi::TMAX_RELTIM + ]); + assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), vec![ + abi::TMAX_RELTIM, + 10000 + ]); } #[test] fn test_dur2tmos() { assert_eq!(dur2tmos(reltim2dur(0)).collect::>(), vec![0]); assert_eq!(dur2tmos(reltim2dur(42)).collect::>(), vec![42]); - assert_eq!( - dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), - vec![abi::TMAX_RELTIM] - ); - assert_eq!( - dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), - vec![abi::TMAX_RELTIM, 10000] - ); + assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), vec![ + abi::TMAX_RELTIM + ]); + assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), vec![ + abi::TMAX_RELTIM, + 10000 + ]); } diff --git a/std/src/sys/pal/sgx/fd.rs b/std/src/sys/pal/sgx/fd.rs index 3bb3189a1d127..c41b527cff798 100644 --- a/std/src/sys/pal/sgx/fd.rs +++ b/std/src/sys/pal/sgx/fd.rs @@ -12,7 +12,7 @@ pub struct FileDesc { impl FileDesc { pub fn new(fd: Fd) -> FileDesc { - FileDesc { fd } + FileDesc { fd: fd } } pub fn raw(&self) -> Fd { diff --git a/std/src/sys/pal/sgx/mod.rs b/std/src/sys/pal/sgx/mod.rs index 9a04fa4b97e19..586ccd18c2f57 100644 --- a/std/src/sys/pal/sgx/mod.rs +++ b/std/src/sys/pal/sgx/mod.rs @@ -14,7 +14,10 @@ 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; @@ -45,7 +48,7 @@ pub fn unsupported() -> crate::io::Result { } pub fn unsupported_err() -> crate::io::Error { - crate::io::const_error!(ErrorKind::Unsupported, "operation not supported on SGX yet") + crate::io::const_io_error!(ErrorKind::Unsupported, "operation not supported on SGX yet") } /// This function is used to implement various functions that doesn't exist, @@ -56,7 +59,7 @@ pub fn unsupported_err() -> crate::io::Error { pub fn sgx_ineffective(v: T) -> crate::io::Result { static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false); if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) { - Err(crate::io::const_error!( + Err(crate::io::const_io_error!( ErrorKind::Uncategorized, "operation can't be trusted to have any effect on SGX", )) diff --git a/std/src/sys/net/connection/sgx.rs b/std/src/sys/pal/sgx/net.rs similarity index 99% rename from std/src/sys/net/connection/sgx.rs rename to std/src/sys/pal/sgx/net.rs index b390a5eac5f74..c966886d16344 100644 --- a/std/src/sys/net/connection/sgx.rs +++ b/std/src/sys/pal/sgx/net.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/std/src/sys/pal/sgx/stdio.rs b/std/src/sys/pal/sgx/stdio.rs index e79a3d971c6be..2e680e740fde3 100644 --- a/std/src/sys/pal/sgx/stdio.rs +++ b/std/src/sys/pal/sgx/stdio.rs @@ -62,7 +62,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { // FIXME: Rust normally maps Unix EBADF to `Uncategorized` diff --git a/std/src/sys/pal/solid/error.rs b/std/src/sys/pal/solid/error.rs index b399463c0c280..e092497856d43 100644 --- a/std/src/sys/pal/solid/error.rs +++ b/std/src/sys/pal/solid/error.rs @@ -1,7 +1,6 @@ pub use self::itron::error::{ItronError as SolidError, expect_success}; -use super::{abi, itron}; +use super::{abi, itron, net}; 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/std/src/sys/pal/solid/fs.rs b/std/src/sys/pal/solid/fs.rs index fa2e470d6b601..776a96ff3b7ba 100644 --- a/std/src/sys/pal/solid/fs.rs +++ b/std/src/sys/pal/solid/fs.rs @@ -12,12 +12,15 @@ use crate::sys::unsupported; pub use crate::sys_common::fs::exists; use crate::sys_common::ignore_notfound; -type CIntNotMinusOne = core::num::niche_types::NotAllOnes; - /// A file descriptor. #[derive(Clone, Copy)] +#[rustc_layout_scalar_valid_range_start(0)] +// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a +// 32-bit c_int. Below is -2, in two's complement, but that only works out +// because c_int is 32 bits. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] struct FileDesc { - fd: CIntNotMinusOne, + fd: c_int, } impl FileDesc { @@ -26,13 +29,12 @@ impl FileDesc { assert_ne!(fd, -1i32); // Safety: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - let fd = unsafe { CIntNotMinusOne::new_unchecked(fd) }; - FileDesc { fd } + unsafe { FileDesc { fd } } } #[inline] fn raw(&self) -> c_int { - self.fd.as_inner() + self.fd } } @@ -301,7 +303,7 @@ fn cstr(path: &Path) -> io::Result { if !path.starts_with(br"\") { // Relative paths aren't supported - return Err(crate::io::const_error!( + return Err(crate::io::const_io_error!( crate::io::ErrorKind::Unsupported, "relative path is not supported on this platform", )); @@ -312,7 +314,10 @@ fn cstr(path: &Path) -> io::Result { let wrapped_path = [SAFE_PREFIX, &path, &[0]].concat(); CString::from_vec_with_nul(wrapped_path).map_err(|_| { - crate::io::const_error!(io::ErrorKind::InvalidInput, "path provided contains a nul byte",) + crate::io::const_io_error!( + io::ErrorKind::InvalidInput, + "path provided contains a nul byte", + ) }) } @@ -507,7 +512,7 @@ impl fmt::Debug for File { pub fn unlink(p: &Path) -> io::Result<()> { if stat(p)?.file_type().is_dir() { - Err(io::const_error!(io::ErrorKind::IsADirectory, "is a directory")) + Err(io::const_io_error!(io::ErrorKind::IsADirectory, "is a directory")) } else { error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) }) .map_err(|e| e.as_io_error())?; @@ -537,7 +542,7 @@ pub fn rmdir(p: &Path) -> io::Result<()> { .map_err(|e| e.as_io_error())?; Ok(()) } else { - Err(io::const_error!(io::ErrorKind::NotADirectory, "not a directory")) + Err(io::const_io_error!(io::ErrorKind::NotADirectory, "not a directory")) } } @@ -565,7 +570,7 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { pub fn readlink(p: &Path) -> io::Result { // This target doesn't support symlinks stat(p)?; - Err(io::const_error!(io::ErrorKind::InvalidInput, "not a symbolic link")) + Err(io::const_io_error!(io::ErrorKind::InvalidInput, "not a symbolic link")) } pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { diff --git a/std/src/sys/io/io_slice/windows.rs b/std/src/sys/pal/solid/io.rs similarity index 54% rename from std/src/sys/io/io_slice/windows.rs rename to std/src/sys/pal/solid/io.rs index c3d8ec87c19e3..9ef4b7049b690 100644 --- a/std/src/sys/io/io_slice/windows.rs +++ b/std/src/sys/pal/solid/io.rs @@ -1,82 +1,86 @@ +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: c::WSABUF, + vec: iovec, _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 }, + 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.len as usize) < n { + if self.vec.iov_len < n { panic!("advancing IoSlice beyond its length"); } unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); + 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.buf, self.vec.len as usize) } + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } #[repr(transparent)] pub struct IoSliceMut<'a> { - vec: c::WSABUF, + vec: iovec, _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() }, + 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.len as usize) < n { + if self.vec.iov_len < n { panic!("advancing IoSliceMut beyond its length"); } unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); + 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.buf, self.vec.len as usize) } + 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.buf, self.vec.len as usize) } + 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.buf, self.vec.len as usize) } + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } + +pub fn is_terminal(_: &T) -> bool { + false +} diff --git a/std/src/sys/pal/solid/mod.rs b/std/src/sys/pal/solid/mod.rs index 06af7bfade059..d41042be51844 100644 --- a/std/src/sys/pal/solid/mod.rs +++ b/std/src/sys/pal/solid/mod.rs @@ -23,6 +23,8 @@ 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; @@ -49,7 +51,7 @@ pub fn unsupported_err() -> crate::io::Error { #[inline] pub fn is_interrupted(code: i32) -> bool { - crate::sys::net::is_interrupted(code) + net::is_interrupted(code) } pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { diff --git a/std/src/sys/net/connection/socket/solid.rs b/std/src/sys/pal/solid/net.rs similarity index 94% rename from std/src/sys/net/connection/socket/solid.rs rename to std/src/sys/pal/solid/net.rs index f85ecbb883ee7..c0818ecd856d2 100644 --- a/std/src/sys/net/connection/socket/solid.rs +++ b/std/src/sys/pal/solid/net.rs @@ -1,23 +1,24 @@ 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::abi; -use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys_common::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 crate::sys::abi::sockets::*; + pub use super::super::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. @@ -77,7 +78,7 @@ fn last_error() -> io::Error { io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) } -pub fn error_name(er: abi::ER) -> Option<&'static str> { +pub(super) fn error_name(er: abi::ER) -> Option<&'static str> { unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() } @@ -86,7 +87,7 @@ pub fn is_interrupted(er: abi::ER) -> bool { er == netc::SOLID_NET_ERR_BASE - libc::EINTR } -pub fn decode_error_kind(er: abi::ER) -> ErrorKind { +pub(super) 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, @@ -174,7 +175,7 @@ impl Socket { }; match n { - 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), + 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")), _ => { let can_write = writefds.num_fds != 0; if !can_write { @@ -267,6 +268,17 @@ impl Socket { self.recv_from_with_flags(buf, MSG_PEEK) } + pub fn write(&self, buf: &[u8]) -> io::Result { + 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 { let ret = cvt(unsafe { netc::writev( diff --git a/std/src/sys/pal/solid/os.rs b/std/src/sys/pal/solid/os.rs index 57c28aed3b293..d8afcb91f67f2 100644 --- a/std/src/sys/pal/solid/os.rs +++ b/std/src/sys/pal/solid/os.rs @@ -204,7 +204,7 @@ pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { /// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this /// function just returns a generic error. fn cvt_env(t: c_int) -> io::Result { - if t == -1 { Err(io::const_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } + if t == -1 { Err(io::const_io_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) } } pub fn temp_dir() -> PathBuf { diff --git a/std/src/sys/pal/teeos/mod.rs b/std/src/sys/pal/teeos/mod.rs index f850fefc8f22f..60a227afb84e3 100644 --- a/std/src/sys/pal/teeos/mod.rs +++ b/std/src/sys/pal/teeos/mod.rs @@ -13,6 +13,9 @@ 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; @@ -24,14 +27,6 @@ pub mod thread; #[path = "../unix/time.rs"] pub mod time; -#[path = "../unix/sync"] -pub mod sync { - mod condvar; - mod mutex; - pub use condvar::Condvar; - pub use mutex::Mutex; -} - use crate::io::ErrorKind; pub fn abort_internal() -> ! { @@ -68,7 +63,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ECONNREFUSED => ConnectionRefused, libc::ECONNRESET => ConnectionReset, libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, + libc::EDQUOT => FilesystemQuotaExceeded, libc::EEXIST => AlreadyExists, libc::EFBIG => FileTooLarge, libc::EHOSTUNREACH => HostUnreachable, diff --git a/std/src/sys/net/connection/unsupported.rs b/std/src/sys/pal/teeos/net.rs similarity index 99% rename from std/src/sys/net/connection/unsupported.rs rename to std/src/sys/pal/teeos/net.rs index 87e6106468fdb..fed95205027a7 100644 --- a/std/src/sys/net/connection/unsupported.rs +++ b/std/src/sys/pal/teeos/net.rs @@ -346,7 +346,6 @@ 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, @@ -359,7 +358,6 @@ 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, @@ -367,3 +365,5 @@ pub mod netc { pub sin6_scope_id: u32, } } + +pub type Socket = UdpSocket; diff --git a/std/src/sys/pal/uefi/fs.rs b/std/src/sys/pal/uefi/fs.rs deleted file mode 100644 index 9585ec24f687d..0000000000000 --- a/std/src/sys/pal/uefi/fs.rs +++ /dev/null @@ -1,344 +0,0 @@ -use crate::ffi::OsString; -use crate::fmt; -use crate::hash::{Hash, Hasher}; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::path::{Path, PathBuf}; -use crate::sys::time::SystemTime; -use crate::sys::unsupported; - -pub struct File(!); - -pub struct FileAttr(!); - -pub struct ReadDir(!); - -pub struct DirEntry(!); - -#[derive(Clone, Debug)] -pub struct OpenOptions {} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes {} - -pub struct FilePermissions(!); - -pub struct FileType(!); - -#[derive(Debug)] -pub struct DirBuilder {} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.0 - } - - pub fn perm(&self) -> FilePermissions { - self.0 - } - - pub fn file_type(&self) -> FileType { - self.0 - } - - pub fn modified(&self) -> io::Result { - self.0 - } - - pub fn accessed(&self) -> io::Result { - self.0 - } - - pub fn created(&self) -> io::Result { - self.0 - } -} - -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - self.0 - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.0 - } - - pub fn set_readonly(&mut self, _readonly: bool) { - self.0 - } -} - -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - self.0 - } -} - -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - self.0 - } -} - -impl Eq for FilePermissions {} - -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl FileTimes { - pub fn set_accessed(&mut self, _t: SystemTime) {} - pub fn set_modified(&mut self, _t: SystemTime) {} -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.0 - } - - pub fn is_file(&self) -> bool { - self.0 - } - - pub fn is_symlink(&self) -> bool { - self.0 - } -} - -impl Clone for FileType { - fn clone(&self) -> FileType { - self.0 - } -} - -impl Copy for FileType {} - -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - self.0 - } -} - -impl Eq for FileType {} - -impl Hash for FileType { - fn hash(&self, _h: &mut H) { - self.0 - } -} - -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -impl Iterator for ReadDir { - type Item = io::Result; - - fn next(&mut self) -> Option> { - self.0 - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - self.0 - } - - pub fn file_name(&self) -> OsString { - self.0 - } - - pub fn metadata(&self) -> io::Result { - self.0 - } - - pub fn file_type(&self) -> io::Result { - self.0 - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - OpenOptions {} - } - - pub fn read(&mut self, _read: bool) {} - pub fn write(&mut self, _write: bool) {} - pub fn append(&mut self, _append: bool) {} - pub fn truncate(&mut self, _truncate: bool) {} - pub fn create(&mut self, _create: bool) {} - pub fn create_new(&mut self, _create_new: bool) {} -} - -impl File { - pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { - unsupported() - } - - pub fn file_attr(&self) -> io::Result { - self.0 - } - - pub fn fsync(&self) -> io::Result<()> { - self.0 - } - - pub fn datasync(&self) -> io::Result<()> { - self.0 - } - - pub fn lock(&self) -> io::Result<()> { - self.0 - } - - pub fn lock_shared(&self) -> io::Result<()> { - self.0 - } - - pub fn try_lock(&self) -> io::Result { - self.0 - } - - pub fn try_lock_shared(&self) -> io::Result { - self.0 - } - - pub fn unlock(&self) -> io::Result<()> { - self.0 - } - - pub fn truncate(&self, _size: u64) -> io::Result<()> { - self.0 - } - - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn write(&self, _buf: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn flush(&self) -> io::Result<()> { - self.0 - } - - pub fn seek(&self, _pos: SeekFrom) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - self.0 - } - - pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { - self.0 - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder {} - } - - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() - } -} - -impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub fn readdir(_p: &Path) -> io::Result { - unsupported() -} - -pub fn unlink(_p: &Path) -> io::Result<()> { - unsupported() -} - -pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { - unsupported() -} - -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} -} - -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() -} - -pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - unsupported() -} - -pub fn exists(_path: &Path) -> io::Result { - unsupported() -} - -pub fn readlink(_p: &Path) -> io::Result { - unsupported() -} - -pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { - unsupported() -} - -pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { - unsupported() -} - -pub fn stat(_p: &Path) -> io::Result { - unsupported() -} - -pub fn lstat(_p: &Path) -> io::Result { - unsupported() -} - -pub fn canonicalize(_p: &Path) -> io::Result { - unsupported() -} - -pub fn copy(_from: &Path, _to: &Path) -> io::Result { - unsupported() -} diff --git a/std/src/sys/pal/uefi/helpers.rs b/std/src/sys/pal/uefi/helpers.rs index dccc137d6f561..abc8e69a285f3 100644 --- a/std/src/sys/pal/uefi/helpers.rs +++ b/std/src/sys/pal/uefi/helpers.rs @@ -13,13 +13,11 @@ use r_efi::efi::{self, Guid}; 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::io::{self, const_io_error}; 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}; @@ -32,7 +30,7 @@ type BootUninstallMultipleProtocolInterfaces = unsafe extern "efiapi" fn(_: r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; const BOOT_SERVICES_UNAVAILABLE: io::Error = - const_error!(io::ErrorKind::Other, "Boot Services are no longer available"); + const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); /// Locates Handles with a particular Protocol GUID. /// @@ -116,7 +114,7 @@ pub(crate) fn open_protocol( Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { NonNull::new(unsafe { protocol.assume_init() }) - .ok_or(const_error!(io::ErrorKind::Other, "null protocol")) + .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) } } @@ -136,7 +134,7 @@ pub(crate) fn create_event( if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { - NonNull::new(event).ok_or(const_error!(io::ErrorKind::Other, "null protocol")) + NonNull::new(event).ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) } } @@ -157,8 +155,10 @@ pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result /// /// Note: Some protocols need to be manually freed. It is the caller's responsibility to do so. pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> io::Result> { - let system_handle = uefi::env::try_image_handle() - .ok_or(io::const_error!(io::ErrorKind::NotFound, "Protocol not found in Image handle"))?; + let system_handle = uefi::env::try_image_handle().ok_or(io::const_io_error!( + io::ErrorKind::NotFound, + "Protocol not found in Image handle" + ))?; open_protocol(system_handle, protocol_guid) } @@ -178,7 +178,7 @@ pub(crate) fn device_path_to_text(path: NonNull) -> io::R }; let path = os_string_from_raw(path_ptr) - .ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?; + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?; if let Some(boot_services) = crate::os::uefi::env::boot_services() { let boot_services: NonNull = boot_services.cast(); @@ -213,7 +213,7 @@ pub(crate) fn device_path_to_text(path: NonNull) -> io::R } } - Err(io::const_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) + Err(io::const_io_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) } /// Gets RuntimeServices. @@ -224,17 +224,17 @@ pub(crate) fn runtime_services() -> Option> NonNull::new(runtime_services) } -pub(crate) struct OwnedDevicePath(NonNull); +pub(crate) struct DevicePath(NonNull); -impl OwnedDevicePath { +impl DevicePath { pub(crate) fn from_text(p: &OsStr) -> io::Result { fn inner( p: &OsStr, protocol: NonNull, - ) -> io::Result { + ) -> io::Result { let path_vec = p.encode_wide().chain(Some(0)).collect::>(); if path_vec[..path_vec.len() - 1].contains(&0) { - return Err(const_error!( + return Err(const_io_error!( io::ErrorKind::InvalidInput, "strings passed to UEFI cannot contain NULs", )); @@ -243,9 +243,9 @@ impl OwnedDevicePath { let path = unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; - NonNull::new(path) - .map(OwnedDevicePath) - .ok_or_else(|| const_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path")) + NonNull::new(path).map(DevicePath).ok_or_else(|| { + const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path") + }) } static LAST_VALID_HANDLE: AtomicPtr = @@ -271,22 +271,18 @@ impl OwnedDevicePath { } } - io::Result::Err(const_error!( + io::Result::Err(const_io_error!( io::ErrorKind::NotFound, "DevicePathFromText Protocol not found" )) } - pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { + pub(crate) 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 { +impl Drop for DevicePath { fn drop(&mut self) { if let Some(bt) = boot_services() { let bt: NonNull = bt.cast(); @@ -297,39 +293,6 @@ impl Drop for OwnedDevicePath { } } -impl crate::fmt::Debug for OwnedDevicePath { - fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { - 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, - phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, -} - -impl<'a> BorrowedDevicePath<'a> { - pub(crate) const fn new(protocol: NonNull) -> Self { - Self { protocol, phantom: PhantomData } - } - - pub(crate) fn to_text(&self) -> io::Result { - 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 { guid: r_efi::efi::Guid, handle: NonNull, @@ -363,7 +326,7 @@ impl OwnedProtocol { }; let handle = NonNull::new(handle) - .ok_or(io::const_error!(io::ErrorKind::Uncategorized, "found null handle"))?; + .ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?; Ok(Self { guid, handle, protocol }) } @@ -482,21 +445,3 @@ pub(crate) fn open_shell() -> Option> { 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> { - 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/std/src/sys/pal/uefi/mod.rs b/std/src/sys/pal/uefi/mod.rs index 4766e2ef0a95f..c0ab52f650aa5 100644 --- a/std/src/sys/pal/uefi/mod.rs +++ b/std/src/sys/pal/uefi/mod.rs @@ -15,8 +15,13 @@ pub mod args; pub mod env; +#[path = "../unsupported/fs.rs"] 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; @@ -90,7 +95,7 @@ pub const fn unsupported() -> std_io::Result { #[inline] pub const fn unsupported_err() -> std_io::Error { - std_io::const_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) + std_io::const_io_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI",) } pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { diff --git a/std/src/sys/pal/uefi/os.rs b/std/src/sys/pal/uefi/os.rs index 6d23c72ef2209..27395f7c3c0b3 100644 --- a/std/src/sys/pal/uefi/os.rs +++ b/std/src/sys/pal/uefi/os.rs @@ -131,7 +131,7 @@ pub fn getcwd() -> io::Result { let path_ptr = unsafe { ((*shell.as_ptr()).get_cur_dir)(crate::ptr::null_mut()) }; helpers::os_string_from_raw(path_ptr) .map(PathBuf::from) - .ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path")) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path")) } None => { let mut t = current_exe()?; @@ -147,7 +147,7 @@ pub fn chdir(p: &path::Path) -> io::Result<()> { let shell = helpers::open_shell().ok_or(unsupported_err())?; let mut p = helpers::os_string_to_raw(p.as_os_str()) - .ok_or(io::const_error!(io::ErrorKind::InvalidData, "Invalid path"))?; + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?; let r = unsafe { ((*shell.as_ptr()).set_cur_dir)(crate::ptr::null_mut(), p.as_mut_ptr()) }; if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } @@ -290,15 +290,15 @@ mod uefi_env { pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; let mut val_ptr = helpers::os_string_to_raw(val) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } } pub(crate) fn unset(key: &OsStr) -> io::Result<()> { let mut key_ptr = helpers::os_string_to_raw(key) - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } } @@ -328,7 +328,7 @@ mod uefi_env { }); // SAFETY: val.add(start) is always NULL terminated let val = unsafe { get_raw(shell, val.add(start)) } - .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; vars.push((key, val)); start = i + 1; diff --git a/std/src/sys/pal/uefi/process.rs b/std/src/sys/pal/uefi/process.rs index 0757f1cb490d4..1b83f4b0aee88 100644 --- a/std/src/sys/pal/uefi/process.rs +++ b/std/src/sys/pal/uefi/process.rs @@ -1,7 +1,6 @@ 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}; @@ -22,7 +21,6 @@ pub struct Command { args: Vec, stdout: Option, stderr: Option, - env: CommandEnv, } // passed back to std::process with the pipes connected to the child, if any @@ -42,13 +40,7 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { - prog: program.to_os_string(), - args: Vec::new(), - stdout: None, - stderr: None, - env: Default::default(), - } + Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } } pub fn arg(&mut self, arg: &OsStr) { @@ -56,7 +48,7 @@ impl Command { } pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env + panic!("unsupported") } pub fn cwd(&mut self, _dir: &OsStr) { @@ -84,7 +76,7 @@ impl Command { } pub fn get_envs(&self) -> CommandEnvs<'_> { - self.env.iter() + panic!("unsupported") } pub fn get_current_dir(&self) -> Option<&Path> { @@ -148,30 +140,8 @@ 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()?; @@ -337,7 +307,7 @@ mod uefi_command_internal { use super::super::helpers; use crate::ffi::{OsStr, OsString}; - use crate::io::{self, const_error}; + use crate::io::{self, const_io_error}; use crate::mem::MaybeUninit; use crate::os::uefi::env::{boot_services, image_handle, system_table}; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; @@ -356,9 +326,9 @@ mod uefi_command_internal { impl Image { pub fn load_image(p: &OsStr) -> io::Result { - let path = helpers::OwnedDevicePath::from_text(p)?; + let path = helpers::DevicePath::from_text(p)?; let boot_services: NonNull = boot_services() - .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? .cast(); let mut child_handle: MaybeUninit = MaybeUninit::uninit(); let image_handle = image_handle(); @@ -399,7 +369,7 @@ mod uefi_command_internal { } let boot_services: NonNull = boot_services() - .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? .cast(); let mut exit_data_size: usize = 0; let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit(); @@ -490,7 +460,7 @@ mod uefi_command_internal { helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); let len = args.len(); - let args_size: u32 = (len * crate::mem::size_of::()).try_into().unwrap(); + let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap(); let ptr = Box::into_raw(args).as_mut_ptr(); unsafe { @@ -613,7 +583,7 @@ mod uefi_command_internal { OsString::from_wide(&self._buffer) .into_string() .map(Into::into) - .map_err(|_| const_error!(io::ErrorKind::Other, "utf8 conversion failed")) + .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed")) } extern "efiapi" fn reset( @@ -736,10 +706,9 @@ 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() { @@ -750,37 +719,10 @@ mod uefi_command_internal { res.push(c); } res.push(QUOTE); - } - 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, Option)>> { - if env.is_unchanged() { - return None; - } - - let mut result = BTreeMap::, Option)>::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)); + res.push(SPACE); } - } - for (k, v) in env.iter() { - let v: Option = 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)); + res.into_boxed_slice() } - - Some(result) } diff --git a/std/src/sys/pal/unix/fd.rs b/std/src/sys/pal/unix/fd.rs index 2fc33bdfefbf5..6a28799ca55eb 100644 --- a/std/src/sys/pal/unix/fd.rs +++ b/std/src/sys/pal/unix/fd.rs @@ -5,6 +5,7 @@ mod tests; #[cfg(not(any( target_os = "linux", + target_os = "emscripten", target_os = "l4re", target_os = "android", target_os = "hurd", @@ -13,6 +14,7 @@ use libc::off_t as off64_t; #[cfg(any( target_os = "android", target_os = "linux", + target_os = "emscripten", target_os = "l4re", target_os = "hurd", ))] diff --git a/std/src/sys/pal/unix/fs.rs b/std/src/sys/pal/unix/fs.rs index 00cfa7a7fcfda..96f99efb21e84 100644 --- a/std/src/sys/pal/unix/fs.rs +++ b/std/src/sys/pal/unix/fs.rs @@ -8,14 +8,16 @@ mod tests; use libc::c_char; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", 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"))] +#[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", + target_os = "hurd" +))] use libc::fstatat64; #[cfg(any( target_os = "android", @@ -32,6 +34,7 @@ use libc::readdir as readdir64; #[cfg(not(any( target_os = "android", target_os = "linux", + target_os = "emscripten", target_os = "solaris", target_os = "illumos", target_os = "l4re", @@ -45,7 +48,7 @@ use libc::readdir as readdir64; use libc::readdir_r as readdir64_r; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::readdir64; -#[cfg(target_os = "l4re")] +#[cfg(any(target_os = "emscripten", target_os = "l4re"))] use libc::readdir64_r; use libc::{c_int, mode_t}; #[cfg(target_os = "android")] @@ -55,6 +58,7 @@ use libc::{ }; #[cfg(not(any( all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", target_os = "l4re", target_os = "android", target_os = "hurd", @@ -65,6 +69,7 @@ use libc::{ }; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", target_os = "l4re", target_os = "hurd" ))] @@ -163,8 +168,7 @@ cfg_has_statx! {{ ) -> c_int } - let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed); - if statx_availability == STATX_STATE::Unavailable as u8 { + if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 { return None; } @@ -196,9 +200,6 @@ cfg_has_statx! {{ return None; } } - if statx_availability == STATX_STATE::Unknown as u8 { - STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed); - } // We cannot fill `stat64` exhaustively because of private padding fields. let mut stat: stat64 = mem::zeroed(); @@ -558,7 +559,7 @@ impl FileAttr { return if (ext.stx_mask & libc::STATX_BTIME) != 0 { SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64) } else { - Err(io::const_error!( + Err(io::const_io_error!( io::ErrorKind::Unsupported, "creation time is not available for the filesystem", )) @@ -566,7 +567,7 @@ impl FileAttr { } } - Err(io::const_error!( + Err(io::const_io_error!( io::ErrorKind::Unsupported, "creation time is not available on this platform \ currently", @@ -712,7 +713,7 @@ impl Iterator for ReadDir { // thread safety for readdir() as long an individual DIR* is not accessed // concurrently, which is sufficient for Rust. super::os::set_errno(0); - let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0); + let entry_ptr = readdir64(self.inner.dirp.0); if entry_ptr.is_null() { // We either encountered an error, or reached the end. Either way, // the next call to next() should return None. @@ -738,32 +739,44 @@ impl Iterator for ReadDir { // contents were "simply" partially initialized data. // // Like for uninitialized contents, converting entry_ptr to `&dirent64` - // would not be legal. However, we can use `&raw const (*entry_ptr).d_name` - // to refer the fields individually, because that operation is equivalent - // 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. + // would not be legal. However, unique to dirent64 is that we don't even + // get to use `&raw const (*entry_ptr).d_name` because that operation + // requires the full extent of *entry_ptr to be in bounds of the same + // allocation, which is not necessarily the case here. + // + // Instead we must access fields individually through their offsets. + macro_rules! offset_ptr { + ($entry_ptr:expr, $field:ident) => {{ + const OFFSET: isize = mem::offset_of!(dirent64, $field) as isize; + if true { + // Cast to the same type determined by the else branch. + $entry_ptr.byte_offset(OFFSET).cast::<_>() + } else { + #[allow(deref_nullptr)] + { + &raw const (*ptr::null::()).$field + } + } + }}; + } // d_name is guaranteed to be null-terminated. - let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast()); + let name = CStr::from_ptr(offset_ptr!(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_ptr).d_ino as u64, + d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, #[cfg(not(any( target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "nto", )))] - d_type: (*entry_ptr).d_type as u8, + d_type: *offset_ptr!(entry_ptr, d_type) as u8, }; #[cfg(target_os = "vita")] @@ -851,6 +864,7 @@ 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", @@ -881,8 +895,8 @@ impl DirEntry { #[cfg(all( any( all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", target_os = "android", - target_os = "fuchsia", target_os = "hurd" ), not(miri) // no dirfd on Miri @@ -910,8 +924,8 @@ impl DirEntry { #[cfg(any( not(any( all(target_os = "linux", not(target_env = "musl")), + target_os = "emscripten", target_os = "android", - target_os = "fuchsia", target_os = "hurd", )), miri @@ -1215,7 +1229,6 @@ impl File { } #[cfg(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "android", target_os = "netbsd", @@ -1228,7 +1241,6 @@ impl File { } #[cfg(not(any( target_os = "android", - target_os = "fuchsia", target_os = "freebsd", target_os = "linux", target_os = "netbsd", @@ -1244,7 +1256,6 @@ impl File { #[cfg(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1256,18 +1267,16 @@ impl File { #[cfg(not(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn lock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "lock() not supported")) } #[cfg(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1279,18 +1288,16 @@ impl File { #[cfg(not(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn lock_shared(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "lock_shared() not supported")) } #[cfg(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1308,18 +1315,16 @@ impl File { #[cfg(not(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn try_lock(&self) -> io::Result { - Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock() not supported")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "try_lock() not supported")) } #[cfg(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1337,18 +1342,16 @@ impl File { #[cfg(not(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn try_lock_shared(&self) -> io::Result { - Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock_shared() not supported")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "try_lock_shared() not supported")) } #[cfg(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1360,13 +1363,12 @@ impl File { #[cfg(not(any( target_os = "freebsd", - target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", )))] pub fn unlock(&self) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "unlock() not supported")) } pub fn truncate(&self, size: u64) -> io::Result<()> { @@ -1457,11 +1459,11 @@ impl File { )))] let to_timespec = |time: Option| match time { Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!( io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time" )), - Some(_) => Err(io::const_error!( + Some(_) => Err(io::const_io_error!( io::ErrorKind::InvalidInput, "timestamp is too small to set as a file time" )), @@ -1474,7 +1476,7 @@ impl File { // the same as for Redox. // `futimens` and `UTIME_OMIT` are a work in progress for vxworks. let _ = times; - Err(io::const_error!( + Err(io::const_io_error!( io::ErrorKind::Unsupported, "setting file times not supported", )) @@ -1513,7 +1515,7 @@ impl File { weak!(fn futimens(c_int, *const libc::timespec) -> c_int); match futimens.get() { Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()), - None => return Err(io::const_error!( + None => return Err(io::const_io_error!( io::ErrorKind::Unsupported, "setting file times requires Android API level >= 19", )), @@ -1942,7 +1944,7 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> #[cfg(target_os = "espidf")] fn open_to_and_set_permissions( to: &Path, - _reader_metadata: &crate::fs::Metadata, + _reader_metadata: crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::OpenOptions; let writer = OpenOptions::new().open(to)?; @@ -1953,7 +1955,7 @@ fn open_to_and_set_permissions( #[cfg(not(target_os = "espidf"))] fn open_to_and_set_permissions( to: &Path, - reader_metadata: &crate::fs::Metadata, + reader_metadata: crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::OpenOptions; use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt}; @@ -1978,63 +1980,30 @@ fn open_to_and_set_permissions( Ok((writer, writer_metadata)) } -mod cfm { - use crate::fs::{File, Metadata}; - use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write}; - - #[allow(dead_code)] - pub struct CachedFileMetadata(pub File, pub Metadata); +#[cfg(not(any(target_os = "linux", target_os = "android", target_vendor = "apple")))] +pub fn copy(from: &Path, to: &Path) -> io::Result { + let (mut reader, reader_metadata) = open_from(from)?; + let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; - impl Read for CachedFileMetadata { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result { - self.0.read_vectored(bufs) - } - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { - self.0.read_buf(cursor) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> Result { - self.0.read_to_end(buf) - } - fn read_to_string(&mut self, buf: &mut String) -> Result { - self.0.read_to_string(buf) - } - } - impl Write for CachedFileMetadata { - fn write(&mut self, buf: &[u8]) -> Result { - self.0.write(buf) - } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { - self.0.write_vectored(bufs) - } - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - #[inline] - fn flush(&mut self) -> Result<()> { - self.0.flush() - } - } + io::copy(&mut reader, &mut writer) } -#[cfg(any(target_os = "linux", target_os = "android"))] -pub(crate) use cfm::CachedFileMetadata; -#[cfg(not(target_vendor = "apple"))] +#[cfg(any(target_os = "linux", target_os = "android"))] pub fn copy(from: &Path, to: &Path) -> io::Result { - let (reader, reader_metadata) = open_from(from)?; - let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; - - io::copy( - &mut cfm::CachedFileMetadata(reader, reader_metadata), - &mut cfm::CachedFileMetadata(writer, writer_metadata), - ) + let (mut reader, reader_metadata) = open_from(from)?; + let max_len = u64::MAX; + let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; + + use super::kernel_copy::{CopyResult, copy_regular_files}; + + match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) { + CopyResult::Ended(bytes) => Ok(bytes), + CopyResult::Error(e, _) => Err(e), + CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) { + Ok(bytes) => Ok(bytes + written), + Err(e) => Err(e), + }, + } } #[cfg(target_vendor = "apple")] @@ -2071,7 +2040,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { } // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. - let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?; + let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?; // We ensure that `FreeOnDrop` never contains a null pointer so it is // always safe to call `copyfile_state_free` @@ -2121,7 +2090,7 @@ pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { #[cfg(target_os = "vxworks")] pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { let (_, _, _) = (path, uid, gid); - Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) } #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] @@ -2132,7 +2101,7 @@ pub fn chroot(dir: &Path) -> io::Result<()> { #[cfg(target_os = "vxworks")] pub fn chroot(dir: &Path) -> io::Result<()> { let _ = dir; - Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) } pub use remove_dir_impl::remove_dir_all; diff --git a/std/src/sys/pal/unix/io.rs b/std/src/sys/pal/unix/io.rs new file mode 100644 index 0000000000000..0d5a152dc0dc6 --- /dev/null +++ b/std/src/sys/pal/unix/io.rs @@ -0,0 +1,87 @@ +use libc::{c_void, iovec}; + +use crate::marker::PhantomData; +use crate::os::fd::{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(); + unsafe { libc::isatty(fd.as_raw_fd()) != 0 } +} diff --git a/std/src/sys/pal/unix/kernel_copy.rs b/std/src/sys/pal/unix/kernel_copy.rs index bbf29f3252341..a671383cb7957 100644 --- a/std/src/sys/pal/unix/kernel_copy.rs +++ b/std/src/sys/pal/unix/kernel_copy.rs @@ -52,19 +52,19 @@ use crate::cmp::min; use crate::fs::{File, Metadata}; use crate::io::copy::generic_copy; use crate::io::{ - BufRead, BufReader, BufWriter, Error, PipeReader, PipeWriter, Read, Result, StderrLock, - StdinLock, StdoutLock, Take, Write, + BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take, + Write, }; use crate::mem::ManuallyDrop; use crate::net::TcpStream; use crate::os::unix::fs::FileTypeExt; use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use crate::os::unix::net::UnixStream; +use crate::pipe::{PipeReader, PipeWriter}; use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; -use crate::sys::fs::CachedFileMetadata; use crate::sys::weak::syscall; #[cfg(test)] @@ -192,7 +192,7 @@ impl SpecCopy for Copier<'_, '_, R, W> { let w_cfg = writer.properties(); // before direct operations on file descriptors ensure that all source and sink buffers are empty - let mut flush = || -> Result { + let mut flush = || -> crate::io::Result { let bytes = reader.drain_to(writer, u64::MAX)?; // BufWriter buffered bytes have already been accounted for in earlier write() calls writer.flush()?; @@ -537,18 +537,6 @@ impl CopyWrite for BufWriter { } } -impl CopyRead for CachedFileMetadata { - fn properties(&self) -> CopyParams { - CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd())) - } -} - -impl CopyWrite for CachedFileMetadata { - fn properties(&self) -> CopyParams { - CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd())) - } -} - fn fd_to_meta(fd: &T) -> FdMeta { let fd = fd.as_raw_fd(); let file: ManuallyDrop = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) }); diff --git a/std/src/sys/pal/unix/kernel_copy/tests.rs b/std/src/sys/pal/unix/kernel_copy/tests.rs index 54d8f8ed2edd4..1350d743ff6f3 100644 --- a/std/src/sys/pal/unix/kernel_copy/tests.rs +++ b/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::test_helpers::tmpdir; +use crate::sys_common::io::test::tmpdir; #[test] fn copy_specialization() -> Result<()> { diff --git a/std/src/sys/pal/unix/l4re.rs b/std/src/sys/pal/unix/l4re.rs new file mode 100644 index 0000000000000..52d39dcfb16fb --- /dev/null +++ b/std/src/sys/pal/unix/l4re.rs @@ -0,0 +1,564 @@ +macro_rules! unimpl { + () => { + return Err(io::const_io_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 { + unimpl!(); + } + + pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result { + 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 { + unimpl!(); + } + + pub fn duplicate(&self) -> io::Result { + unimpl!(); + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { + unimpl!(); + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + unimpl!(); + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + 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 { + unimpl!(); + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + unimpl!(); + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { + unimpl!(); + } + + pub fn timeout(&self, _: libc::c_int) -> io::Result> { + unimpl!(); + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + unimpl!(); + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn linger(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn nodelay(&self) -> io::Result { + unimpl!(); + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + 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 for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } + } + + impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Socket { + Socket(file_desc) + } + } + + impl IntoInner 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 { + unimpl!(); + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unimpl!(); + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn read_timeout(&self) -> io::Result> { + unimpl!(); + } + + pub fn write_timeout(&self) -> io::Result> { + unimpl!(); + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { + unimpl!(); + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + unimpl!(); + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn write(&self, _: &[u8]) -> io::Result { + unimpl!(); + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + unimpl!(); + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn peer_addr(&self) -> io::Result { + unimpl!(); + } + + pub fn socket_addr(&self) -> io::Result { + unimpl!(); + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + unimpl!(); + } + + pub fn duplicate(&self) -> io::Result { + unimpl!(); + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn linger(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn nodelay(&self) -> io::Result { + unimpl!(); + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn ttl(&self) -> io::Result { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + } + + impl FromInner 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 { + unimpl!(); + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn socket_addr(&self) -> io::Result { + unimpl!(); + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + unimpl!(); + } + + pub fn duplicate(&self) -> io::Result { + unimpl!(); + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn ttl(&self) -> io::Result { + unimpl!(); + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn only_v6(&self) -> io::Result { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + } + + impl FromInner 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 { + unimpl!(); + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn peer_addr(&self) -> io::Result { + unimpl!(); + } + + pub fn socket_addr(&self) -> io::Result { + 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 { + unimpl!(); + } + + pub fn duplicate(&self) -> io::Result { + unimpl!(); + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn read_timeout(&self) -> io::Result> { + unimpl!(); + } + + pub fn write_timeout(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn broadcast(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_loop_v4(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_loop_v6(&self) -> io::Result { + 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 { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unimpl!(); + } + + pub fn send(&self, _: &[u8]) -> io::Result { + unimpl!(); + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + unimpl!(); + } + } + + impl FromInner 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 { + 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 { + unimpl!(); + } + } + + impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unimpl!(); + } + } +} diff --git a/std/src/sys/pal/unix/mod.rs b/std/src/sys/pal/unix/mod.rs index 027df6c5691c6..4fe18daa2040f 100644 --- a/std/src/sys/pal/unix/mod.rs +++ b/std/src/sys/pal/unix/mod.rs @@ -11,16 +11,22 @@ 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; pub mod stack_overflow; pub mod stdio; -pub mod sync; pub mod thread; pub mod thread_parking; pub mod time; @@ -247,7 +253,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ECONNREFUSED => ConnectionRefused, libc::ECONNRESET => ConnectionReset, libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, + libc::EDQUOT => FilesystemQuotaExceeded, libc::EEXIST => AlreadyExists, libc::EFBIG => FileTooLarge, libc::EHOSTUNREACH => HostUnreachable, diff --git a/std/src/sys/net/connection/socket/unix.rs b/std/src/sys/pal/unix/net.rs similarity index 98% rename from std/src/sys/net/connection/socket/unix.rs rename to std/src/sys/pal/unix/net.rs index da6316055273f..6a67bb0a101e9 100644 --- a/std/src/sys/net/connection/socket/unix.rs +++ b/std/src/sys/pal/unix/net.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::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys::pal::IsMinusOne; +use crate::sys::pal::unix::IsMinusOne; +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}; @@ -19,11 +19,11 @@ cfg_if::cfg_if! { } } -pub(crate) use libc as netc; - pub use crate::sys::{cvt, cvt_r}; -#[expect(non_camel_case_types)] +#[allow(unused_extern_crates)] +pub extern crate libc as netc; + pub type wrlen_t = size_t; pub struct Socket(FileDesc); @@ -81,7 +81,6 @@ impl Socket { target_os = "netbsd", target_os = "openbsd", target_os = "nto", - target_os = "solaris", ))] { // On platforms that support it we pass the SOCK_CLOEXEC // flag to atomically create the socket and set it as @@ -191,7 +190,7 @@ impl Socket { loop { let elapsed = start.elapsed(); if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); + return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")); } let timeout = timeout - elapsed; @@ -226,7 +225,7 @@ impl Socket { // for POLLHUP or POLLERR rather than read readiness if pollfd.revents & (libc::POLLHUP | libc::POLLERR) != 0 { let e = self.take_error()?.unwrap_or_else(|| { - io::const_error!( + io::const_io_error!( io::ErrorKind::Uncategorized, "no error set after POLLHUP", ) diff --git a/std/src/sys/pal/unix/os.rs b/std/src/sys/pal/unix/os.rs index b83772e34c173..f207131ddf332 100644 --- a/std/src/sys/pal/unix/os.rs +++ b/std/src/sys/pal/unix/os.rs @@ -258,7 +258,7 @@ pub fn current_exe() -> io::Result { use crate::env; use crate::io::ErrorKind; - let exe_path = env::args().next().ok_or(io::const_error!( + let exe_path = env::args().next().ok_or(io::const_io_error!( ErrorKind::NotFound, "an executable path was not found because no arguments were provided through argv" ))?; @@ -284,7 +284,7 @@ pub fn current_exe() -> io::Result { } } } - Err(io::const_error!(ErrorKind::NotFound, "an executable path was not found")) + Err(io::const_io_error!(ErrorKind::NotFound, "an executable path was not found")) } #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] @@ -340,7 +340,7 @@ pub fn current_exe() -> io::Result { 0, ))?; if path_len <= 1 { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::Uncategorized, "KERN_PROC_PATHNAME sysctl returned zero-length string", )); @@ -363,7 +363,7 @@ pub fn current_exe() -> io::Result { if curproc_exe.is_file() { return crate::fs::read_link(curproc_exe); } - Err(io::const_error!( + Err(io::const_io_error!( io::ErrorKind::Uncategorized, "/proc/curproc/exe doesn't point to regular file.", )) @@ -382,9 +382,10 @@ pub fn current_exe() -> io::Result { cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?; argv.set_len(argv_len as usize); if argv[0].is_null() { - return Err( - io::const_error!(io::ErrorKind::Uncategorized, "no current exe available",), - ); + return Err(io::const_io_error!( + io::ErrorKind::Uncategorized, + "no current exe available", + )); } let argv0 = CStr::from_ptr(argv[0]).to_bytes(); if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') { @@ -404,7 +405,7 @@ pub fn current_exe() -> io::Result { ))] pub fn current_exe() -> io::Result { match crate::fs::read_link("/proc/self/exe") { - Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_error!( + Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_io_error!( io::ErrorKind::Uncategorized, "no /proc/self/exe available. Is /proc mounted?", )), @@ -427,13 +428,11 @@ pub fn current_exe() -> io::Result { pub fn current_exe() -> io::Result { unsafe { let mut sz: u32 = 0; - #[expect(deprecated)] libc::_NSGetExecutablePath(ptr::null_mut(), &mut sz); if sz == 0 { return Err(io::Error::last_os_error()); } let mut v: Vec = Vec::with_capacity(sz as usize); - #[expect(deprecated)] let err = libc::_NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); if err != 0 { return Err(io::Error::last_os_error()); @@ -477,7 +476,7 @@ pub fn current_exe() -> io::Result { ); if result != libc::B_OK { use crate::io::ErrorKind; - Err(io::const_error!(ErrorKind::Uncategorized, "Error getting executable path")) + Err(io::const_io_error!(ErrorKind::Uncategorized, "Error getting executable path")) } else { // find_path adds the null terminator. let name = CStr::from_ptr(name.as_ptr()).to_bytes(); @@ -494,7 +493,7 @@ pub fn current_exe() -> io::Result { #[cfg(target_os = "l4re")] pub fn current_exe() -> io::Result { use crate::io::ErrorKind; - Err(io::const_error!(ErrorKind::Unsupported, "Not yet implemented!")) + Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!")) } #[cfg(target_os = "vxworks")] @@ -524,7 +523,7 @@ pub fn current_exe() -> io::Result { use crate::env; use crate::io::ErrorKind; - let exe_path = env::args().next().ok_or(io::const_error!( + let exe_path = env::args().next().ok_or(io::const_io_error!( ErrorKind::Uncategorized, "an executable path was not found because no arguments were provided through argv" ))?; diff --git a/std/src/sys/pal/unix/process/process_common.rs b/std/src/sys/pal/unix/process/process_common.rs index 342818ac91183..13290fed762ae 100644 --- a/std/src/sys/pal/unix/process/process_common.rs +++ b/std/src/sys/pal/unix/process/process_common.rs @@ -393,7 +393,7 @@ impl Command { fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { CString::new(s.as_bytes()).unwrap_or_else(|_e| { *saw_nul = true; - c"".to_owned() + CString::new("").unwrap() }) } diff --git a/std/src/sys/pal/unix/process/process_fuchsia.rs b/std/src/sys/pal/unix/process/process_fuchsia.rs index b7a35718757ae..8f7d786e32fcd 100644 --- a/std/src/sys/pal/unix/process/process_fuchsia.rs +++ b/std/src/sys/pal/unix/process/process_fuchsia.rs @@ -18,7 +18,7 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "nul byte found in provided data", )); @@ -38,7 +38,7 @@ impl Command { pub fn exec(&mut self, default: Stdio) -> io::Error { if self.saw_nul() { - return io::const_error!( + return io::const_io_error!( io::ErrorKind::InvalidInput, "nul byte found in provided data", ); @@ -185,7 +185,7 @@ impl Process { ))?; } if actual != 1 { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, "Failed to get exit status of process", )); @@ -222,7 +222,7 @@ impl Process { ))?; } if actual != 1 { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, "Failed to get exit status of process", )); diff --git a/std/src/sys/pal/unix/process/process_unix.rs b/std/src/sys/pal/unix/process/process_unix.rs index 2bff192a5bd83..8faf1fda5464d 100644 --- a/std/src/sys/pal/unix/process/process_unix.rs +++ b/std/src/sys/pal/unix/process/process_unix.rs @@ -19,7 +19,8 @@ use crate::sys::process::process_common::*; use crate::{fmt, mem, sys}; cfg_if::cfg_if! { - if #[cfg(target_os = "nto")] { + // 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"))] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; @@ -60,7 +61,7 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::const_error!( + return Err(io::const_io_error!( ErrorKind::InvalidInput, "nul byte found in provided data", )); @@ -174,7 +175,7 @@ impl Command { // allowed to exist in dead code), but it sounds bad, so we go out of our // way to avoid that all-together. #[cfg(any(target_os = "tvos", target_os = "watchos"))] - const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_error!( + const ERR_APPLE_TV_WATCH_NO_FORK_EXEC: Error = io::const_io_error!( ErrorKind::Unsupported, "`fork`+`exec`-based process spawning is not supported on this target", ); @@ -186,7 +187,12 @@ 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_os = "nto")))] + #[cfg(not(any( + target_os = "watchos", + target_os = "tvos", + target_env = "nto70", + target_env = "nto71" + )))] unsafe fn do_fork(&mut self) -> Result { cvt(libc::fork()) } @@ -195,7 +201,8 @@ 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 - #[cfg(target_os = "nto")] + // 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"))] unsafe fn do_fork(&mut self) -> Result { use crate::sys::os::errno; @@ -211,7 +218,7 @@ impl Command { } else if delay < MAX_FORKSPAWN_SLEEP { thread::sleep(delay); } else { - return Err(io::const_error!( + return Err(io::const_io_error!( ErrorKind::WouldBlock, "forking returned EBADF too often", )); @@ -228,7 +235,7 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data",); + return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",); } match self.setup_io(default, true) { @@ -554,7 +561,7 @@ impl Command { } else if delay < MAX_FORKSPAWN_SLEEP { thread::sleep(delay); } else { - return Err(io::const_error!( + return Err(io::const_io_error!( ErrorKind::WouldBlock, "posix_spawnp returned EBADF too often", )); diff --git a/std/src/sys/pal/unix/process/process_vxworks.rs b/std/src/sys/pal/unix/process/process_vxworks.rs index e2c1b6a032624..38daf6af91808 100644 --- a/std/src/sys/pal/unix/process/process_vxworks.rs +++ b/std/src/sys/pal/unix/process/process_vxworks.rs @@ -22,7 +22,7 @@ impl Command { let envp = self.capture_env(); if self.saw_nul() { - return Err(io::const_error!( + return Err(io::const_io_error!( ErrorKind::InvalidInput, "nul byte found in provided data", )); diff --git a/std/src/sys/pal/unix/stack_overflow.rs b/std/src/sys/pal/unix/stack_overflow.rs index db5c6bd3a1c32..69b31da427fcb 100644 --- a/std/src/sys/pal/unix/stack_overflow.rs +++ b/std/src/sys/pal/unix/stack_overflow.rs @@ -100,11 +100,10 @@ mod imp { // If the faulting address is within the guard page, then we print a // message saying so and abort. if start <= addr && addr < end { - thread::with_current_name(|name| { - let name = name.unwrap_or(""); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); - }); - + rtprintpanic!( + "\nthread '{}' has overflowed its stack\n", + thread::current().name().unwrap_or("") + ); rtabort!("stack overflow"); } else { // Unregister ourselves by reverting back to the default behavior. diff --git a/std/src/sys/pal/unix/stdio.rs b/std/src/sys/pal/unix/stdio.rs index 8c2f61a40de3b..97e75f1b5b669 100644 --- a/std/src/sys/pal/unix/stdio.rs +++ b/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::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { Some(Stderr::new()) diff --git a/std/src/sys/pal/unix/sync/condvar.rs b/std/src/sys/pal/unix/sync/condvar.rs deleted file mode 100644 index 73631053e9f47..0000000000000 --- a/std/src/sys/pal/unix/sync/condvar.rs +++ /dev/null @@ -1,172 +0,0 @@ -use super::Mutex; -use crate::cell::UnsafeCell; -use crate::pin::Pin; -#[cfg(not(target_os = "nto"))] -use crate::sys::pal::time::TIMESPEC_MAX; -#[cfg(target_os = "nto")] -use crate::sys::pal::time::TIMESPEC_MAX_CAPPED; -use crate::sys::pal::time::Timespec; -use crate::time::Duration; - -pub struct Condvar { - inner: UnsafeCell, -} - -impl Condvar { - pub fn new() -> Condvar { - Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) } - } - - #[inline] - fn raw(&self) -> *mut libc::pthread_cond_t { - self.inner.get() - } - - /// # Safety - /// `init` must have been called on this instance. - #[inline] - pub unsafe fn notify_one(self: Pin<&Self>) { - let r = unsafe { libc::pthread_cond_signal(self.raw()) }; - debug_assert_eq!(r, 0); - } - - /// # Safety - /// `init` must have been called on this instance. - #[inline] - pub unsafe fn notify_all(self: Pin<&Self>) { - let r = unsafe { libc::pthread_cond_broadcast(self.raw()) }; - debug_assert_eq!(r, 0); - } - - /// # Safety - /// * `init` must have been called on this instance. - /// * `mutex` must be locked by the current thread. - /// * This condition variable may only be used with the same mutex. - #[inline] - pub unsafe fn wait(self: Pin<&Self>, mutex: Pin<&Mutex>) { - let r = unsafe { libc::pthread_cond_wait(self.raw(), mutex.raw()) }; - debug_assert_eq!(r, 0); - } - - /// # Safety - /// * `init` must have been called on this instance. - /// * `mutex` must be locked by the current thread. - /// * This condition variable may only be used with the same mutex. - pub unsafe fn wait_timeout(&self, mutex: Pin<&Mutex>, dur: Duration) -> bool { - let mutex = mutex.raw(); - - // OSX implementation of `pthread_cond_timedwait` is buggy - // with super long durations. When duration is greater than - // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` - // in macOS Sierra returns error 316. - // - // This program demonstrates the issue: - // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c - // - // To work around this issue, the timeout is clamped to 1000 years. - #[cfg(target_vendor = "apple")] - let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); - - let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur); - - #[cfg(not(target_os = "nto"))] - let timeout = timeout.and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); - - #[cfg(target_os = "nto")] - let timeout = timeout.and_then(|t| t.to_timespec_capped()).unwrap_or(TIMESPEC_MAX_CAPPED); - - let r = unsafe { libc::pthread_cond_timedwait(self.raw(), mutex, &timeout) }; - assert!(r == libc::ETIMEDOUT || r == 0); - r == 0 - } -} - -#[cfg(not(any( - target_os = "android", - target_vendor = "apple", - target_os = "espidf", - target_os = "horizon", - target_os = "l4re", - target_os = "redox", - target_os = "teeos", -)))] -impl Condvar { - pub const PRECISE_TIMEOUT: bool = true; - const CLOCK: libc::clockid_t = libc::CLOCK_MONOTONIC; - - /// # Safety - /// May only be called once per instance of `Self`. - pub unsafe fn init(self: Pin<&mut Self>) { - use crate::mem::MaybeUninit; - - struct AttrGuard<'a>(pub &'a mut MaybeUninit); - impl Drop for AttrGuard<'_> { - fn drop(&mut self) { - unsafe { - let result = libc::pthread_condattr_destroy(self.0.as_mut_ptr()); - assert_eq!(result, 0); - } - } - } - - unsafe { - let mut attr = MaybeUninit::::uninit(); - let r = libc::pthread_condattr_init(attr.as_mut_ptr()); - assert_eq!(r, 0); - let attr = AttrGuard(&mut attr); - let r = libc::pthread_condattr_setclock(attr.0.as_mut_ptr(), Self::CLOCK); - assert_eq!(r, 0); - let r = libc::pthread_cond_init(self.raw(), attr.0.as_ptr()); - assert_eq!(r, 0); - } - } -} - -// `pthread_condattr_setclock` is unfortunately not supported on these platforms. -#[cfg(any( - target_os = "android", - target_vendor = "apple", - target_os = "espidf", - target_os = "horizon", - target_os = "l4re", - target_os = "redox", - target_os = "teeos", -))] -impl Condvar { - pub const PRECISE_TIMEOUT: bool = false; - const CLOCK: libc::clockid_t = libc::CLOCK_REALTIME; - - /// # Safety - /// May only be called once per instance of `Self`. - pub unsafe fn init(self: Pin<&mut Self>) { - if cfg!(any(target_os = "espidf", target_os = "horizon", target_os = "teeos")) { - // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet - // So on that platform, init() should always be called. - // - // Similar story for the 3DS (horizon) and for TEEOS. - let r = unsafe { libc::pthread_cond_init(self.raw(), crate::ptr::null()) }; - assert_eq!(r, 0); - } - } -} - -impl !Unpin for Condvar {} - -unsafe impl Sync for Condvar {} -unsafe impl Send for Condvar {} - -impl Drop for Condvar { - #[inline] - fn drop(&mut self) { - let r = unsafe { libc::pthread_cond_destroy(self.raw()) }; - if cfg!(target_os = "dragonfly") { - // On DragonFly pthread_cond_destroy() returns EINVAL if called on - // a condvar that was just initialized with - // libc::PTHREAD_COND_INITIALIZER. Once it is used or - // pthread_cond_init() is called, this behaviour no longer occurs. - debug_assert!(r == 0 || r == libc::EINVAL); - } else { - debug_assert_eq!(r, 0); - } - } -} diff --git a/std/src/sys/pal/unix/sync/mod.rs b/std/src/sys/pal/unix/sync/mod.rs deleted file mode 100644 index b430ff5d8ef5f..0000000000000 --- a/std/src/sys/pal/unix/sync/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![cfg(not(any( - target_os = "linux", - target_os = "android", - all(target_os = "emscripten", target_feature = "atomics"), - target_os = "freebsd", - target_os = "openbsd", - target_os = "dragonfly", - target_os = "fuchsia", -)))] -#![forbid(unsafe_op_in_unsafe_fn)] - -mod condvar; -mod mutex; - -pub use condvar::Condvar; -pub use mutex::Mutex; diff --git a/std/src/sys/pal/unix/sync/mutex.rs b/std/src/sys/pal/unix/sync/mutex.rs deleted file mode 100644 index 8ff6c3d3d15da..0000000000000 --- a/std/src/sys/pal/unix/sync/mutex.rs +++ /dev/null @@ -1,135 +0,0 @@ -use super::super::cvt_nz; -use crate::cell::UnsafeCell; -use crate::io::Error; -use crate::mem::MaybeUninit; -use crate::pin::Pin; - -pub struct Mutex { - inner: UnsafeCell, -} - -impl Mutex { - pub fn new() -> Mutex { - Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } - } - - pub(super) fn raw(&self) -> *mut libc::pthread_mutex_t { - self.inner.get() - } - - /// # Safety - /// May only be called once per instance of `Self`. - pub unsafe fn init(self: Pin<&mut Self>) { - // Issue #33770 - // - // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have - // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you - // try to re-lock it from the same thread when you already hold a lock - // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). - // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL - // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that - // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same - // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in - // a Mutex where re-locking is UB. - // - // In practice, glibc takes advantage of this undefined behavior to - // implement hardware lock elision, which uses hardware transactional - // memory to avoid acquiring the lock. While a transaction is in - // progress, the lock appears to be unlocked. This isn't a problem for - // other threads since the transactional memory will abort if a conflict - // is detected, however no abort is generated when re-locking from the - // same thread. - // - // Since locking the same mutex twice will result in two aliasing &mut - // references, we instead create the mutex with type - // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to - // re-lock it from the same thread, thus avoiding undefined behavior. - unsafe { - let mut attr = MaybeUninit::::uninit(); - cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); - let attr = AttrGuard(&mut attr); - cvt_nz(libc::pthread_mutexattr_settype( - attr.0.as_mut_ptr(), - libc::PTHREAD_MUTEX_NORMAL, - )) - .unwrap(); - cvt_nz(libc::pthread_mutex_init(self.raw(), attr.0.as_ptr())).unwrap(); - } - } - - /// # Safety - /// * If `init` was not called on this instance, reentrant locking causes - /// undefined behaviour. - /// * Destroying a locked mutex causes undefined behaviour. - pub unsafe fn lock(self: Pin<&Self>) { - #[cold] - #[inline(never)] - fn fail(r: i32) -> ! { - let error = Error::from_raw_os_error(r); - panic!("failed to lock mutex: {error}"); - } - - let r = unsafe { libc::pthread_mutex_lock(self.raw()) }; - // As we set the mutex type to `PTHREAD_MUTEX_NORMAL` above, we expect - // the lock call to never fail. Unfortunately however, some platforms - // (Solaris) do not conform to the standard, and instead always provide - // deadlock detection. How kind of them! Unfortunately that means that - // we need to check the error code here. To save us from UB on other - // less well-behaved platforms in the future, we do it even on "good" - // platforms like macOS. See #120147 for more context. - if r != 0 { - fail(r) - } - } - - /// # Safety - /// * If `init` was not called on this instance, reentrant locking causes - /// undefined behaviour. - /// * Destroying a locked mutex causes undefined behaviour. - pub unsafe fn try_lock(self: Pin<&Self>) -> bool { - unsafe { libc::pthread_mutex_trylock(self.raw()) == 0 } - } - - /// # Safety - /// The mutex must be locked by the current thread. - pub unsafe fn unlock(self: Pin<&Self>) { - let r = unsafe { libc::pthread_mutex_unlock(self.raw()) }; - debug_assert_eq!(r, 0); - } -} - -impl !Unpin for Mutex {} - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -impl Drop for Mutex { - fn drop(&mut self) { - // SAFETY: - // If `lock` or `init` was called, the mutex must have been pinned, so - // it is still at the same location. Otherwise, `inner` must contain - // `PTHREAD_MUTEX_INITIALIZER`, which is valid at all locations. Thus, - // this call always destroys a valid mutex. - let r = unsafe { libc::pthread_mutex_destroy(self.raw()) }; - if cfg!(target_os = "dragonfly") { - // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a - // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. - // Once it is used (locked/unlocked) or pthread_mutex_init() is called, - // this behaviour no longer occurs. - debug_assert!(r == 0 || r == libc::EINVAL); - } else { - debug_assert_eq!(r, 0); - } - } -} - -struct AttrGuard<'a>(pub &'a mut MaybeUninit); - -impl Drop for AttrGuard<'_> { - fn drop(&mut self) { - unsafe { - let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr()); - assert_eq!(result, 0); - } - } -} diff --git a/std/src/sys/pal/unix/thread.rs b/std/src/sys/pal/unix/thread.rs index 356669980c7d9..040246618360f 100644 --- a/std/src/sys/pal/unix/thread.rs +++ b/std/src/sys/pal/unix/thread.rs @@ -45,7 +45,6 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn new(stack: usize, p: Box) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = mem::zeroed(); @@ -130,32 +129,25 @@ impl Thread { } } - #[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "nuttx" - ))] + #[cfg(target_os = "linux")] pub fn set_name(name: &CStr) { + const TASK_COMM_LEN: usize = 16; + unsafe { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - // Linux limits the allowed length of the name. - const TASK_COMM_LEN: usize = 16; - let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); - } else { - // 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, - // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. + // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20. + let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. debug_assert_eq!(res, 0); } } - #[cfg(target_os = "openbsd")] + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "nuttx" + ))] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); @@ -477,7 +469,7 @@ pub fn available_parallelism() -> io::Result> { unsafe { use libc::_syspage_ptr; if _syspage_ptr.is_null() { - Err(io::const_error!(io::ErrorKind::NotFound, "No syspage available")) + Err(io::const_io_error!(io::ErrorKind::NotFound, "No syspage available")) } else { let cpus = (*_syspage_ptr).num_cpu; NonZero::new(cpus as usize) @@ -517,7 +509,7 @@ pub fn available_parallelism() -> io::Result> { } } else { // FIXME: implement on Redox, l4re - Err(io::const_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform")) } } } diff --git a/std/src/sys/pal/unix/time.rs b/std/src/sys/pal/unix/time.rs index e224980e95f31..535fe6b27d91e 100644 --- a/std/src/sys/pal/unix/time.rs +++ b/std/src/sys/pal/unix/time.rs @@ -1,5 +1,3 @@ -use core::num::niche_types::Nanoseconds; - use crate::time::Duration; use crate::{fmt, io}; @@ -17,6 +15,12 @@ pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, }; +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +#[rustc_layout_scalar_valid_range_end(999_999_999)] +struct Nanoseconds(u32); + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SystemTime { pub(crate) t: Timespec, @@ -55,14 +59,14 @@ impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec) + .field("tv_nsec", &self.t.tv_nsec.0) .finish() } } impl Timespec { const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec { - Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } } + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } } pub const fn zero() -> Timespec { @@ -92,7 +96,7 @@ impl Timespec { if tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64 { Ok(unsafe { Self::new_unchecked(tv_sec, tv_nsec) }) } else { - Err(io::const_error!(io::ErrorKind::InvalidData, "Invalid timestamp")) + Err(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid timestamp")) } } @@ -143,15 +147,12 @@ impl Timespec { // // Ideally this code could be rearranged such that it more // directly expresses the lower-cost behavior we want from it. - let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() { - ( - (self.tv_sec - other.tv_sec) as u64, - self.tv_nsec.as_inner() - other.tv_nsec.as_inner(), - ) + let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 { + ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) } else { ( (self.tv_sec - other.tv_sec - 1) as u64, - self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(), + self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, ) }; @@ -169,7 +170,7 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.tv_nsec.as_inner(); + let mut nsec = other.subsec_nanos() + self.tv_nsec.0; if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; @@ -181,7 +182,7 @@ impl Timespec { let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. - let mut nsec = self.tv_nsec.as_inner() as i32 - other.subsec_nanos() as i32; + let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; @@ -193,7 +194,7 @@ impl Timespec { pub fn to_timespec(&self) -> Option { Some(libc::timespec { tv_sec: self.tv_sec.try_into().ok()?, - tv_nsec: self.tv_nsec.as_inner().try_into().ok()?, + tv_nsec: self.tv_nsec.0.try_into().ok()?, }) } @@ -202,7 +203,7 @@ impl Timespec { #[cfg(target_os = "nto")] pub(in crate::sys) fn to_timespec_capped(&self) -> Option { // Check if timeout in nanoseconds would fit into an u64 - if (self.tv_nsec.as_inner() as u64) + if (self.tv_nsec.0 as u64) .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) .is_none() { @@ -218,7 +219,7 @@ impl Timespec { not(target_arch = "riscv32") ))] pub fn to_timespec64(&self) -> __timespec64 { - __timespec64::new(self.tv_sec, self.tv_nsec.as_inner() as _) + __timespec64::new(self.tv_sec, self.tv_nsec.0 as _) } } @@ -292,7 +293,7 @@ impl fmt::Debug for Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec) + .field("tv_nsec", &self.t.tv_nsec.0) .finish() } } diff --git a/std/src/sys/io/io_slice/unsupported.rs b/std/src/sys/pal/unsupported/io.rs similarity index 94% rename from std/src/sys/io/io_slice/unsupported.rs rename to std/src/sys/pal/unsupported/io.rs index 1572cac6cd771..604735d32d51a 100644 --- a/std/src/sys/io/io_slice/unsupported.rs +++ b/std/src/sys/pal/unsupported/io.rs @@ -50,3 +50,7 @@ impl<'a> IoSliceMut<'a> { self.0 } } + +pub fn is_terminal(_: &T) -> bool { + false +} diff --git a/std/src/sys/pal/unsupported/mod.rs b/std/src/sys/pal/unsupported/mod.rs index b1aaeb1b4c814..01d516f7568bf 100644 --- a/std/src/sys/pal/unsupported/mod.rs +++ b/std/src/sys/pal/unsupported/mod.rs @@ -3,6 +3,8 @@ 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/std/src/sys/net/connection/uefi/mod.rs b/std/src/sys/pal/unsupported/net.rs similarity index 100% rename from std/src/sys/net/connection/uefi/mod.rs rename to std/src/sys/pal/unsupported/net.rs diff --git a/std/src/sys/pal/unsupported/os.rs b/std/src/sys/pal/unsupported/os.rs index 48de4312885fe..481fd62c04fe8 100644 --- a/std/src/sys/pal/unsupported/os.rs +++ b/std/src/sys/pal/unsupported/os.rs @@ -96,11 +96,11 @@ pub fn getenv(_: &OsStr) -> Option { } pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } pub fn temp_dir() -> PathBuf { diff --git a/std/src/sys/pal/wasi/fs.rs b/std/src/sys/pal/wasi/fs.rs index 7779d2b97d7f9..3296c762cca2b 100644 --- a/std/src/sys/pal/wasi/fs.rs +++ b/std/src/sys/pal/wasi/fs.rs @@ -27,27 +27,10 @@ pub struct FileAttr { pub struct ReadDir { inner: Arc, - state: ReadDirState, -} - -enum ReadDirState { - /// Fill `buf` with `buf.len()` bytes starting from `next_read_offset`. - FillBuffer { - next_read_offset: wasi::Dircookie, - buf: Vec, - }, - ProcessEntry { - buf: Vec, - next_read_offset: Option, - offset: usize, - }, - /// There is no more data to get in [`Self::FillBuffer`]; keep returning - /// entries via ProcessEntry until `buf` is exhausted. - RunUntilExhaustion { - buf: Vec, - offset: usize, - }, - Done, + cookie: Option, + buf: Vec, + offset: usize, + cap: usize, } struct ReadDirInner { @@ -164,8 +147,11 @@ impl FileType { impl ReadDir { fn new(dir: File, root: PathBuf) -> ReadDir { ReadDir { + cookie: Some(0), + buf: vec![0; 128], + offset: 0, + cap: 0, inner: Arc::new(ReadDirInner { dir, root }), - state: ReadDirState::FillBuffer { next_read_offset: 0, buf: vec![0; 128] }, } } } @@ -176,99 +162,78 @@ impl fmt::Debug for ReadDir { } } -impl core::iter::FusedIterator for ReadDir {} - impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - match &mut self.state { - ReadDirState::FillBuffer { next_read_offset, ref mut buf } => { - let result = self.inner.dir.fd.readdir(buf, *next_read_offset); - match result { - Ok(read_bytes) => { - if read_bytes < buf.len() { - buf.truncate(read_bytes); - self.state = - ReadDirState::RunUntilExhaustion { buf: mem::take(buf), offset: 0 }; - } else { - debug_assert_eq!(read_bytes, buf.len()); - self.state = ReadDirState::ProcessEntry { - buf: mem::take(buf), - offset: 0, - next_read_offset: Some(*next_read_offset), - }; - } - self.next() - } - Err(e) => { - self.state = ReadDirState::Done; - return Some(Err(e)); - } + loop { + // If we've reached the capacity of our buffer then we need to read + // some more from the OS, otherwise we pick up at our old offset. + let offset = if self.offset == self.cap { + let cookie = self.cookie.take()?; + match self.inner.dir.fd.readdir(&mut self.buf, cookie) { + Ok(bytes) => self.cap = bytes, + Err(e) => return Some(Err(e)), } - } - ReadDirState::ProcessEntry { ref mut buf, next_read_offset, offset } => { - let contents = &buf[*offset..]; - const DIRENT_SIZE: usize = crate::mem::size_of::(); - if contents.len() >= DIRENT_SIZE { - let (dirent, data) = contents.split_at(DIRENT_SIZE); - let dirent = - unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; - // If the file name was truncated, then we need to reinvoke - // `readdir` so we truncate our buffer to start over and reread this - // descriptor. - if data.len() < dirent.d_namlen as usize { - if buf.len() < dirent.d_namlen as usize + DIRENT_SIZE { - buf.resize(dirent.d_namlen as usize + DIRENT_SIZE, 0); - } - if let Some(next_read_offset) = *next_read_offset { - self.state = - ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; - } else { - self.state = ReadDirState::Done; - } - - return self.next(); - } - next_read_offset.as_mut().map(|cookie| { - *cookie = dirent.d_next; - }); - *offset = *offset + DIRENT_SIZE + dirent.d_namlen as usize; - - let name = &data[..(dirent.d_namlen as usize)]; - - // These names are skipped on all other platforms, so let's skip - // them here too - if name == b"." || name == b".." { - return self.next(); - } - - return Some(Ok(DirEntry { - meta: dirent, - name: name.to_vec(), - inner: self.inner.clone(), - })); - } else if let Some(next_read_offset) = *next_read_offset { - self.state = ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; - } else { - self.state = ReadDirState::Done; + self.offset = 0; + self.cookie = Some(cookie); + + // If we didn't actually read anything, this is in theory the + // end of the directory. + if self.cap == 0 { + self.cookie = None; + return None; } - self.next() + + 0 + } else { + self.offset + }; + let data = &self.buf[offset..self.cap]; + + // If we're not able to read a directory entry then that means it + // must have been truncated at the end of the buffer, so reset our + // offset so we can go back and reread into the buffer, picking up + // where we last left off. + let dirent_size = mem::size_of::(); + if data.len() < dirent_size { + assert!(self.cookie.is_some()); + assert!(self.buf.len() >= dirent_size); + self.offset = self.cap; + continue; } - ReadDirState::RunUntilExhaustion { buf, offset } => { - if *offset >= buf.len() { - self.state = ReadDirState::Done; - } else { - self.state = ReadDirState::ProcessEntry { - buf: mem::take(buf), - offset: *offset, - next_read_offset: None, - }; + let (dirent, data) = data.split_at(dirent_size); + let dirent = unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; + + // If the file name was truncated, then we need to reinvoke + // `readdir` so we truncate our buffer to start over and reread this + // descriptor. Note that if our offset is 0 that means the file name + // is massive and we need a bigger buffer. + if data.len() < dirent.d_namlen as usize { + if offset == 0 { + let amt_to_add = self.buf.capacity(); + self.buf.extend(iter::repeat(0).take(amt_to_add)); } + assert!(self.cookie.is_some()); + self.offset = self.cap; + continue; + } + self.cookie = Some(dirent.d_next); + self.offset = offset + dirent_size + dirent.d_namlen as usize; + + let name = &data[..(dirent.d_namlen as usize)]; - self.next() + // These names are skipped on all other platforms, so let's skip + // them here too + if name == b"." || name == b".." { + continue; } - ReadDirState::Done => None, + + return Some(Ok(DirEntry { + meta: dirent, + name: name.to_vec(), + inner: self.inner.clone(), + })); } } } @@ -531,7 +496,7 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { let to_timestamp = |time: Option| match time { Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_error!( + Some(_) => Err(io::const_io_error!( io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time" )), @@ -799,7 +764,8 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { } pub fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) + f.to_str() + .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) } pub fn copy(from: &Path, to: &Path) -> io::Result { @@ -845,7 +811,7 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { for entry in ReadDir::new(fd, dummy_root) { let entry = entry?; let path = crate::str::from_utf8(&entry.name).map_err(|_| { - io::const_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") + io::const_io_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") })?; let result: io::Result<()> = try { diff --git a/std/src/sys/io/io_slice/wasi.rs b/std/src/sys/pal/wasi/io.rs similarity index 90% rename from std/src/sys/io/io_slice/wasi.rs rename to std/src/sys/pal/wasi/io.rs index 87acbbd924e56..57f81bc6257cd 100644 --- a/std/src/sys/io/io_slice/wasi.rs +++ b/std/src/sys/pal/wasi/io.rs @@ -1,4 +1,7 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + use crate::marker::PhantomData; +use crate::os::fd::{AsFd, AsRawFd}; use crate::slice; #[derive(Copy, Clone)] @@ -74,3 +77,8 @@ 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/std/src/sys/pal/wasi/mod.rs b/std/src/sys/pal/wasi/mod.rs index f4588a60ea9a4..5d54c7903065c 100644 --- a/std/src/sys/pal/wasi/mod.rs +++ b/std/src/sys/pal/wasi/mod.rs @@ -1,7 +1,8 @@ //! System bindings for the wasm/web platform //! //! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for wasm. +//! OS level functionality for wasm. Note that this wasm is *not* the emscripten +//! 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 @@ -20,7 +21,9 @@ 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; @@ -43,4 +46,5 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; +use helpers::err2io; +pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; diff --git a/std/src/sys/net/connection/wasip1.rs b/std/src/sys/pal/wasi/net.rs similarity index 99% rename from std/src/sys/net/connection/wasip1.rs rename to std/src/sys/pal/wasi/net.rs index 27e3a528af497..a648679982812 100644 --- a/std/src/sys/net/connection/wasip1.rs +++ b/std/src/sys/pal/wasi/net.rs @@ -1,11 +1,12 @@ #![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::fd::WasiFd; -use crate::sys::{err2io, unsupported}; +use crate::sys::unsupported; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; diff --git a/std/src/sys/pal/wasi/stdio.rs b/std/src/sys/pal/wasi/stdio.rs index d08b772e5fc7d..ca49f871e1957 100644 --- a/std/src/sys/pal/wasi/stdio.rs +++ b/std/src/sys/pal/wasi/stdio.rs @@ -101,7 +101,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys_common::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/std/src/sys/pal/wasi/thread.rs b/std/src/sys/pal/wasi/thread.rs index f5e19f26bfe17..4b83870fdea6c 100644 --- a/std/src/sys/pal/wasi/thread.rs +++ b/std/src/sys/pal/wasi/thread.rs @@ -2,6 +2,7 @@ use crate::ffi::CStr; use crate::num::NonZero; +use crate::sys::unsupported; use crate::time::Duration; use crate::{io, mem}; @@ -33,8 +34,6 @@ cfg_if::cfg_if! { #[allow(non_camel_case_types)] pub type pthread_t = *mut ffi::c_void; - pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; - extern "C" { pub fn pthread_create( native: *mut pthread_t, @@ -122,7 +121,7 @@ impl Thread { } } else { pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { - crate::sys::unsupported() + unsupported() } } } @@ -188,14 +187,5 @@ impl Thread { } pub fn available_parallelism() -> io::Result> { - cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { - match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { - -1 => Err(io::Error::last_os_error()), - cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), - } - } else { - crate::sys::unsupported() - } - } + unsupported() } diff --git a/std/src/sys/pal/wasip2/mod.rs b/std/src/sys/pal/wasip2/mod.rs index 72c9742b2e549..320712fdcc9fe 100644 --- a/std/src/sys/pal/wasip2/mod.rs +++ b/std/src/sys/pal/wasip2/mod.rs @@ -17,7 +17,10 @@ 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/std/src/sys/net/connection/socket/wasip2.rs b/std/src/sys/pal/wasip2/net.rs similarity index 98% rename from std/src/sys/net/connection/socket/wasip2.rs rename to std/src/sys/pal/wasip2/net.rs index 9d1c05a473e4d..06e623df8438e 100644 --- a/std/src/sys/net/connection/socket/wasip2.rs +++ b/std/src/sys/pal/wasip2/net.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}; @@ -117,7 +117,7 @@ impl Socket { loop { let elapsed = start.elapsed(); if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); + return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")); } let timeout = timeout - elapsed; diff --git a/std/src/sys/pal/wasm/mod.rs b/std/src/sys/pal/wasm/mod.rs index 32d59c4d0f7c4..8141bfac49aad 100644 --- a/std/src/sys/pal/wasm/mod.rs +++ b/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 -//! or wasi wasm, so we have no runtime here. +//! 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,6 +21,10 @@ 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/std/src/sys/pal/windows/args.rs b/std/src/sys/pal/windows/args.rs index 3447a0157e4c5..e9fc19bcb99c1 100644 --- a/std/src/sys/pal/windows/args.rs +++ b/std/src/sys/pal/windows/args.rs @@ -327,7 +327,7 @@ pub(crate) fn make_bat_command_line( force_quotes: bool, ) -> io::Result> { const INVALID_ARGUMENT_ERROR: io::Error = - io::const_error!(io::ErrorKind::InvalidInput, r#"batch file arguments are invalid"#); + io::const_io_error!(io::ErrorKind::InvalidInput, r#"batch file arguments are invalid"#); // Set the start of the command line to `cmd.exe /c "` // It is necessary to surround the command in an extra pair of quotes, // hence the trailing quote here. It will be closed after all arguments @@ -340,7 +340,7 @@ pub(crate) fn make_bat_command_line( // Windows file names cannot contain a `"` character or end with `\\`. // If the script name does then return an error. if script.contains(&(b'"' as u16)) || script.last() == Some(&(b'\\' as u16)) { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "Windows file names may not contain `\"` or end with `\\`" )); diff --git a/std/src/sys/pal/windows/args/tests.rs b/std/src/sys/pal/windows/args/tests.rs index 484a90ab056df..6d5c953cbd5c6 100644 --- a/std/src/sys/pal/windows/args/tests.rs +++ b/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/std/src/sys/pal/windows/c/bindings.txt b/std/src/sys/pal/windows/c/bindings.txt index c06f274685c24..248ce3c9ff624 100644 --- a/std/src/sys/pal/windows/c/bindings.txt +++ b/std/src/sys/pal/windows/c/bindings.txt @@ -2295,7 +2295,6 @@ Windows.Win32.Storage.FileSystem.FILE_NAME_OPENED Windows.Win32.Storage.FileSystem.FILE_READ_ATTRIBUTES Windows.Win32.Storage.FileSystem.FILE_READ_DATA Windows.Win32.Storage.FileSystem.FILE_READ_EA -Windows.Win32.Storage.FileSystem.FILE_RENAME_INFO Windows.Win32.Storage.FileSystem.FILE_SHARE_DELETE Windows.Win32.Storage.FileSystem.FILE_SHARE_MODE Windows.Win32.Storage.FileSystem.FILE_SHARE_NONE @@ -2426,7 +2425,6 @@ Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_PROCESSING Windows.Win32.System.Console.ENABLE_WINDOW_INPUT Windows.Win32.System.Console.ENABLE_WRAP_AT_EOL_OUTPUT Windows.Win32.System.Console.GetConsoleMode -Windows.Win32.System.Console.GetConsoleOutputCP Windows.Win32.System.Console.GetStdHandle Windows.Win32.System.Console.ReadConsoleW Windows.Win32.System.Console.STD_ERROR_HANDLE @@ -2605,7 +2603,5 @@ Windows.Win32.System.Threading.WaitForMultipleObjects Windows.Win32.System.Threading.WaitForSingleObject Windows.Win32.System.Threading.WakeAllConditionVariable Windows.Win32.System.Threading.WakeConditionVariable -Windows.Win32.System.WindowsProgramming.FILE_RENAME_FLAG_POSIX_SEMANTICS -Windows.Win32.System.WindowsProgramming.FILE_RENAME_FLAG_REPLACE_IF_EXISTS Windows.Win32.System.WindowsProgramming.PROGRESS_CONTINUE Windows.Win32.UI.Shell.GetUserProfileDirectoryW diff --git a/std/src/sys/pal/windows/c/windows_sys.rs b/std/src/sys/pal/windows/c/windows_sys.rs index 79513d33a1ac7..19925e59dfe9c 100644 --- a/std/src/sys/pal/windows/c/windows_sys.rs +++ b/std/src/sys/pal/windows/c/windows_sys.rs @@ -34,7 +34,6 @@ windows_targets::link!("kernel32.dll" "system" fn FreeEnvironmentStringsW(penv : windows_targets::link!("kernel32.dll" "system" fn GetActiveProcessorCount(groupnumber : u16) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetCommandLineW() -> PCWSTR); windows_targets::link!("kernel32.dll" "system" fn GetConsoleMode(hconsolehandle : HANDLE, lpmode : *mut CONSOLE_MODE) -> BOOL); -windows_targets::link!("kernel32.dll" "system" fn GetConsoleOutputCP() -> u32); windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferlength : u32, lpbuffer : PWSTR) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32); @@ -2473,22 +2472,6 @@ pub const FILE_RANDOM_ACCESS: NTCREATEFILE_CREATE_OPTIONS = 2048u32; pub const FILE_READ_ATTRIBUTES: FILE_ACCESS_RIGHTS = 128u32; pub const FILE_READ_DATA: FILE_ACCESS_RIGHTS = 1u32; pub const FILE_READ_EA: FILE_ACCESS_RIGHTS = 8u32; -pub const FILE_RENAME_FLAG_POSIX_SEMANTICS: u32 = 2u32; -pub const FILE_RENAME_FLAG_REPLACE_IF_EXISTS: u32 = 1u32; -#[repr(C)] -#[derive(Clone, Copy)] -pub struct FILE_RENAME_INFO { - pub Anonymous: FILE_RENAME_INFO_0, - pub RootDirectory: HANDLE, - pub FileNameLength: u32, - pub FileName: [u16; 1], -} -#[repr(C)] -#[derive(Clone, Copy)] -pub union FILE_RENAME_INFO_0 { - pub ReplaceIfExists: BOOLEAN, - pub Flags: u32, -} pub const FILE_RESERVE_OPFILTER: NTCREATEFILE_CREATE_OPTIONS = 1048576u32; pub const FILE_SEQUENTIAL_ONLY: NTCREATEFILE_CREATE_OPTIONS = 4u32; pub const FILE_SESSION_AWARE: NTCREATEFILE_CREATE_OPTIONS = 262144u32; @@ -3334,7 +3317,6 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 8], pub Reserved4: [u8; 224], } - #[cfg(target_arch = "arm")] #[repr(C)] pub struct WSADATA { diff --git a/std/src/sys/pal/windows/fs.rs b/std/src/sys/pal/windows/fs.rs index bdb55643bb101..07e4f93a37956 100644 --- a/std/src/sys/pal/windows/fs.rs +++ b/std/src/sys/pal/windows/fs.rs @@ -1,6 +1,5 @@ use super::api::{self, WinError}; use super::{IoResult, to_u16s}; -use crate::alloc::{alloc, handle_alloc_error}; use crate::borrow::Cow; use crate::ffi::{OsStr, OsString, c_void}; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; @@ -296,10 +295,6 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { let path = maybe_verbatim(path)?; - Self::open_native(&path, opts) - } - - fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result { let creation = opts.get_creation_mode()?; let handle = unsafe { c::CreateFileW( @@ -320,28 +315,19 @@ impl File { && api::get_last_error() == WinError::ALREADY_EXISTS { unsafe { - // This first tries `FileAllocationInfo` but falls back to - // `FileEndOfFileInfo` in order to support WINE. - // If WINE gains support for FileAllocationInfo, we should - // remove the fallback. - let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 }; + // This originally used `FileAllocationInfo` instead of + // `FileEndOfFileInfo` but that wasn't supported by WINE. + // It's arguable which fits the semantics of `OpenOptions` + // better so let's just use the more widely supported method. + let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; let result = c::SetFileInformationByHandle( handle.as_raw_handle(), - c::FileAllocationInfo, - (&raw const alloc).cast::(), - mem::size_of::() as u32, + c::FileEndOfFileInfo, + (&raw const eof).cast::(), + mem::size_of::() as u32, ); if result == 0 { - let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; - let result = c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileEndOfFileInfo, - (&raw const eof).cast::(), - mem::size_of::() as u32, - ); - if result == 0 { - return Err(io::Error::last_os_error()); - } + return Err(io::Error::last_os_error()); } } } @@ -691,7 +677,7 @@ impl File { ) } _ => { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::Uncategorized, "Unsupported reparse point type", )); @@ -732,7 +718,7 @@ impl File { || times.modified.map_or(false, is_zero) || times.created.map_or(false, is_zero) { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "Cannot set file timestamp to 0", )); @@ -742,7 +728,7 @@ impl File { || times.modified.map_or(false, is_max) || times.created.map_or(false, is_max) { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "Cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF", )); @@ -1230,167 +1216,14 @@ pub fn readdir(p: &Path) -> io::Result { pub fn unlink(p: &Path) -> io::Result<()> { let p_u16s = maybe_verbatim(p)?; - 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(()) - } + cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?; + Ok(()) } pub fn rename(old: &Path, new: &Path) -> io::Result<()> { let old = maybe_verbatim(old)?; let new = maybe_verbatim(new)?; - - let new_len_without_nul_in_bytes = (new.len() - 1).try_into().unwrap(); - - // The last field of FILE_RENAME_INFO, the file name, is unsized, - // and FILE_RENAME_INFO has two padding bytes. - // Therefore we need to make sure to not allocate less than - // size_of::() bytes, which would be the case with - // 0 or 1 character paths + a null byte. - let struct_size = mem::size_of::() - .max(mem::offset_of!(c::FILE_RENAME_INFO, FileName) + new.len() * mem::size_of::()); - - let struct_size: u32 = struct_size.try_into().unwrap(); - - let create_file = |extra_access, extra_flags| { - let handle = unsafe { - HandleOrInvalid::from_raw_handle(c::CreateFileW( - old.as_ptr(), - c::SYNCHRONIZE | c::DELETE | extra_access, - c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, - ptr::null(), - c::OPEN_EXISTING, - c::FILE_ATTRIBUTE_NORMAL | c::FILE_FLAG_BACKUP_SEMANTICS | extra_flags, - ptr::null_mut(), - )) - }; - - OwnedHandle::try_from(handle).map_err(|_| io::Error::last_os_error()) - }; - - // The following code replicates `MoveFileEx`'s behavior as reverse-engineered from its disassembly. - // If `old` refers to a mount point, we move it instead of the target. - let handle = match create_file(c::FILE_READ_ATTRIBUTES, c::FILE_FLAG_OPEN_REPARSE_POINT) { - Ok(handle) => { - let mut file_attribute_tag_info: MaybeUninit = - MaybeUninit::uninit(); - - let result = unsafe { - cvt(c::GetFileInformationByHandleEx( - handle.as_raw_handle(), - c::FileAttributeTagInfo, - file_attribute_tag_info.as_mut_ptr().cast(), - mem::size_of::().try_into().unwrap(), - )) - }; - - if let Err(err) = result { - if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) - || err.raw_os_error() == Some(c::ERROR_INVALID_FUNCTION as _) - { - // `GetFileInformationByHandleEx` documents that not all underlying drivers support all file information classes. - // Since we know we passed the correct arguments, this means the underlying driver didn't understand our request; - // `MoveFileEx` proceeds by reopening the file without inhibiting reparse point behavior. - None - } else { - Some(Err(err)) - } - } else { - // SAFETY: The struct has been initialized by GetFileInformationByHandleEx - let file_attribute_tag_info = unsafe { file_attribute_tag_info.assume_init() }; - let file_type = FileType::new( - file_attribute_tag_info.FileAttributes, - file_attribute_tag_info.ReparseTag, - ); - - if file_type.is_symlink() { - // The file is a mount point, junction point or symlink so - // don't reopen the file so that the link gets renamed. - Some(Ok(handle)) - } else { - // Otherwise reopen the file without inhibiting reparse point behavior. - None - } - } - } - // The underlying driver may not support `FILE_FLAG_OPEN_REPARSE_POINT`: Retry without it. - Err(err) if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) => None, - Err(err) => Some(Err(err)), - } - .unwrap_or_else(|| create_file(0, 0))?; - - let layout = core::alloc::Layout::from_size_align( - struct_size as _, - mem::align_of::(), - ) - .unwrap(); - - let file_rename_info = unsafe { alloc(layout) } as *mut c::FILE_RENAME_INFO; - - if file_rename_info.is_null() { - handle_alloc_error(layout); - } - - // SAFETY: file_rename_info is a non-null pointer pointing to memory allocated by the global allocator. - let mut file_rename_info = unsafe { Box::from_raw(file_rename_info) }; - - // SAFETY: We have allocated enough memory for a full FILE_RENAME_INFO struct and a filename. - unsafe { - (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 { - Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS, - }); - - (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut()); - (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes); - - new.as_ptr() - .copy_to_nonoverlapping((&raw mut (*file_rename_info).FileName) as *mut u16, new.len()); - } - - // We don't use `set_file_information_by_handle` here as `FILE_RENAME_INFO` is used for both `FileRenameInfo` and `FileRenameInfoEx`. - let result = unsafe { - cvt(c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileRenameInfoEx, - (&raw const *file_rename_info).cast::(), - struct_size, - )) - }; - - if let Err(err) = result { - if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) { - // FileRenameInfoEx and FILE_RENAME_FLAG_POSIX_SEMANTICS were added in Windows 10 1607; retry with FileRenameInfo. - file_rename_info.Anonymous.ReplaceIfExists = 1; - - cvt(unsafe { - c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileRenameInfo, - (&raw const *file_rename_info).cast::(), - struct_size, - ) - })?; - } else { - return Err(err); - } - } - + cvt(unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) })?; Ok(()) } @@ -1472,9 +1305,10 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { #[cfg(target_vendor = "uwp")] pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { - return Err( - io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP",), - ); + return Err(io::const_io_error!( + io::ErrorKind::Unsupported, + "hard link are not supported on UWP", + )); } pub fn stat(path: &Path) -> io::Result { @@ -1661,7 +1495,7 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) }; r"\??\UNC\".encode_utf16().chain(bytes.encode_wide()).collect() } else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "path is not valid")); + return Err(io::const_io_error!(io::ErrorKind::InvalidInput, "path is not valid")); } }; // Defined inline so we don't have to mess about with variable length buffer. @@ -1678,7 +1512,10 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { } let data_len = 12 + (abs_path.len() * 2); if data_len > u16::MAX as usize { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long")); + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "`original` path is too long" + )); } let data_len = data_len as u16; let mut header = MountPointBuffer { diff --git a/std/src/sys/io/is_terminal/windows.rs b/std/src/sys/pal/windows/io.rs similarity index 55% rename from std/src/sys/io/is_terminal/windows.rs rename to std/src/sys/pal/windows/io.rs index 3ec18fb47b9de..f2865d2ffc168 100644 --- a/std/src/sys/io/is_terminal/windows.rs +++ b/std/src/sys/pal/windows/io.rs @@ -1,8 +1,90 @@ -use crate::ffi::c_void; +use core::ffi::c_void; + +use crate::marker::PhantomData; 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/std/src/sys/pal/windows/mod.rs b/std/src/sys/pal/windows/mod.rs index 1eca346b76c2b..aca69490d7a1a 100644 --- a/std/src/sys/pal/windows/mod.rs +++ b/std/src/sys/pal/windows/mod.rs @@ -21,6 +21,8 @@ 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; @@ -61,7 +63,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() { - crate::sys::net::cleanup(); + net::cleanup(); } #[inline] @@ -111,7 +113,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::ERROR_WRITE_PROTECT => return ReadOnlyFilesystem, c::ERROR_DISK_FULL | c::ERROR_HANDLE_DISK_FULL => return StorageFull, c::ERROR_SEEK_ON_DEVICE => return NotSeekable, - c::ERROR_DISK_QUOTA_EXCEEDED => return QuotaExceeded, + c::ERROR_DISK_QUOTA_EXCEEDED => return FilesystemQuotaExceeded, c::ERROR_FILE_TOO_LARGE => return FileTooLarge, c::ERROR_BUSY => return ResourceBusy, c::ERROR_POSSIBLE_DEADLOCK => return Deadlock, @@ -136,7 +138,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::WSAEHOSTUNREACH => HostUnreachable, c::WSAENETDOWN => NetworkDown, c::WSAENETUNREACH => NetworkUnreachable, - c::WSAEDQUOT => QuotaExceeded, + c::WSAEDQUOT => FilesystemQuotaExceeded, _ => Uncategorized, } @@ -181,7 +183,7 @@ pub fn to_u16s>(s: S) -> crate::io::Result> { maybe_result.extend(s.encode_wide()); if unrolled_find_u16s(0, &maybe_result).is_some() { - return Err(crate::io::const_error!( + return Err(crate::io::const_io_error!( ErrorKind::InvalidInput, "strings passed to WinAPI cannot contain NULs", )); @@ -270,7 +272,7 @@ where unreachable!(); } else { // Safety: First `k` values are initialized. - let slice: &[u16] = buf[..k].assume_init_ref(); + let slice: &[u16] = MaybeUninit::slice_assume_init_ref(&buf[..k]); return Ok(f2(slice)); } } diff --git a/std/src/sys/net/connection/socket/windows.rs b/std/src/sys/pal/windows/net.rs similarity index 95% rename from std/src/sys/net/connection/socket/windows.rs rename to std/src/sys/pal/windows/net.rs index 80cf37eaf0580..fd62d1f407c27 100644 --- a/std/src/sys/net/connection/socket/windows.rs +++ b/std/src/sys/pal/windows/net.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}; +use crate::sys_common::{AsInner, FromInner, IntoInner, net}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -110,7 +110,6 @@ pub mod netc { } } -#[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); static WSA_CLEANUP: OnceLock i32> = OnceLock::new(); @@ -268,7 +267,7 @@ impl Socket { }; match count { - 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), + 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")), _ => { if writefds.fd_count != 1 { if let Some(e) = self.take_error()? { @@ -401,12 +400,12 @@ impl Socket { let error = unsafe { c::WSAGetLastError() }; if error == c::WSAESHUTDOWN { - Ok((0, super::sockaddr_to_addr(&storage, addrlen as usize)?)) + Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?)) } else { Err(io::Error::from_raw_os_error(error)) } } - _ => Ok((result as usize, super::sockaddr_to_addr(&storage, addrlen as usize)?)), + _ => Ok((result as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)), } } @@ -451,11 +450,11 @@ impl Socket { } None => 0, }; - super::setsockopt(self, c::SOL_SOCKET, kind, timeout) + net::setsockopt(self, c::SOL_SOCKET, kind, timeout) } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: u32 = super::getsockopt(self, c::SOL_SOCKET, kind)?; + let raw: u32 = net::getsockopt(self, c::SOL_SOCKET, kind)?; if raw == 0 { Ok(None) } else { @@ -488,26 +487,26 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; - super::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) } pub fn linger(&self) -> io::Result> { - let val: c::LINGER = super::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = net::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<()> { - super::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } pub fn nodelay(&self) -> io::Result { - let raw: c::BOOL = super::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; Ok(raw != 0) } pub fn take_error(&self) -> io::Result> { - let raw: c_int = super::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + let raw: c_int = net::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/std/src/sys/pal/windows/os.rs b/std/src/sys/pal/windows/os.rs index 044dc2e8cd8fa..5242bc9da31fe 100644 --- a/std/src/sys/pal/windows/os.rs +++ b/std/src/sys/pal/windows/os.rs @@ -5,9 +5,8 @@ #[cfg(test)] mod tests; -#[cfg(not(target_vendor = "uwp"))] -use super::api::WinError; -use super::{api, to_u16s}; +use super::api::{self, WinError}; +use super::to_u16s; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::os::windows::ffi::EncodeWide; @@ -378,8 +377,8 @@ fn home_dir_crt() -> Option { } pub fn home_dir() -> Option { - crate::env::var_os("USERPROFILE") - .filter(|s| !s.is_empty()) + crate::env::var_os("HOME") + .or_else(|| crate::env::var_os("USERPROFILE")) .map(PathBuf::from) .or_else(home_dir_crt) } diff --git a/std/src/sys/pal/windows/process.rs b/std/src/sys/pal/windows/process.rs index 9332c9b49ffb9..17bb03fe7af04 100644 --- a/std/src/sys/pal/windows/process.rs +++ b/std/src/sys/pal/windows/process.rs @@ -10,10 +10,10 @@ use crate::collections::BTreeMap; use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, Error, ErrorKind}; +use crate::mem::MaybeUninit; use crate::num::NonZero; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle}; -use crate::os::windows::process::ProcThreadAttributeList; use crate::path::{Path, PathBuf}; use crate::sync::Mutex; use crate::sys::args::{self, Arg}; @@ -142,11 +142,11 @@ impl AsRef for EnvKey { } } -pub(crate) fn ensure_no_nuls>(s: T) -> io::Result { - if s.as_ref().encode_wide().any(|b| b == 0) { - Err(io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) +pub(crate) fn ensure_no_nuls>(str: T) -> io::Result { + if str.as_ref().encode_wide().any(|b| b == 0) { + Err(io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) } else { - Ok(s) + Ok(str) } } @@ -162,6 +162,7 @@ pub struct Command { stdout: Option, stderr: Option, force_quotes_enabled: bool, + proc_thread_attributes: BTreeMap, } pub enum Stdio { @@ -193,6 +194,7 @@ impl Command { stdout: None, stderr: None, force_quotes_enabled: false, + proc_thread_attributes: Default::default(), } } @@ -246,19 +248,21 @@ impl Command { self.cwd.as_ref().map(Path::new) } - pub fn spawn( + pub unsafe fn raw_attribute( &mut self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - self.spawn_with_attributes(default, needs_stdin, None) + attribute: usize, + value: T, + ) { + self.proc_thread_attributes.insert(attribute, ProcThreadAttributeValue { + size: mem::size_of::(), + data: Box::new(value), + }); } - pub fn spawn_with_attributes( + pub fn spawn( &mut self, default: Stdio, needs_stdin: bool, - proc_thread_attribute_list: Option<&ProcThreadAttributeList<'_>>, ) -> io::Result<(Process, StdioPipes)> { let maybe_env = self.env.capture_if_changed(); @@ -351,18 +355,18 @@ impl Command { let si_ptr: *mut c::STARTUPINFOW; + let mut proc_thread_attribute_list; let mut si_ex; - if let Some(proc_thread_attribute_list) = proc_thread_attribute_list { + if !self.proc_thread_attributes.is_empty() { si.cb = mem::size_of::() as u32; flags |= c::EXTENDED_STARTUPINFO_PRESENT; + proc_thread_attribute_list = + make_proc_thread_attribute_list(&self.proc_thread_attributes)?; si_ex = c::STARTUPINFOEXW { StartupInfo: si, - // SAFETY: Casting this `*const` pointer to a `*mut` pointer is "safe" - // here because windows does not internally mutate the attribute list. - // Ideally this should be reflected in the interface of the `windows-sys` crate. - lpAttributeList: proc_thread_attribute_list.as_ptr().cast::().cast_mut(), + lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _, }; si_ptr = (&raw mut si_ex) as _; } else { @@ -435,9 +439,10 @@ fn resolve_exe<'a>( ) -> io::Result> { // Early return if there is no filename. if exe_path.is_empty() || path::has_trailing_slash(exe_path) { - return Err( - io::const_error!(io::ErrorKind::InvalidInput, "program path has no file name",), - ); + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "program path has no file name", + )); } // Test if the file name has the `exe` extension. // This does a case-insensitive `ends_with`. @@ -487,7 +492,7 @@ fn resolve_exe<'a>( } } // If we get here then the executable cannot be found. - Err(io::const_error!(io::ErrorKind::NotFound, "program not found")) + Err(io::const_io_error!(io::ErrorKind::NotFound, "program not found")) } // Calls `f` for every path that should be used to find an executable. @@ -892,6 +897,79 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec)> { } } +struct ProcThreadAttributeList(Box<[MaybeUninit]>); + +impl Drop for ProcThreadAttributeList { + fn drop(&mut self) { + let lp_attribute_list = self.0.as_mut_ptr() as _; + unsafe { c::DeleteProcThreadAttributeList(lp_attribute_list) } + } +} + +/// Wrapper around the value data to be used as a Process Thread Attribute. +struct ProcThreadAttributeValue { + data: Box, + size: usize, +} + +fn make_proc_thread_attribute_list( + attributes: &BTreeMap, +) -> io::Result { + // To initialize our ProcThreadAttributeList, we need to determine + // how many bytes to allocate for it. The Windows API simplifies this process + // by allowing us to call `InitializeProcThreadAttributeList` with + // a null pointer to retrieve the required size. + let mut required_size = 0; + let Ok(attribute_count) = attributes.len().try_into() else { + return Err(io::const_io_error!( + ErrorKind::InvalidInput, + "maximum number of ProcThreadAttributes exceeded", + )); + }; + unsafe { + c::InitializeProcThreadAttributeList( + ptr::null_mut(), + attribute_count, + 0, + &mut required_size, + ) + }; + + let mut proc_thread_attribute_list = + ProcThreadAttributeList(vec![MaybeUninit::uninit(); required_size].into_boxed_slice()); + + // Once we've allocated the necessary memory, it's safe to invoke + // `InitializeProcThreadAttributeList` to properly initialize the list. + cvt(unsafe { + c::InitializeProcThreadAttributeList( + proc_thread_attribute_list.0.as_mut_ptr() as *mut _, + attribute_count, + 0, + &mut required_size, + ) + })?; + + // # Add our attributes to the buffer. + // It's theoretically possible for the attribute count to exceed a u32 value. + // Therefore, we ensure that we don't add more attributes than the buffer was initialized for. + for (&attribute, value) in attributes.iter().take(attribute_count as usize) { + let value_ptr = (&raw const *value.data) as _; + cvt(unsafe { + c::UpdateProcThreadAttribute( + proc_thread_attribute_list.0.as_mut_ptr() as _, + 0, + attribute, + value_ptr, + value.size, + ptr::null_mut(), + ptr::null_mut(), + ) + })?; + } + + Ok(proc_thread_attribute_list) +} + pub struct CommandArgs<'a> { iter: crate::slice::Iter<'a, Arg>, } diff --git a/std/src/sys/pal/windows/process/tests.rs b/std/src/sys/pal/windows/process/tests.rs index 9a1eaf42fd9a8..1bcc5fa6b2048 100644 --- a/std/src/sys/pal/windows/process/tests.rs +++ b/std/src/sys/pal/windows/process/tests.rs @@ -158,7 +158,7 @@ fn windows_exe_resolver() { use super::resolve_exe; use crate::io; use crate::sys::fs::symlink; - use crate::test_helpers::tmpdir; + use crate::sys_common::io::test::tmpdir; let env_paths = || env::var_os("PATH"); diff --git a/std/src/sys/pal/windows/stack_overflow.rs b/std/src/sys/pal/windows/stack_overflow.rs index 734cd30bed08f..467e21ab56a28 100644 --- a/std/src/sys/pal/windows/stack_overflow.rs +++ b/std/src/sys/pal/windows/stack_overflow.rs @@ -18,10 +18,10 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN let code = rec.ExceptionCode; if code == c::EXCEPTION_STACK_OVERFLOW { - thread::with_current_name(|name| { - let name = name.unwrap_or(""); - rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); - }); + rtprintpanic!( + "\nthread '{}' has overflowed its stack\n", + thread::current().name().unwrap_or("") + ); } c::EXCEPTION_CONTINUE_SEARCH } diff --git a/std/src/sys/pal/windows/stdio.rs b/std/src/sys/pal/windows/stdio.rs index fd3f559ba1901..575f2250eb91c 100644 --- a/std/src/sys/pal/windows/stdio.rs +++ b/std/src/sys/pal/windows/stdio.rs @@ -84,43 +84,21 @@ fn is_console(handle: c::HANDLE) -> bool { unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } } -/// Returns true if the attached console's code page is currently UTF-8. -#[cfg(not(target_vendor = "win7"))] -fn is_utf8_console() -> bool { - unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 } -} - -#[cfg(target_vendor = "win7")] -fn is_utf8_console() -> bool { - // Windows 7 has a fun "feature" where WriteFile on a console handle will return - // the number of UTF-16 code units written and not the number of bytes from the input string. - // So we always claim the console isn't UTF-8 to trigger the WriteConsole fallback code. - false -} - fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result { if data.is_empty() { return Ok(0); } let handle = get_handle(handle_id)?; - if !is_console(handle) || is_utf8_console() { + if !is_console(handle) { unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.write(data); let _ = handle.into_raw_handle(); // Don't close the handle return ret; } - } else { - write_console_utf16(data, incomplete_utf8, handle) } -} -fn write_console_utf16( - data: &[u8], - incomplete_utf8: &mut IncompleteUtf8, - handle: c::HANDLE, -) -> io::Result { if incomplete_utf8.len > 0 { assert!( incomplete_utf8.len < 4, @@ -129,7 +107,7 @@ fn write_console_utf16( if data[0] >> 6 != 0b10 { // not a continuation byte - reject incomplete_utf8.len = 0; - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); @@ -151,7 +129,7 @@ fn write_console_utf16( return Ok(1); } Err(_) => { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); @@ -175,7 +153,7 @@ fn write_console_utf16( incomplete_utf8.len = 1; return Ok(1); } else { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidData, "Windows stdio in console mode does not support writing non-UTF-8 byte sequences", )); @@ -207,7 +185,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result return Ok(bytes_copied + value), Err(e) => return Err(e), @@ -414,7 +392,7 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { }; if result == 0 { // We can't really do any better than forget all data and return an error. - Err(io::const_error!( + Err(io::const_io_error!( io::ErrorKind::InvalidData, "Windows stdin in console mode does not support non-UTF-16 input; \ encountered unpaired surrogate", diff --git a/std/src/sys/pal/windows/thread.rs b/std/src/sys/pal/windows/thread.rs index 45e52cf4d047f..2c8ce42f4148b 100644 --- a/std/src/sys/pal/windows/thread.rs +++ b/std/src/sys/pal/windows/thread.rs @@ -19,7 +19,6 @@ pub struct Thread { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn new(stack: usize, p: Box) -> io::Result { let p = Box::into_raw(Box::new(p)); diff --git a/std/src/sys/pal/xous/mod.rs b/std/src/sys/pal/xous/mod.rs index 1bd0e67f37162..a64cd06856006 100644 --- a/std/src/sys/pal/xous/mod.rs +++ b/std/src/sys/pal/xous/mod.rs @@ -5,6 +5,9 @@ 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/std/src/sys/net/connection/xous/dns.rs b/std/src/sys/pal/xous/net/dns.rs similarity index 94% rename from std/src/sys/net/connection/xous/dns.rs rename to std/src/sys/pal/xous/net/dns.rs index ff6e49ed2d430..1a2b56b4da5d3 100644 --- a/std/src/sys/net/connection/xous/dns.rs +++ b/std/src/sys/pal/xous/net/dns.rs @@ -107,7 +107,7 @@ impl TryFrom<&str> for LookupHost { ($e:expr, $msg:expr) => { match $e { Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, &$msg)), + None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &$msg)), } }; } @@ -123,6 +123,7 @@ impl TryFrom<(&str, u16)> for LookupHost { type Error = io::Error; fn try_from(v: (&str, u16)) -> io::Result { - lookup(v.0, v.1).map_err(|_e| io::const_error!(io::ErrorKind::InvalidInput, &"DNS failure")) + lookup(v.0, v.1) + .map_err(|_e| io::const_io_error!(io::ErrorKind::InvalidInput, &"DNS failure")) } } diff --git a/std/src/sys/net/connection/xous/mod.rs b/std/src/sys/pal/xous/net/mod.rs similarity index 100% rename from std/src/sys/net/connection/xous/mod.rs rename to std/src/sys/pal/xous/net/mod.rs diff --git a/std/src/sys/net/connection/xous/tcplistener.rs b/std/src/sys/pal/xous/net/tcplistener.rs similarity index 85% rename from std/src/sys/net/connection/xous/tcplistener.rs rename to std/src/sys/pal/xous/net/tcplistener.rs index 640a02a64f525..ddfb289162b69 100644 --- a/std/src/sys/net/connection/xous/tcplistener.rs +++ b/std/src/sys/pal/xous/net/tcplistener.rs @@ -9,7 +9,7 @@ use crate::{fmt, io}; macro_rules! unimpl { () => { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::Unsupported, &"This function is not yet implemented", )); @@ -71,7 +71,7 @@ impl TcpListener { 0, 4096, ) else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")); + return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response")); }; // The first four bytes should be zero upon success, and will be nonzero @@ -80,13 +80,16 @@ impl TcpListener { if response[0] != 0 || valid == 0 { let errcode = response[1]; if errcode == NetError::SocketInUse as u8 { - return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); + return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); } else if errcode == NetError::Invalid as u8 { - return Err(io::const_error!(io::ErrorKind::AddrNotAvailable, &"Invalid address")); + return Err(io::const_io_error!( + io::ErrorKind::AddrNotAvailable, + &"Invalid address" + )); } else if errcode == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); } else { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::Other, &"Unable to connect or internal error" )); @@ -127,15 +130,16 @@ impl TcpListener { if receive_request.raw[0] != 0 { // error case if receive_request.raw[1] == NetError::TimedOut as u8 { - return Err(io::const_error!(io::ErrorKind::TimedOut, &"accept timed out",)); + return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"accept timed out",)); } else if receive_request.raw[1] == NetError::WouldBlock as u8 { - return Err( - io::const_error!(io::ErrorKind::WouldBlock, &"accept would block",), - ); + return Err(io::const_io_error!( + io::ErrorKind::WouldBlock, + &"accept would block", + )); } else if receive_request.raw[1] == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); } else { - return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); + return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); } } else { // accept successful @@ -159,7 +163,7 @@ impl TcpListener { port, ) } else { - return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); + return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); }; // replenish the listener @@ -171,7 +175,7 @@ impl TcpListener { Ok((TcpStream::from_listener(stream_fd, self.local.port(), port, addr), addr)) } } else { - Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unable to accept")) + Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unable to accept")) } } @@ -188,7 +192,7 @@ impl TcpListener { services::net_server(), services::NetBlockingScalar::StdSetTtlTcp(self.fd.load(Ordering::Relaxed), ttl).into(), ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -197,7 +201,7 @@ impl TcpListener { services::net_server(), services::NetBlockingScalar::StdGetTtlTcp(self.fd.load(Ordering::Relaxed)).into(), ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|res| res[0] as _)?) } diff --git a/std/src/sys/net/connection/xous/tcpstream.rs b/std/src/sys/pal/xous/net/tcpstream.rs similarity index 87% rename from std/src/sys/net/connection/xous/tcpstream.rs rename to std/src/sys/pal/xous/net/tcpstream.rs index 572dd6b3b6398..03442cf2fcdfd 100644 --- a/std/src/sys/net/connection/xous/tcpstream.rs +++ b/std/src/sys/pal/xous/net/tcpstream.rs @@ -10,7 +10,7 @@ use crate::time::Duration; macro_rules! unimpl { () => { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::Unsupported, &"This function is not yet implemented", )); @@ -96,7 +96,7 @@ impl TcpStream { 0, 4096, ) else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")); + return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response")); }; // The first four bytes should be zero upon success, and will be nonzero @@ -106,11 +106,14 @@ impl TcpStream { // errcode is a u8 but stuck in a u16 where the upper byte is invalid. Mask & decode accordingly. let errcode = response[0]; if errcode == NetError::SocketInUse as u8 { - return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use",)); + return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use",)); } else if errcode == NetError::Unaddressable as u8 { - return Err(io::const_error!(io::ErrorKind::AddrNotAvailable, &"Invalid address",)); + return Err(io::const_io_error!( + io::ErrorKind::AddrNotAvailable, + &"Invalid address", + )); } else { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, &"Unable to connect or internal error", )); @@ -196,7 +199,7 @@ impl TcpStream { self.read_timeout.load(Ordering::Relaxed) as usize, data_to_read, ) else { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, &"Library failure: wrong message type or messaging error" )); @@ -212,14 +215,14 @@ impl TcpStream { if result[0] != 0 { if result[1] == 8 { // timed out - return Err(io::const_error!(io::ErrorKind::TimedOut, &"Timeout",)); + return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"Timeout",)); } if result[1] == 9 { // would block - return Err(io::const_error!(io::ErrorKind::WouldBlock, &"Would block",)); + return Err(io::const_io_error!(io::ErrorKind::WouldBlock, &"Would block",)); } } - Err(io::const_error!(io::ErrorKind::Other, &"recv_slice failure")) + Err(io::const_io_error!(io::ErrorKind::Other, &"recv_slice failure")) } } @@ -258,20 +261,23 @@ impl TcpStream { self.write_timeout.load(Ordering::Relaxed) as usize, buf_len, ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")))?; + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")))?; if send_request.raw[0] != 0 { if send_request.raw[4] == 8 { // timed out - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::BrokenPipe, &"Timeout or connection closed", )); } else if send_request.raw[4] == 9 { // would block - return Err(io::const_error!(io::ErrorKind::WouldBlock, &"Would block",)); + return Err(io::const_io_error!(io::ErrorKind::WouldBlock, &"Would block",)); } else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Error when sending",)); + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + &"Error when sending", + )); } } Ok(u32::from_le_bytes([ @@ -304,7 +310,7 @@ impl TcpStream { 0, 0, ) else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")); + return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")); }; let mut i = get_addr.raw.iter(); match *i.next().unwrap() { @@ -324,7 +330,7 @@ impl TcpStream { } Ok(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.local_port, 0, 0))) } - _ => Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")), + _ => Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Internal error")), } } @@ -333,7 +339,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdTcpStreamShutdown(self.fd, how).into(), ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -355,7 +361,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdSetNodelay(self.fd, enabled).into(), ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -364,7 +370,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdGetNodelay(self.fd).into(), ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|res| res[0] != 0)?) } @@ -376,7 +382,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdSetTtlTcp(self.fd, ttl).into(), ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -385,7 +391,7 @@ impl TcpStream { services::net_server(), services::NetBlockingScalar::StdGetTtlTcp(self.fd).into(), ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|res| res[0] as _)?) } diff --git a/std/src/sys/net/connection/xous/udp.rs b/std/src/sys/pal/xous/net/udp.rs similarity index 88% rename from std/src/sys/net/connection/xous/udp.rs rename to std/src/sys/pal/xous/net/udp.rs index 1b7ecac6d3a7e..de5133280ba9d 100644 --- a/std/src/sys/net/connection/xous/udp.rs +++ b/std/src/sys/pal/xous/net/udp.rs @@ -11,7 +11,7 @@ use crate::{fmt, io}; macro_rules! unimpl { () => { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::Unsupported, &"This function is not yet implemented", )); @@ -72,16 +72,16 @@ impl UdpSocket { if response[0] != 0 || valid == 0 { let errcode = response[1]; if errcode == NetError::SocketInUse as u8 { - return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); + return Err(io::const_io_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); } else if errcode == NetError::Invalid as u8 { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, &"Port can't be 0 or invalid address" )); } else if errcode == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); } else { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::Other, &"Unable to connect or internal error" )); @@ -98,13 +98,13 @@ impl UdpSocket { nonblocking: Cell::new(false), }); } - Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")) + Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Invalid response")) } pub fn peer_addr(&self) -> io::Result { match self.remote.get() { Some(dest) => Ok(dest), - None => Err(io::const_error!(io::ErrorKind::NotConnected, &"No peer specified")), + None => Err(io::const_io_error!(io::ErrorKind::NotConnected, &"No peer specified")), } } @@ -141,13 +141,16 @@ impl UdpSocket { if receive_request.raw[0] != 0 { // error case if receive_request.raw[1] == NetError::TimedOut as u8 { - return Err(io::const_error!(io::ErrorKind::TimedOut, &"recv timed out",)); + return Err(io::const_io_error!(io::ErrorKind::TimedOut, &"recv timed out",)); } else if receive_request.raw[1] == NetError::WouldBlock as u8 { - return Err(io::const_error!(io::ErrorKind::WouldBlock, &"recv would block",)); + return Err(io::const_io_error!( + io::ErrorKind::WouldBlock, + &"recv would block", + )); } else if receive_request.raw[1] == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")); } else { - return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); + return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); } } else { let rr = &receive_request.raw; @@ -170,7 +173,7 @@ impl UdpSocket { port, ) } else { - return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); + return Err(io::const_io_error!(io::ErrorKind::Other, &"library error",)); }; for (&s, d) in rr[22..22 + rxlen as usize].iter().zip(buf.iter_mut()) { *d = s; @@ -178,7 +181,7 @@ impl UdpSocket { Ok((rxlen as usize, addr)) } } else { - Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unable to recv")) + Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unable to recv")) } } @@ -208,7 +211,7 @@ impl UdpSocket { if let Some(addr) = self.remote.get() { self.send_to(buf, &addr) } else { - Err(io::const_error!(io::ErrorKind::NotConnected, &"No remote specified")) + Err(io::const_io_error!(io::ErrorKind::NotConnected, &"No remote specified")) } } @@ -279,19 +282,22 @@ impl UdpSocket { if response[0] != 0 || valid == 0 { let errcode = response[1]; if errcode == NetError::SocketInUse as u8 { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::ResourceBusy, &"Socket in use" )); } else if errcode == NetError::Invalid as u8 { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, &"Socket not valid" )); } else if errcode == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + return Err(io::const_io_error!( + io::ErrorKind::Other, + &"Library error" + )); } else { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::Other, &"Unable to connect" )); @@ -303,7 +309,7 @@ impl UdpSocket { } Err(crate::os::xous::ffi::Error::ServerQueueFull) => { if now.elapsed() >= write_timeout { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::WouldBlock, &"Write timed out" )); @@ -312,7 +318,7 @@ impl UdpSocket { crate::thread::yield_now(); } } - _ => return Err(io::const_error!(io::ErrorKind::Other, &"Library error")), + _ => return Err(io::const_io_error!(io::ErrorKind::Other, &"Library error")), } } } @@ -366,7 +372,7 @@ impl UdpSocket { services::net_server(), services::NetBlockingScalar::StdSetTtlUdp(self.fd, ttl).into(), ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|_| ()) } @@ -375,7 +381,7 @@ impl UdpSocket { services::net_server(), services::NetBlockingScalar::StdGetTtlUdp(self.fd).into(), ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .or(Err(io::const_io_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) .map(|res| res[0] as _)?) } diff --git a/std/src/sys/pal/zkvm/mod.rs b/std/src/sys/pal/zkvm/mod.rs index 054c867f90d8e..6ea057720296d 100644 --- a/std/src/sys/pal/zkvm/mod.rs +++ b/std/src/sys/pal/zkvm/mod.rs @@ -16,6 +16,10 @@ 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/std/src/sys/pal/zkvm/os.rs b/std/src/sys/pal/zkvm/os.rs index 868b19e33b672..5d224ffd1ba5a 100644 --- a/std/src/sys/pal/zkvm/os.rs +++ b/std/src/sys/pal/zkvm/os.rs @@ -115,11 +115,11 @@ pub fn getenv(varname: &OsStr) -> Option { } pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) + Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } pub fn temp_dir() -> PathBuf { diff --git a/std/src/sys/pal/zkvm/stdio.rs b/std/src/sys/pal/zkvm/stdio.rs index 5f1d06dd1d78d..dd218c8894ca5 100644 --- a/std/src/sys/pal/zkvm/stdio.rs +++ b/std/src/sys/pal/zkvm/stdio.rs @@ -54,7 +54,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(_err: &io::Error) -> bool { true diff --git a/std/src/sys/path/mod.rs b/std/src/sys/path/mod.rs index 1fa4e80d6780c..24a94ec782824 100644 --- a/std/src/sys/path/mod.rs +++ b/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(target_os = "solid_asp3")] { + } else if #[cfg(any( + target_os = "uefi", + 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/std/src/sys/path/sgx.rs b/std/src/sys/path/sgx.rs index 32c7752f605d9..c805c15e70245 100644 --- a/std/src/sys/path/sgx.rs +++ b/std/src/sys/path/sgx.rs @@ -23,7 +23,3 @@ pub const MAIN_SEP: char = '/'; pub(crate) fn absolute(_path: &Path) -> io::Result { unsupported() } - -pub(crate) fn is_absolute(path: &Path) -> bool { - path.has_root() && path.prefix().is_some() -} diff --git a/std/src/sys/path/uefi.rs b/std/src/sys/path/uefi.rs deleted file mode 100644 index a3f4a3bfe1b67..0000000000000 --- a/std/src/sys/path/uefi.rs +++ /dev/null @@ -1,105 +0,0 @@ -#![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> { - 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 { - // 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/std/src/sys/path/unix.rs b/std/src/sys/path/unix.rs index 361e99964f18c..2a7c025c3c46a 100644 --- a/std/src/sys/path/unix.rs +++ b/std/src/sys/path/unix.rs @@ -60,14 +60,3 @@ pub(crate) fn absolute(path: &Path) -> io::Result { Ok(normalized) } - -pub(crate) fn is_absolute(path: &Path) -> bool { - if cfg!(target_os = "redox") { - // FIXME: Allow Redox prefixes - path.has_root() || crate::path::has_redox_scheme(path.as_u8_slice()) - } else if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) { - path.has_root() - } else { - path.has_root() && path.prefix().is_some() - } -} diff --git a/std/src/sys/path/unsupported_backslash.rs b/std/src/sys/path/unsupported_backslash.rs index 30b06c132c98d..855f443678c6c 100644 --- a/std/src/sys/path/unsupported_backslash.rs +++ b/std/src/sys/path/unsupported_backslash.rs @@ -24,7 +24,3 @@ pub const MAIN_SEP: char = '\\'; pub(crate) fn absolute(_path: &Path) -> io::Result { unsupported() } - -pub(crate) fn is_absolute(path: &Path) -> bool { - path.has_root() && path.prefix().is_some() -} diff --git a/std/src/sys/path/windows.rs b/std/src/sys/path/windows.rs index 1c53472191699..9267602cb9715 100644 --- a/std/src/sys/path/windows.rs +++ b/std/src/sys/path/windows.rs @@ -328,7 +328,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { if prefix.map(|x| x.is_verbatim()).unwrap_or(false) { // NULs in verbatim paths are rejected for consistency. if path.as_encoded_bytes().contains(&0) { - return Err(io::const_error!( + return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "strings passed to WinAPI cannot contain NULs", )); @@ -346,7 +346,3 @@ pub(crate) fn absolute(path: &Path) -> io::Result { os2path, ) } - -pub(crate) fn is_absolute(path: &Path) -> bool { - path.has_root() && path.prefix().is_some() -} diff --git a/std/src/sys/personality/gcc.rs b/std/src/sys/personality/gcc.rs index 88a25caeff0df..ad596ecff65d5 100644 --- a/std/src/sys/personality/gcc.rs +++ b/std/src/sys/personality/gcc.rs @@ -5,7 +5,7 @@ //! documents linked from it. //! These are also good reads: //! * -//! * +//! * //! * //! //! ## A brief summary diff --git a/std/src/sys/personality/mod.rs b/std/src/sys/personality/mod.rs index 2e1d2e53a2979..9754e840d151a 100644 --- a/std/src/sys/personality/mod.rs +++ b/std/src/sys/personality/mod.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "nuttx")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems"), not(target_os = "nuttx")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/std/src/sys/sync/condvar/no_threads.rs b/std/src/sys/sync/condvar/no_threads.rs index 18d97d4b17ab0..2a67ed766aa0c 100644 --- a/std/src/sys/sync/condvar/no_threads.rs +++ b/std/src/sys/sync/condvar/no_threads.rs @@ -1,11 +1,11 @@ use crate::sys::sync::Mutex; -use crate::thread::sleep; use crate::time::Duration; pub struct Condvar {} impl Condvar { #[inline] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_locks", since = "1.63.0"))] pub const fn new() -> Condvar { Condvar {} } @@ -20,8 +20,7 @@ impl Condvar { panic!("condvar wait not supported") } - pub unsafe fn wait_timeout(&self, _mutex: &Mutex, dur: Duration) -> bool { - sleep(dur); - false + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + panic!("condvar wait not supported"); } } diff --git a/std/src/sys/sync/condvar/pthread.rs b/std/src/sys/sync/condvar/pthread.rs index 5bb7431eecf0c..cee728e35cdfc 100644 --- a/std/src/sys/sync/condvar/pthread.rs +++ b/std/src/sys/sync/condvar/pthread.rs @@ -1,88 +1,196 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::pin::Pin; +use crate::cell::UnsafeCell; use crate::ptr; -use crate::sync::atomic::AtomicUsize; +use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::pal::sync as pal; use crate::sys::sync::{Mutex, OnceBox}; -use crate::time::{Duration, Instant}; +#[cfg(not(target_os = "nto"))] +use crate::sys::time::TIMESPEC_MAX; +#[cfg(target_os = "nto")] +use crate::sys::time::TIMESPEC_MAX_CAPPED; +use crate::time::Duration; + +struct AllocatedCondvar(UnsafeCell); pub struct Condvar { - cvar: OnceBox, - mutex: AtomicUsize, + inner: OnceBox, + mutex: AtomicPtr, +} + +unsafe impl Send for AllocatedCondvar {} +unsafe impl Sync for AllocatedCondvar {} + +impl AllocatedCondvar { + fn new() -> Box { + let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); + + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "l4re", + target_os = "android", + target_os = "redox", + target_vendor = "apple", + ))] { + // `pthread_condattr_setclock` is unfortunately not supported on these platforms. + } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "teeos"))] { + // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet + // So on that platform, init() should always be called + // Moreover, that platform does not have pthread_condattr_setclock support, + // hence that initialization should be skipped as well + // + // Similar story for the 3DS (horizon). + let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; + assert_eq!(r, 0); + } else { + use crate::mem::MaybeUninit; + let mut attr = MaybeUninit::::uninit(); + let r = unsafe { libc::pthread_condattr_init(attr.as_mut_ptr()) }; + assert_eq!(r, 0); + let r = unsafe { libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC) }; + assert_eq!(r, 0); + let r = unsafe { libc::pthread_cond_init(condvar.0.get(), attr.as_ptr()) }; + assert_eq!(r, 0); + let r = unsafe { libc::pthread_condattr_destroy(attr.as_mut_ptr()) }; + assert_eq!(r, 0); + } + } + + condvar + } +} + +impl Drop for AllocatedCondvar { + #[inline] + fn drop(&mut self) { + let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; + if cfg!(target_os = "dragonfly") { + // On DragonFly pthread_cond_destroy() returns EINVAL if called on + // a condvar that was just initialized with + // libc::PTHREAD_COND_INITIALIZER. Once it is used or + // pthread_cond_init() is called, this behavior no longer occurs. + debug_assert!(r == 0 || r == libc::EINVAL); + } else { + debug_assert_eq!(r, 0); + } + } } impl Condvar { pub const fn new() -> Condvar { - Condvar { cvar: OnceBox::new(), mutex: AtomicUsize::new(0) } + Condvar { inner: OnceBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } } - #[inline] - fn get(&self) -> Pin<&pal::Condvar> { - self.cvar.get_or_init(|| { - let mut cvar = Box::pin(pal::Condvar::new()); - // SAFETY: we only call `init` once per `pal::Condvar`, namely here. - unsafe { cvar.as_mut().init() }; - cvar - }) + fn get(&self) -> *mut libc::pthread_cond_t { + self.inner.get_or_init(AllocatedCondvar::new).0.get() } #[inline] - fn verify(&self, mutex: Pin<&pal::Mutex>) { - let addr = ptr::from_ref::(&mutex).addr(); - // Relaxed is okay here because we never read through `self.mutex`, and only use it to + fn verify(&self, mutex: *mut libc::pthread_mutex_t) { + // Relaxed is okay here because we never read through `self.addr`, and only use it to // compare addresses. - match self.mutex.compare_exchange(0, addr, Relaxed, Relaxed) { - Ok(_) => {} // Stored the address - Err(n) if n == addr => {} // Lost a race to store the same address + match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { + Ok(_) => {} // Stored the address + Err(n) if n == mutex => {} // Lost a race to store the same address _ => panic!("attempted to use a condition variable with two mutexes"), } } #[inline] pub fn notify_one(&self) { - // SAFETY: we called `init` above. - unsafe { self.get().notify_one() } + let r = unsafe { libc::pthread_cond_signal(self.get()) }; + debug_assert_eq!(r, 0); } #[inline] pub fn notify_all(&self) { - // SAFETY: we called `init` above. - unsafe { self.get().notify_all() } + let r = unsafe { libc::pthread_cond_broadcast(self.get()) }; + debug_assert_eq!(r, 0); } #[inline] pub unsafe fn wait(&self, mutex: &Mutex) { - // SAFETY: the caller guarantees that the lock is owned, thus the mutex - // must have been initialized already. - let mutex = unsafe { mutex.pal.get_unchecked() }; + let mutex = mutex.get_assert_locked(); self.verify(mutex); - // SAFETY: we called `init` above, we verified that this condition - // variable is only used with `mutex` and the caller guarantees that - // `mutex` is locked by the current thread. - unsafe { self.get().wait(mutex) } + let r = libc::pthread_cond_wait(self.get(), mutex); + debug_assert_eq!(r, 0); } + // This implementation is used on systems that support pthread_condattr_setclock + // where we configure condition variable to use monotonic clock (instead of + // default system clock). This approach avoids all problems that result + // from changes made to the system time. + #[cfg(not(any( + target_os = "android", + target_os = "espidf", + target_os = "horizon", + target_vendor = "apple", + )))] pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - // SAFETY: the caller guarantees that the lock is owned, thus the mutex - // must have been initialized already. - let mutex = unsafe { mutex.pal.get_unchecked() }; + use crate::sys::time::Timespec; + + let mutex = mutex.get_assert_locked(); self.verify(mutex); - if pal::Condvar::PRECISE_TIMEOUT { - // SAFETY: we called `init` above, we verified that this condition - // variable is only used with `mutex` and the caller guarantees that - // `mutex` is locked by the current thread. - unsafe { self.get().wait_timeout(mutex, dur) } - } else { - // Timeout reports are not reliable, so do the check ourselves. - let now = Instant::now(); - // SAFETY: we called `init` above, we verified that this condition - // variable is only used with `mutex` and the caller guarantees that - // `mutex` is locked by the current thread. - let woken = unsafe { self.get().wait_timeout(mutex, dur) }; - woken || now.elapsed() < dur - } + #[cfg(not(target_os = "nto"))] + let timeout = Timespec::now(libc::CLOCK_MONOTONIC) + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec()) + .unwrap_or(TIMESPEC_MAX); + + #[cfg(target_os = "nto")] + let timeout = Timespec::now(libc::CLOCK_MONOTONIC) + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec_capped()) + .unwrap_or(TIMESPEC_MAX_CAPPED); + + let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout); + assert!(r == libc::ETIMEDOUT || r == 0); + r == 0 + } + + // This implementation is modeled after libcxx's condition_variable + // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 + // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 + #[cfg(any( + target_os = "android", + target_os = "espidf", + target_os = "horizon", + target_vendor = "apple", + ))] + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + use crate::sys::time::SystemTime; + use crate::time::Instant; + + let mutex = mutex.get_assert_locked(); + self.verify(mutex); + + // OSX implementation of `pthread_cond_timedwait` is buggy + // with super long durations. When duration is greater than + // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` + // in macOS Sierra returns error 316. + // + // This program demonstrates the issue: + // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c + // + // To work around this issue, and possible bugs of other OSes, timeout + // is clamped to 1000 years, which is allowable per the API of `wait_timeout` + // because of spurious wakeups. + let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400)); + + // pthread_cond_timedwait uses system time, but we want to report timeout + // based on stable time. + let now = Instant::now(); + + let timeout = SystemTime::now() + .t + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec()) + .unwrap_or(TIMESPEC_MAX); + + let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout); + debug_assert!(r == libc::ETIMEDOUT || r == 0); + + // ETIMEDOUT is not a totally reliable method of determining timeout due + // to clock shifts, so do the check ourselves + now.elapsed() < dur } } diff --git a/std/src/sys/sync/condvar/sgx.rs b/std/src/sys/sync/condvar/sgx.rs index 2bde9d0694eda..e60715e4b592e 100644 --- a/std/src/sys/sync/condvar/sgx.rs +++ b/std/src/sys/sync/condvar/sgx.rs @@ -13,19 +13,17 @@ impl Condvar { } fn get(&self) -> &SpinMutex> { - self.inner.get_or_init(|| Box::pin(SpinMutex::new(WaitVariable::new(())))).get_ref() + self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(())))) } #[inline] pub fn notify_one(&self) { - let guard = self.get().lock(); - let _ = WaitQueue::notify_one(guard); + let _ = WaitQueue::notify_one(self.get().lock()); } #[inline] pub fn notify_all(&self) { - let guard = self.get().lock(); - let _ = WaitQueue::notify_all(guard); + let _ = WaitQueue::notify_all(self.get().lock()); } pub unsafe fn wait(&self, mutex: &Mutex) { diff --git a/std/src/sys/sync/mutex/no_threads.rs b/std/src/sys/sync/mutex/no_threads.rs index 57c78f454c57c..7b243575e018e 100644 --- a/std/src/sys/sync/mutex/no_threads.rs +++ b/std/src/sys/sync/mutex/no_threads.rs @@ -10,6 +10,7 @@ unsafe impl Sync for Mutex {} // no threads on this platform impl Mutex { #[inline] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_locks", since = "1.63.0"))] pub const fn new() -> Mutex { Mutex { locked: Cell::new(false) } } diff --git a/std/src/sys/sync/mutex/pthread.rs b/std/src/sys/sync/mutex/pthread.rs index 75b4b9c6dad9b..abd58122523cf 100644 --- a/std/src/sys/sync/mutex/pthread.rs +++ b/std/src/sys/sync/mutex/pthread.rs @@ -1,66 +1,163 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::mem::forget; -use crate::pin::Pin; -use crate::sys::pal::sync as pal; +use crate::cell::UnsafeCell; +use crate::io::Error; +use crate::mem::{MaybeUninit, forget}; +use crate::sys::cvt_nz; use crate::sys::sync::OnceBox; +struct AllocatedMutex(UnsafeCell); + pub struct Mutex { - pub pal: OnceBox, + inner: OnceBox, +} + +unsafe impl Send for AllocatedMutex {} +unsafe impl Sync for AllocatedMutex {} + +impl AllocatedMutex { + fn new() -> Box { + let mutex = Box::new(AllocatedMutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))); + + // Issue #33770 + // + // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have + // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you + // try to re-lock it from the same thread when you already hold a lock + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). + // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL + // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that + // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same + // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in + // a Mutex where re-locking is UB. + // + // In practice, glibc takes advantage of this undefined behavior to + // implement hardware lock elision, which uses hardware transactional + // memory to avoid acquiring the lock. While a transaction is in + // progress, the lock appears to be unlocked. This isn't a problem for + // other threads since the transactional memory will abort if a conflict + // is detected, however no abort is generated when re-locking from the + // same thread. + // + // Since locking the same mutex twice will result in two aliasing &mut + // references, we instead create the mutex with type + // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to + // re-lock it from the same thread, thus avoiding undefined behavior. + unsafe { + let mut attr = MaybeUninit::::uninit(); + cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); + let attr = PthreadMutexAttr(&mut attr); + cvt_nz(libc::pthread_mutexattr_settype( + attr.0.as_mut_ptr(), + libc::PTHREAD_MUTEX_NORMAL, + )) + .unwrap(); + cvt_nz(libc::pthread_mutex_init(mutex.0.get(), attr.0.as_ptr())).unwrap(); + } + + mutex + } +} + +impl Drop for AllocatedMutex { + #[inline] + fn drop(&mut self) { + let r = unsafe { libc::pthread_mutex_destroy(self.0.get()) }; + if cfg!(target_os = "dragonfly") { + // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a + // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. + // Once it is used (locked/unlocked) or pthread_mutex_init() is called, + // this behavior no longer occurs. + debug_assert!(r == 0 || r == libc::EINVAL); + } else { + debug_assert_eq!(r, 0); + } + } } impl Mutex { #[inline] pub const fn new() -> Mutex { - Mutex { pal: OnceBox::new() } + Mutex { inner: OnceBox::new() } } + /// Gets access to the pthread mutex under the assumption that the mutex is + /// locked. + /// + /// This allows skipping the initialization check, as the mutex can only be + /// locked if it is already initialized, and allows relaxing the ordering + /// on the pointer load, since the allocation cannot have been modified + /// since the `lock` and the lock must have occurred on the current thread. + /// + /// # Safety + /// Causes undefined behavior if the mutex is not locked. #[inline] - fn get(&self) -> Pin<&pal::Mutex> { - // If the initialization race is lost, the new mutex is destroyed. - // This is sound however, as it cannot have been locked. - self.pal.get_or_init(|| { - let mut pal = Box::pin(pal::Mutex::new()); - // SAFETY: we only call `init` once per `pal::Mutex`, namely here. - unsafe { pal.as_mut().init() }; - pal - }) + pub(crate) unsafe fn get_assert_locked(&self) -> *mut libc::pthread_mutex_t { + unsafe { self.inner.get_unchecked().0.get() } + } + + #[inline] + fn get(&self) -> *mut libc::pthread_mutex_t { + // If initialization fails, the mutex is destroyed. This is always sound, + // however, as the mutex cannot have been locked yet. + self.inner.get_or_init(AllocatedMutex::new).0.get() } #[inline] pub fn lock(&self) { - // SAFETY: we call `init` above, therefore reentrant locking is safe. - // In `drop` we ensure that the mutex is not destroyed while locked. - unsafe { self.get().lock() } + #[cold] + #[inline(never)] + fn fail(r: i32) -> ! { + let error = Error::from_raw_os_error(r); + panic!("failed to lock mutex: {error}"); + } + + let r = unsafe { libc::pthread_mutex_lock(self.get()) }; + // As we set the mutex type to `PTHREAD_MUTEX_NORMAL` above, we expect + // the lock call to never fail. Unfortunately however, some platforms + // (Solaris) do not conform to the standard, and instead always provide + // deadlock detection. How kind of them! Unfortunately that means that + // we need to check the error code here. To save us from UB on other + // less well-behaved platforms in the future, we do it even on "good" + // platforms like macOS. See #120147 for more context. + if r != 0 { + fail(r) + } } #[inline] pub unsafe fn unlock(&self) { - // SAFETY: the mutex can only be locked if it is already initialized - // and we observed this initialization since we observed the locking. - unsafe { self.pal.get_unchecked().unlock() } + let r = libc::pthread_mutex_unlock(self.get_assert_locked()); + debug_assert_eq!(r, 0); } #[inline] pub fn try_lock(&self) -> bool { - // SAFETY: we call `init` above, therefore reentrant locking is safe. - // In `drop` we ensure that the mutex is not destroyed while locked. - unsafe { self.get().try_lock() } + unsafe { libc::pthread_mutex_trylock(self.get()) == 0 } } } impl Drop for Mutex { fn drop(&mut self) { - let Some(pal) = self.pal.take() else { return }; + let Some(mutex) = self.inner.take() else { return }; // We're not allowed to pthread_mutex_destroy a locked mutex, // so check first if it's unlocked. - if unsafe { pal.as_ref().try_lock() } { - unsafe { pal.as_ref().unlock() }; - drop(pal) + if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } { + unsafe { libc::pthread_mutex_unlock(mutex.0.get()) }; + drop(mutex); } else { // The mutex is locked. This happens if a MutexGuard is leaked. // In this case, we just leak the Mutex too. - forget(pal) + forget(mutex); + } + } +} + +pub(super) struct PthreadMutexAttr<'a>(pub &'a mut MaybeUninit); + +impl Drop for PthreadMutexAttr<'_> { + fn drop(&mut self) { + unsafe { + let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr()); + debug_assert_eq!(result, 0); } } } diff --git a/std/src/sys/sync/mutex/sgx.rs b/std/src/sys/sync/mutex/sgx.rs index 3eb981bc65af6..8529e85797043 100644 --- a/std/src/sys/sync/mutex/sgx.rs +++ b/std/src/sys/sync/mutex/sgx.rs @@ -13,7 +13,7 @@ impl Mutex { } fn get(&self) -> &SpinMutex> { - self.inner.get_or_init(|| Box::pin(SpinMutex::new(WaitVariable::new(false)))).get_ref() + self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(false)))) } #[inline] @@ -33,7 +33,7 @@ impl Mutex { pub unsafe fn unlock(&self) { // SAFETY: the mutex was locked by the current thread, so it has been // initialized already. - let guard = unsafe { self.inner.get_unchecked().get_ref().lock() }; + let guard = unsafe { self.inner.get_unchecked().lock() }; if let Err(mut guard) = WaitQueue::notify_one(guard) { // No other waiters, unlock *guard.lock_var_mut() = false; diff --git a/std/src/sys/sync/once/futex.rs b/std/src/sys/sync/once/futex.rs index 539f0fe89eaaa..10bfa81a6d72a 100644 --- a/std/src/sys/sync/once/futex.rs +++ b/std/src/sys/sync/once/futex.rs @@ -1,7 +1,7 @@ use crate::cell::Cell; use crate::sync as public; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::poison::once::ExclusiveState; +use crate::sync::once::ExclusiveState; use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake_all}; // On some platforms, the OS is very nice and handles the waiter queue for us. diff --git a/std/src/sys/sync/once/no_threads.rs b/std/src/sys/sync/once/no_threads.rs index 2568059cfe3a8..fb1b496510aba 100644 --- a/std/src/sys/sync/once/no_threads.rs +++ b/std/src/sys/sync/once/no_threads.rs @@ -1,6 +1,6 @@ use crate::cell::Cell; use crate::sync as public; -use crate::sync::poison::once::ExclusiveState; +use crate::sync::once::ExclusiveState; pub struct Once { state: Cell, @@ -35,6 +35,7 @@ unsafe impl Sync for Once {} impl Once { #[inline] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_once_new", since = "1.32.0"))] pub const fn new() -> Once { Once { state: Cell::new(State::Incomplete) } } diff --git a/std/src/sys/sync/once/queue.rs b/std/src/sys/sync/once/queue.rs index fde1e0ca51029..87837915b396e 100644 --- a/std/src/sys/sync/once/queue.rs +++ b/std/src/sys/sync/once/queue.rs @@ -58,7 +58,7 @@ use crate::cell::Cell; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Release}; use crate::sync::atomic::{AtomicBool, AtomicPtr}; -use crate::sync::poison::once::ExclusiveState; +use crate::sync::once::ExclusiveState; use crate::thread::{self, Thread}; use crate::{fmt, ptr, sync as public}; @@ -116,6 +116,7 @@ fn to_state(current: StateAndQueue) -> usize { impl Once { #[inline] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_once_new", since = "1.32.0"))] pub const fn new() -> Once { Once { state_and_queue: AtomicPtr::new(ptr::without_provenance_mut(INCOMPLETE)) } } diff --git a/std/src/sys/sync/once_box.rs b/std/src/sys/sync/once_box.rs index 6953b91999ad1..4105af503295f 100644 --- a/std/src/sys/sync/once_box.rs +++ b/std/src/sys/sync/once_box.rs @@ -6,7 +6,6 @@ #![allow(dead_code)] // Only used on some platforms. use crate::mem::replace; -use crate::pin::Pin; use crate::ptr::null_mut; use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; @@ -28,46 +27,46 @@ impl OnceBox { /// pointer load in this function can be performed with relaxed ordering, /// potentially allowing the optimizer to turn code like this: /// ```rust, ignore - /// once_box.get_or_init(|| Box::pin(42)); + /// once_box.get_or_init(|| Box::new(42)); /// unsafe { once_box.get_unchecked() } /// ``` /// into /// ```rust, ignore - /// once_box.get_or_init(|| Box::pin(42)) + /// once_box.get_or_init(|| Box::new(42)) /// ``` /// /// # Safety /// This causes undefined behavior if the assumption above is violated. #[inline] - pub unsafe fn get_unchecked(&self) -> Pin<&T> { - unsafe { Pin::new_unchecked(&*self.ptr.load(Relaxed)) } + pub unsafe fn get_unchecked(&self) -> &T { + unsafe { &*self.ptr.load(Relaxed) } } #[inline] - pub fn get_or_init(&self, f: impl FnOnce() -> Pin>) -> Pin<&T> { + pub fn get_or_init(&self, f: impl FnOnce() -> Box) -> &T { let ptr = self.ptr.load(Acquire); match unsafe { ptr.as_ref() } { - Some(val) => unsafe { Pin::new_unchecked(val) }, + Some(val) => val, None => self.initialize(f), } } #[inline] - pub fn take(&mut self) -> Option>> { + pub fn take(&mut self) -> Option> { let ptr = replace(self.ptr.get_mut(), null_mut()); - if !ptr.is_null() { Some(unsafe { Pin::new_unchecked(Box::from_raw(ptr)) }) } else { None } + if !ptr.is_null() { Some(unsafe { Box::from_raw(ptr) }) } else { None } } #[cold] - fn initialize(&self, f: impl FnOnce() -> Pin>) -> Pin<&T> { - let new_ptr = Box::into_raw(unsafe { Pin::into_inner_unchecked(f()) }); + fn initialize(&self, f: impl FnOnce() -> Box) -> &T { + let new_ptr = Box::into_raw(f()); match self.ptr.compare_exchange(null_mut(), new_ptr, Release, Acquire) { - Ok(_) => unsafe { Pin::new_unchecked(&*new_ptr) }, + Ok(_) => unsafe { &*new_ptr }, Err(ptr) => { // Lost the race to another thread. // Drop the value we created, and use the one from the other thread instead. drop(unsafe { Box::from_raw(new_ptr) }); - unsafe { Pin::new_unchecked(&*ptr) } + unsafe { &*ptr } } } } diff --git a/std/src/sys/sync/rwlock/no_threads.rs b/std/src/sys/sync/rwlock/no_threads.rs index 573d0d602dbd6..c11e59f719e93 100644 --- a/std/src/sys/sync/rwlock/no_threads.rs +++ b/std/src/sys/sync/rwlock/no_threads.rs @@ -10,6 +10,7 @@ unsafe impl Sync for RwLock {} // no threads on this platform impl RwLock { #[inline] + #[cfg_attr(bootstrap, rustc_const_stable(feature = "const_locks", since = "1.63.0"))] pub const fn new() -> RwLock { RwLock { mode: Cell::new(0) } } diff --git a/std/src/sys/sync/thread_parking/pthread.rs b/std/src/sys/sync/thread_parking/pthread.rs index 19cabd7dd75c8..76df73b2a8e06 100644 --- a/std/src/sys/sync/thread_parking/pthread.rs +++ b/std/src/sys/sync/thread_parking/pthread.rs @@ -1,19 +1,93 @@ //! Thread parking without `futex` using the `pthread` synchronization primitives. +use crate::cell::UnsafeCell; +use crate::marker::PhantomPinned; use crate::pin::Pin; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sys::pal::sync::{Condvar, Mutex}; +#[cfg(not(target_os = "nto"))] +use crate::sys::time::TIMESPEC_MAX; +#[cfg(target_os = "nto")] +use crate::sys::time::TIMESPEC_MAX_CAPPED; use crate::time::Duration; const EMPTY: usize = 0; const PARKED: usize = 1; const NOTIFIED: usize = 2; +unsafe fn lock(lock: *mut libc::pthread_mutex_t) { + let r = libc::pthread_mutex_lock(lock); + debug_assert_eq!(r, 0); +} + +unsafe fn unlock(lock: *mut libc::pthread_mutex_t) { + let r = libc::pthread_mutex_unlock(lock); + debug_assert_eq!(r, 0); +} + +unsafe fn notify_one(cond: *mut libc::pthread_cond_t) { + let r = libc::pthread_cond_signal(cond); + debug_assert_eq!(r, 0); +} + +unsafe fn wait(cond: *mut libc::pthread_cond_t, lock: *mut libc::pthread_mutex_t) { + let r = libc::pthread_cond_wait(cond, lock); + debug_assert_eq!(r, 0); +} + +unsafe fn wait_timeout( + cond: *mut libc::pthread_cond_t, + lock: *mut libc::pthread_mutex_t, + dur: Duration, +) { + // Use the system clock on systems that do not support pthread_condattr_setclock. + // This unfortunately results in problems when the system time changes. + #[cfg(any(target_os = "espidf", target_os = "horizon", target_vendor = "apple"))] + let (now, dur) = { + use crate::cmp::min; + use crate::sys::time::SystemTime; + + // OSX implementation of `pthread_cond_timedwait` is buggy + // with super long durations. When duration is greater than + // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` + // in macOS Sierra return error 316. + // + // This program demonstrates the issue: + // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c + // + // To work around this issue, and possible bugs of other OSes, timeout + // is clamped to 1000 years, which is allowable per the API of `park_timeout` + // because of spurious wakeups. + let dur = min(dur, Duration::from_secs(1000 * 365 * 86400)); + let now = SystemTime::now().t; + (now, dur) + }; + // Use the monotonic clock on other systems. + #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_vendor = "apple")))] + let (now, dur) = { + use crate::sys::time::Timespec; + + (Timespec::now(libc::CLOCK_MONOTONIC), dur) + }; + + #[cfg(not(target_os = "nto"))] + let timeout = + now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); + #[cfg(target_os = "nto")] + let timeout = now + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec_capped()) + .unwrap_or(TIMESPEC_MAX_CAPPED); + let r = libc::pthread_cond_timedwait(cond, lock, &timeout); + debug_assert!(r == libc::ETIMEDOUT || r == 0); +} + pub struct Parker { state: AtomicUsize, - lock: Mutex, - cvar: Condvar, + lock: UnsafeCell, + cvar: UnsafeCell, + // The `pthread` primitives require a stable address, so make this struct `!Unpin`. + _pinned: PhantomPinned, } impl Parker { @@ -22,21 +96,38 @@ impl Parker { /// # Safety /// The constructed parker must never be moved. pub unsafe fn new_in_place(parker: *mut Parker) { - parker.write(Parker { - state: AtomicUsize::new(EMPTY), - lock: Mutex::new(), - cvar: Condvar::new(), - }); - - Pin::new_unchecked(&mut (*parker).cvar).init(); - } + // Use the default mutex implementation to allow for simpler initialization. + // This could lead to undefined behavior when deadlocking. This is avoided + // by not deadlocking. Note in particular the unlocking operation before any + // panic, as code after the panic could try to park again. + (&raw mut (*parker).state).write(AtomicUsize::new(EMPTY)); + (&raw mut (*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)); - fn lock(self: Pin<&Self>) -> Pin<&Mutex> { - unsafe { self.map_unchecked(|p| &p.lock) } - } - - fn cvar(self: Pin<&Self>) -> Pin<&Condvar> { - unsafe { self.map_unchecked(|p| &p.cvar) } + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "l4re", + target_os = "android", + target_os = "redox", + target_os = "vita", + target_vendor = "apple", + ))] { + (&raw mut (*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); + } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { + let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), crate::ptr::null()); + assert_eq!(r, 0); + } else { + use crate::mem::MaybeUninit; + let mut attr = MaybeUninit::::uninit(); + let r = libc::pthread_condattr_init(attr.as_mut_ptr()); + assert_eq!(r, 0); + let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); + assert_eq!(r, 0); + let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), attr.as_ptr()); + assert_eq!(r, 0); + let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); + assert_eq!(r, 0); + } + } } // This implementation doesn't require `unsafe`, but other implementations @@ -51,7 +142,7 @@ impl Parker { } // Otherwise we need to coordinate going to sleep - self.lock().lock(); + lock(self.lock.get()); match self.state.compare_exchange(EMPTY, PARKED, Relaxed, Relaxed) { Ok(_) => {} Err(NOTIFIED) => { @@ -63,20 +154,20 @@ impl Parker { // read from the write it made to `state`. let old = self.state.swap(EMPTY, Acquire); - self.lock().unlock(); + unlock(self.lock.get()); assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); return; } // should consume this notification, so prohibit spurious wakeups in next park. Err(_) => { - self.lock().unlock(); + unlock(self.lock.get()); panic!("inconsistent park state") } } loop { - self.cvar().wait(self.lock()); + wait(self.cvar.get(), self.lock.get()); match self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed) { Ok(_) => break, // got a notification @@ -84,7 +175,7 @@ impl Parker { } } - self.lock().unlock(); + unlock(self.lock.get()); } // This implementation doesn't require `unsafe`, but other implementations @@ -98,19 +189,19 @@ impl Parker { return; } - self.lock().lock(); + lock(self.lock.get()); match self.state.compare_exchange(EMPTY, PARKED, Relaxed, Relaxed) { Ok(_) => {} Err(NOTIFIED) => { // We must read again here, see `park`. let old = self.state.swap(EMPTY, Acquire); - self.lock().unlock(); + unlock(self.lock.get()); assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); return; } // should consume this notification, so prohibit spurious wakeups in next park. Err(_) => { - self.lock().unlock(); + unlock(self.lock.get()); panic!("inconsistent park_timeout state") } } @@ -119,13 +210,13 @@ impl Parker { // from a notification we just want to unconditionally set the state back to // empty, either consuming a notification or un-flagging ourselves as // parked. - self.cvar().wait_timeout(self.lock(), dur); + wait_timeout(self.cvar.get(), self.lock.get(), dur); match self.state.swap(EMPTY, Acquire) { - NOTIFIED => self.lock().unlock(), // got a notification, hurray! - PARKED => self.lock().unlock(), // no notification, alas + NOTIFIED => unlock(self.lock.get()), // got a notification, hurray! + PARKED => unlock(self.lock.get()), // no notification, alas n => { - self.lock().unlock(); + unlock(self.lock.get()); panic!("inconsistent park_timeout state: {n}") } } @@ -157,9 +248,21 @@ impl Parker { // parked thread wakes it doesn't get woken only to have to wait for us // to release `lock`. unsafe { - self.lock().lock(); - self.lock().unlock(); - self.cvar().notify_one(); + lock(self.lock.get()); + unlock(self.lock.get()); + notify_one(self.cvar.get()); } } } + +impl Drop for Parker { + fn drop(&mut self) { + unsafe { + libc::pthread_cond_destroy(self.cvar.get_mut()); + libc::pthread_mutex_destroy(self.lock.get_mut()); + } + } +} + +unsafe impl Sync for Parker {} +unsafe impl Send for Parker {} diff --git a/std/src/sys/thread_local/key/racy.rs b/std/src/sys/thread_local/key/racy.rs index e1bc08eabb358..97df8997b80de 100644 --- a/std/src/sys/thread_local/key/racy.rs +++ b/std/src/sys/thread_local/key/racy.rs @@ -30,6 +30,7 @@ const KEY_SENTVAL: usize = 0; const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1; impl LazyKey { + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "thread_local_internals", issue = "none"))] pub const fn new(dtor: Option) -> LazyKey { LazyKey { key: atomic::AtomicUsize::new(KEY_SENTVAL), dtor } } diff --git a/std/src/sys/thread_local/key/unix.rs b/std/src/sys/thread_local/key/unix.rs index b4b58b3470631..28e48a750b9bf 100644 --- a/std/src/sys/thread_local/key/unix.rs +++ b/std/src/sys/thread_local/key/unix.rs @@ -1,25 +1,5 @@ use crate::mem; -// For WASI add a few symbols not in upstream `libc` just yet. -#[cfg(all(target_os = "wasi", target_env = "p1", target_feature = "atomics"))] -mod libc { - use crate::ffi; - - #[allow(non_camel_case_types)] - pub type pthread_key_t = ffi::c_uint; - - extern "C" { - pub fn pthread_key_create( - key: *mut pthread_key_t, - destructor: unsafe extern "C" fn(*mut ffi::c_void), - ) -> ffi::c_int; - #[allow(dead_code)] - pub fn pthread_getspecific(key: pthread_key_t) -> *mut ffi::c_void; - pub fn pthread_setspecific(key: pthread_key_t, value: *const ffi::c_void) -> ffi::c_int; - pub fn pthread_key_delete(key: pthread_key_t) -> ffi::c_int; - } -} - pub type Key = libc::pthread_key_t; #[inline] diff --git a/std/src/sys/thread_local/mod.rs b/std/src/sys/thread_local/mod.rs index f0a13323ec93f..31d3b43906004 100644 --- a/std/src/sys/thread_local/mod.rs +++ b/std/src/sys/thread_local/mod.rs @@ -86,9 +86,7 @@ pub(crate) mod guard { mod windows; pub(crate) use windows::enable; } else if #[cfg(any( - all(target_family = "wasm", not( - all(target_os = "wasi", target_env = "p1", target_feature = "atomics") - )), + target_family = "wasm", target_os = "uefi", target_os = "zkvm", ))] { @@ -137,7 +135,6 @@ pub(crate) mod key { target_family = "unix", ), target_os = "teeos", - all(target_os = "wasi", target_env = "p1", target_feature = "atomics"), ))] { mod racy; mod unix; diff --git a/std/src/sys/thread_local/os.rs b/std/src/sys/thread_local/os.rs index 00d2e30bd6036..58f291ffdb985 100644 --- a/std/src/sys/thread_local/os.rs +++ b/std/src/sys/thread_local/os.rs @@ -60,6 +60,7 @@ struct Value { } impl Storage { + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "thread_local_internals", issue = "none"))] pub const fn new() -> Storage { Storage { key: LazyKey::new(Some(destroy_value::)), marker: PhantomData } } diff --git a/std/src/sys_common/fs.rs b/std/src/sys_common/fs.rs index bfd684d295b89..a25a7244660bb 100644 --- a/std/src/sys_common/fs.rs +++ b/std/src/sys_common/fs.rs @@ -5,7 +5,7 @@ use crate::io::{self, Error, ErrorKind}; use crate::path::Path; use crate::sys_common::ignore_notfound; -pub(crate) const NOT_FILE_ERROR: Error = io::const_error!( +pub(crate) const NOT_FILE_ERROR: Error = io::const_io_error!( ErrorKind::InvalidInput, "the source path is neither a regular file nor a symlink to a regular file", ); diff --git a/std/src/sys_common/io.rs b/std/src/sys_common/io.rs new file mode 100644 index 0000000000000..6f6f282d432d6 --- /dev/null +++ b/std/src/sys_common/io.rs @@ -0,0 +1,49 @@ +// 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/std/src/sys_common/mod.rs b/std/src/sys_common/mod.rs index 4dc67d26bd8ff..4f7a131f6bb90 100644 --- a/std/src/sys_common/mod.rs +++ b/std/src/sys_common/mod.rs @@ -21,10 +21,25 @@ 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/std/src/sys/net/connection/socket.rs b/std/src/sys_common/net.rs similarity index 96% rename from std/src/sys/net/connection/socket.rs rename to std/src/sys_common/net.rs index 6fe3430b53f79..5a0ad90758101 100644 --- a/std/src/sys/net/connection/socket.rs +++ b/std/src/sys_common/net.rs @@ -5,31 +5,11 @@ 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", @@ -44,11 +24,11 @@ cfg_if::cfg_if! { target_os = "nuttx", target_vendor = "apple", ))] { - use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; - use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; } else { - use c::IPV6_ADD_MEMBERSHIP; - use c::IPV6_DROP_MEMBERSHIP; + use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP; + use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP; } } @@ -142,7 +122,7 @@ pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result *(storage as *const _ as *const c::sockaddr_in6) }))) } - _ => Err(io::const_error!(ErrorKind::InvalidInput, "invalid argument")), + _ => Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid argument")), } } @@ -205,7 +185,7 @@ impl TryFrom<&str> for LookupHost { ($e:expr, $msg:expr) => { match $e { Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, $msg)), + None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, $msg)), } }; } diff --git a/std/src/sys/net/connection/socket/tests.rs b/std/src/sys_common/net/tests.rs similarity index 100% rename from std/src/sys/net/connection/socket/tests.rs rename to std/src/sys_common/net/tests.rs diff --git a/std/src/sys_common/process.rs b/std/src/sys_common/process.rs index 9f61d69d85875..5333ee146f7d6 100644 --- a/std/src/sys_common/process.rs +++ b/std/src/sys_common/process.rs @@ -8,13 +8,19 @@ use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes}; use crate::{env, fmt, io}; // Stores a set of changes to an environment -#[derive(Clone, Default)] +#[derive(Clone)] pub struct CommandEnv { clear: bool, saw_path: bool, vars: BTreeMap>, } +impl Default for CommandEnv { + fn default() -> Self { + CommandEnv { clear: false, saw_path: false, vars: Default::default() } + } +} + impl fmt::Debug for CommandEnv { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut debug_command_env = f.debug_struct("CommandEnv"); diff --git a/std/src/sys_common/wtf8.rs b/std/src/sys_common/wtf8.rs index 6c60d901ee904..666942bb8a10f 100644 --- a/std/src/sys_common/wtf8.rs +++ b/std/src/sys_common/wtf8.rs @@ -204,8 +204,8 @@ impl Wtf8Buf { /// /// Since WTF-8 is a superset of UTF-8, this always succeeds. #[inline] - pub fn from_str(s: &str) -> Wtf8Buf { - Wtf8Buf { bytes: <[_]>::to_vec(s.as_bytes()), is_known_utf8: true } + pub fn from_str(str: &str) -> Wtf8Buf { + Wtf8Buf { bytes: <[_]>::to_vec(str.as_bytes()), is_known_utf8: true } } pub fn clear(&mut self) { diff --git a/std/src/sys_common/wtf8/tests.rs b/std/src/sys_common/wtf8/tests.rs index b57c99a8452a1..bc06eaa2b8fa1 100644 --- a/std/src/sys_common/wtf8/tests.rs +++ b/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::() } - 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,10 +556,9 @@ 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![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9] - ); + assert_eq!(string.encode_wide().collect::>(), vec![ + 0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9 + ]); } #[test] diff --git a/std/src/test_helpers.rs b/std/src/test_helpers.rs deleted file mode 100644 index 7c20f38c863b6..0000000000000 --- a/std/src/test_helpers.rs +++ /dev/null @@ -1,65 +0,0 @@ -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::>(); - 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/std/src/thread/current.rs b/std/src/thread/current.rs index 414711298f047..b9b959f98946b 100644 --- a/std/src/thread/current.rs +++ b/std/src/thread/current.rs @@ -15,7 +15,7 @@ local_pointer! { /// /// We store the thread ID so that it never gets destroyed during the lifetime /// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s. -pub(super) mod id { +mod id { use super::*; cfg_if::cfg_if! { @@ -27,7 +27,7 @@ pub(super) mod id { pub(super) const CHEAP: bool = true; - pub(crate) fn get() -> Option { + pub(super) fn get() -> Option { ID.get() } @@ -44,7 +44,7 @@ pub(super) mod id { pub(super) const CHEAP: bool = false; - pub(crate) fn get() -> Option { + pub(super) fn get() -> Option { let id0 = ID0.get().addr() as u64; let id16 = ID16.get().addr() as u64; let id32 = ID32.get().addr() as u64; @@ -67,7 +67,7 @@ pub(super) mod id { pub(super) const CHEAP: bool = false; - pub(crate) fn get() -> Option { + pub(super) fn get() -> Option { let id0 = ID0.get().addr() as u64; let id32 = ID32.get().addr() as u64; ThreadId::from_u64((id32 << 32) + id0) @@ -85,7 +85,7 @@ pub(super) mod id { pub(super) const CHEAP: bool = true; - pub(crate) fn get() -> Option { + pub(super) fn get() -> Option { let id = ID.get().addr() as u64; ThreadId::from_u64(id) } @@ -112,7 +112,7 @@ pub(super) mod id { /// Tries to set the thread handle for the current thread. Fails if a handle was /// already set or if the thread ID of `thread` would change an already-set ID. -pub(super) fn set_current(thread: Thread) -> Result<(), Thread> { +pub(crate) fn set_current(thread: Thread) -> Result<(), Thread> { if CURRENT.get() != NONE { return Err(thread); } @@ -136,35 +136,32 @@ pub(super) fn set_current(thread: Thread) -> Result<(), Thread> { /// one thread and is guaranteed not to call the global allocator. #[inline] pub(crate) fn current_id() -> ThreadId { - // If accessing the persistent thread ID takes multiple TLS accesses, try + // If accessing the persistant thread ID takes multiple TLS accesses, try // to retrieve it from the current thread handle, which will only take one // TLS access. if !id::CHEAP { - if let Some(id) = try_with_current(|t| t.map(|t| t.id())) { - return id; + let current = CURRENT.get(); + if current > DESTROYED { + unsafe { + let current = ManuallyDrop::new(Thread::from_raw(current)); + return current.id(); + } } } id::get_or_init() } -/// Gets a reference to the handle of the thread that invokes it, if the handle -/// has been initialized. -pub(super) fn try_with_current(f: F) -> R -where - F: FnOnce(Option<&Thread>) -> R, -{ +/// Gets a handle to the thread that invokes it, if the handle has been initialized. +pub(crate) fn try_current() -> Option { let current = CURRENT.get(); if current > DESTROYED { - // SAFETY: `Arc` does not contain interior mutability, so it does not - // matter that the address of the handle might be different depending - // on where this is called. unsafe { let current = ManuallyDrop::new(Thread::from_raw(current)); - f(Some(¤t)) + Some((*current).clone()) } } else { - f(None) + None } } @@ -179,7 +176,7 @@ pub(crate) fn current_or_unnamed() -> Thread { (*current).clone() } } else if current == DESTROYED { - Thread::new(id::get_or_init(), None) + Thread::new_unnamed(id::get_or_init()) } else { init_current(current) } @@ -224,7 +221,7 @@ fn init_current(current: *mut ()) -> Thread { CURRENT.set(BUSY); // If the thread ID was initialized already, use it. let id = id::get_or_init(); - let thread = Thread::new(id, None); + let thread = Thread::new_unnamed(id); // Make sure that `crate::rt::thread_cleanup` will be run, which will // call `drop_current`. @@ -246,17 +243,17 @@ fn init_current(current: *mut ()) -> Thread { // a particular API should be entirely allocation-free, feel free to open // an issue on the Rust repository, we'll see what we can do. rtabort!( - "\n\ - Attempted to access thread-local data while allocating said data.\n\ - Do not access functions that allocate in the global allocator!\n\ - This is a bug in the global allocator.\n\ - " + "\n + Attempted to access thread-local data while allocating said data.\n + Do not access functions that allocate in the global allocator!\n + This is a bug in the global allocator.\n + " ) } else { debug_assert_eq!(current, DESTROYED); panic!( - "use of std::thread::current() is not possible after the thread's \ - local data has been destroyed" + "use of std::thread::current() is not possible after the thread's + local data has been destroyed" ) } } diff --git a/std/src/thread/local.rs b/std/src/thread/local.rs index ca04aa4ada497..9edb3fa41933d 100644 --- a/std/src/thread/local.rs +++ b/std/src/thread/local.rs @@ -2,11 +2,17 @@ #![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; -/// A thread local storage (TLS) key which owns its contents. +/// A thread local storage key which owns its contents. /// /// This key uses the fastest possible implementation available to it for the /// target platform. It is instantiated with the [`thread_local!`] macro and the @@ -224,14 +230,6 @@ impl fmt::Display for AccessError { #[stable(feature = "thread_local_try_with", since = "1.26.0")] impl Error for AccessError {} -// This ensures the panicking code is outlined from `with` for `LocalKey`. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] -#[track_caller] -#[cold] -fn panic_access_error(err: AccessError) -> ! { - panic!("cannot access a Thread Local Storage value during or after destruction: {err:?}") -} - impl LocalKey { #[doc(hidden)] #[unstable( @@ -239,6 +237,7 @@ impl LocalKey { reason = "recently added to create a key", issue = "none" )] + #[cfg_attr(bootstrap, rustc_const_unstable(feature = "thread_local_internals", issue = "none"))] pub const unsafe fn new(inner: fn(Option<&mut Option>) -> *const T) -> LocalKey { LocalKey { inner } } @@ -253,28 +252,15 @@ impl LocalKey { /// This function will `panic!()` if the key currently has its /// destructor running, and it **may** panic if the destructor has /// previously been run for this thread. - /// - /// # Examples - /// - /// ``` - /// thread_local! { - /// pub static STATIC: String = String::from("I am"); - /// } - /// - /// assert_eq!( - /// STATIC.with(|original_value| format!("{original_value} initialized")), - /// "I am initialized", - /// ); - /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn with(&'static self, f: F) -> R where F: FnOnce(&T) -> R, { - match self.try_with(f) { - Ok(r) => r, - Err(err) => panic_access_error(err), - } + self.try_with(f).expect( + "cannot access a Thread Local Storage value \ + during or after destruction", + ) } /// Acquires a reference to the value in this TLS key. @@ -287,19 +273,6 @@ impl LocalKey { /// /// This function will still `panic!()` if the key is uninitialized and the /// key's initializer panics. - /// - /// # Examples - /// - /// ``` - /// thread_local! { - /// pub static STATIC: String = String::from("I am"); - /// } - /// - /// assert_eq!( - /// STATIC.try_with(|original_value| format!("{original_value} initialized")), - /// Ok(String::from("I am initialized")), - /// ); - /// ``` #[stable(feature = "thread_local_try_with", since = "1.26.0")] #[inline] pub fn try_with(&'static self, f: F) -> Result @@ -329,10 +302,10 @@ impl LocalKey { let mut init = Some(init); let reference = unsafe { - match (self.inner)(Some(&mut init)).as_ref() { - Some(r) => r, - None => panic_access_error(AccessError), - } + (self.inner)(Some(&mut init)).as_ref().expect( + "cannot access a Thread Local Storage value \ + during or after destruction", + ) }; f(init, reference) @@ -479,7 +452,7 @@ impl LocalKey> { /// Panics if the key currently has its destructor running, /// and it **may** panic if the destructor has previously been run for this thread. /// - /// # Examples + /// # Example /// /// ``` /// use std::cell::RefCell; @@ -510,7 +483,7 @@ impl LocalKey> { /// Panics if the key currently has its destructor running, /// and it **may** panic if the destructor has previously been run for this thread. /// - /// # Examples + /// # Example /// /// ``` /// use std::cell::RefCell; diff --git a/std/tests/thread_local/dynamic_tests.rs b/std/src/thread/local/dynamic_tests.rs similarity index 89% rename from std/tests/thread_local/dynamic_tests.rs rename to std/src/thread/local/dynamic_tests.rs index 454462b392510..dd18004164824 100644 --- a/std/tests/thread_local/dynamic_tests.rs +++ b/std/src/thread/local/dynamic_tests.rs @@ -1,6 +1,6 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use std::thread_local; +use crate::cell::RefCell; +use crate::collections::HashMap; +use crate::thread_local; #[test] fn smoke() { diff --git a/std/tests/thread_local/tests.rs b/std/src/thread/local/tests.rs similarity index 98% rename from std/tests/thread_local/tests.rs rename to std/src/thread/local/tests.rs index aa020c2559cc5..9d4f52a09218e 100644 --- a/std/tests/thread_local/tests.rs +++ b/std/src/thread/local/tests.rs @@ -1,8 +1,8 @@ -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; +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; #[derive(Clone, Default)] struct Signal(Arc<(Mutex, Condvar)>); diff --git a/std/src/thread/mod.rs b/std/src/thread/mod.rs index 59b395336f2e3..2ff44fcd4c6b7 100644 --- a/std/src/thread/mod.rs +++ b/std/src/thread/mod.rs @@ -158,9 +158,12 @@ #[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] mod tests; +use core::cell::SyncUnsafeCell; +use core::ffi::CStr; +use core::mem::MaybeUninit; + use crate::any::Any; use crate::cell::UnsafeCell; -use crate::ffi::CStr; use crate::marker::PhantomData; use crate::mem::{self, ManuallyDrop, forget}; use crate::num::NonZero; @@ -183,8 +186,7 @@ mod current; #[stable(feature = "rust1", since = "1.0.0")] pub use current::current; -pub(crate) use current::{current_id, current_or_unnamed, drop_current}; -use current::{set_current, try_with_current}; +pub(crate) use current::{current_id, current_or_unnamed, drop_current, set_current, try_current}; mod spawnhook; @@ -389,7 +391,6 @@ impl Builder { /// handler.join().unwrap(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn spawn(self, f: F) -> io::Result> where F: FnOnce() -> T, @@ -457,7 +458,6 @@ impl Builder { /// /// [`io::Result`]: crate::io::Result #[stable(feature = "thread_spawn_unchecked", since = "1.82.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> where F: FnOnce() -> T, @@ -467,7 +467,6 @@ impl Builder { Ok(JoinHandle(unsafe { self.spawn_unchecked_(f, None) }?)) } - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces unsafe fn spawn_unchecked_<'scope, F, T>( self, f: F, @@ -499,7 +498,10 @@ impl Builder { }); let id = ThreadId::new(); - let my_thread = Thread::new(id, name); + let my_thread = match name { + Some(name) => Thread::new(id, name.into()), + None => Thread::new_unnamed(id), + }; let hooks = if no_hooks { spawnhook::ChildSpawnHooks::default() @@ -719,7 +721,6 @@ impl Builder { /// [`join`]: JoinHandle::join /// [`Err`]: crate::result::Result::Err #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn spawn(f: F) -> JoinHandle where F: FnOnce() -> T, @@ -1020,11 +1021,11 @@ impl Drop for PanicGuard { /// /// # Memory Ordering /// -/// Calls to `unpark` _synchronize-with_ calls to `park`, meaning that memory +/// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory /// operations performed before a call to `unpark` are made visible to the thread that /// consumes the token and returns from `park`. Note that all `park` and `unpark` -/// operations for a given thread form a total order and _all_ prior `unpark` operations -/// synchronize-with `park`. +/// operations for a given thread form a total order and `park` synchronizes-with +/// _all_ prior `unpark` operations. /// /// In atomic ordering terms, `unpark` performs a `Release` operation and `park` /// performs the corresponding `Acquire` operation. Calls to `unpark` for the same @@ -1230,7 +1231,7 @@ impl ThreadId { } } - #[cfg(any(not(target_thread_local), target_has_atomic = "64"))] + #[cfg(not(target_thread_local))] fn from_u64(v: u64) -> Option { NonZero::new(v).map(ThreadId) } @@ -1256,14 +1257,29 @@ impl ThreadId { // This module ensures private fields are kept private, which is necessary to enforce the safety requirements. mod thread_name_string { + use core::str; + use crate::ffi::{CStr, CString}; - use crate::str; /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. pub(crate) struct ThreadNameString { inner: CString, } + impl ThreadNameString { + pub fn as_str(&self) -> &str { + // SAFETY: `self.inner` is only initialised via `String`, which upholds the validity invariant of `str`. + unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } + } + } + + impl core::ops::Deref for ThreadNameString { + type Target = CStr; + fn deref(&self) -> &CStr { + &self.inner + } + } + impl From for ThreadNameString { fn from(s: String) -> Self { Self { @@ -1271,124 +1287,82 @@ mod thread_name_string { } } } +} +pub(crate) use thread_name_string::ThreadNameString; - impl ThreadNameString { - pub fn as_cstr(&self) -> &CStr { - &self.inner - } +static MAIN_THREAD_INFO: SyncUnsafeCell<(MaybeUninit, MaybeUninit)> = + SyncUnsafeCell::new((MaybeUninit::uninit(), MaybeUninit::uninit())); - pub fn as_str(&self) -> &str { - // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. - unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } - } - } +/// The internal representation of a `Thread` that is not the main thread. +struct OtherInner { + name: Option, + id: ThreadId, + parker: Parker, } -use thread_name_string::ThreadNameString; - -/// Store the ID of the main thread. -/// -/// The thread handle for the main thread is created lazily, and this might even -/// happen pre-main. Since not every platform has a way to identify the main -/// thread when that happens – macOS's `pthread_main_np` function being a notable -/// exception – we cannot assign it the right name right then. Instead, in our -/// runtime startup code, we remember the thread ID of the main thread (through -/// this modules `set` function) and use it to identify the main thread from then -/// on. This works reliably and has the additional advantage that we can report -/// the right thread name on main even after the thread handle has been destroyed. -/// Note however that this also means that the name reported in pre-main functions -/// will be incorrect, but that's just something we have to live with. -pub(crate) mod main_thread { - cfg_if::cfg_if! { - if #[cfg(target_has_atomic = "64")] { - use super::ThreadId; - use crate::sync::atomic::AtomicU64; - use crate::sync::atomic::Ordering::Relaxed; - - static MAIN: AtomicU64 = AtomicU64::new(0); - - pub(super) fn get() -> Option { - ThreadId::from_u64(MAIN.load(Relaxed)) - } +/// The internal representation of a `Thread` handle. +#[derive(Clone)] +enum Inner { + /// Represents the main thread. May only be constructed by Thread::new_main. + Main(&'static (ThreadId, Parker)), + /// Represents any other thread. + Other(Pin>), +} - /// # Safety - /// May only be called once. - pub(crate) unsafe fn set(id: ThreadId) { - MAIN.store(id.as_u64().get(), Relaxed) - } - } else { - use super::ThreadId; - use crate::mem::MaybeUninit; - use crate::sync::atomic::AtomicBool; - use crate::sync::atomic::Ordering::{Acquire, Release}; - - static INIT: AtomicBool = AtomicBool::new(false); - static mut MAIN: MaybeUninit = MaybeUninit::uninit(); - - pub(super) fn get() -> Option { - if INIT.load(Acquire) { - Some(unsafe { MAIN.assume_init() }) - } else { - None - } - } +impl Inner { + fn id(&self) -> ThreadId { + match self { + Self::Main((thread_id, _)) => *thread_id, + Self::Other(other) => other.id, + } + } - /// # Safety - /// May only be called once. - pub(crate) unsafe fn set(id: ThreadId) { - unsafe { MAIN = MaybeUninit::new(id) }; - INIT.store(true, Release); - } + fn cname(&self) -> Option<&CStr> { + match self { + Self::Main(_) => Some(c"main"), + Self::Other(other) => other.name.as_deref(), } } -} -/// Run a function with the current thread's name. -/// -/// Modulo thread local accesses, this function is safe to call from signal -/// handlers and in similar circumstances where allocations are not possible. -pub(crate) fn with_current_name(f: F) -> R -where - F: FnOnce(Option<&str>) -> R, -{ - try_with_current(|thread| { - if let Some(thread) = thread { - // If there is a current thread handle, try to use the name stored - // there. - if let Some(name) = &thread.inner.name { - return f(Some(name.as_str())); - } else if Some(thread.inner.id) == main_thread::get() { - // The main thread doesn't store its name in the handle, we must - // identify it through its ID. Since we already have the `Thread`, - // we can retrieve the ID from it instead of going through another - // thread local. - return f(Some("main")); - } - } else if let Some(main) = main_thread::get() - && let Some(id) = current::id::get() - && id == main - { - // The main thread doesn't always have a thread handle, we must - // identify it through its ID instead. The checks are ordered so - // that the current ID is only loaded if it is actually needed, - // since loading it from TLS might need multiple expensive accesses. - return f(Some("main")); + fn name(&self) -> Option<&str> { + match self { + Self::Main(_) => Some("main"), + Self::Other(other) => other.name.as_ref().map(ThreadNameString::as_str), } + } - f(None) - }) -} + fn into_raw(self) -> *const () { + match self { + // Just return the pointer to `MAIN_THREAD_INFO`. + Self::Main(ptr) => crate::ptr::from_ref(ptr).cast(), + Self::Other(arc) => { + // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. + let inner = unsafe { Pin::into_inner_unchecked(arc) }; + Arc::into_raw(inner) as *const () + } + } + } -/// The internal representation of a `Thread` handle -struct Inner { - name: Option, - id: ThreadId, - parker: Parker, -} + /// # Safety + /// + /// See [`Thread::from_raw`]. + unsafe fn from_raw(ptr: *const ()) -> Self { + // If the pointer is to `MAIN_THREAD_INFO`, we know it is the `Main` variant. + if crate::ptr::eq(ptr.cast(), &MAIN_THREAD_INFO) { + Self::Main(unsafe { &*ptr.cast() }) + } else { + // Safety: Upheld by caller + Self::Other(unsafe { Pin::new_unchecked(Arc::from_raw(ptr as *const OtherInner)) }) + } + } -impl Inner { - fn parker(self: Pin<&Self>) -> Pin<&Parker> { - unsafe { Pin::map_unchecked(self, |inner| &inner.parker) } + fn parker(&self) -> Pin<&Parker> { + match self { + Self::Main((_, parker_ref)) => Pin::static_ref(parker_ref), + Self::Other(inner) => unsafe { + Pin::map_unchecked(inner.as_ref(), |inner| &inner.parker) + }, + } } } @@ -1412,21 +1386,47 @@ impl Inner { /// docs of [`Builder`] and [`spawn`] for more details. /// /// [`thread::current`]: current::current -pub struct Thread { - inner: Pin>, -} +pub struct Thread(Inner); impl Thread { - pub(crate) fn new(id: ThreadId, name: Option) -> Thread { - let name = name.map(ThreadNameString::from); + /// Used only internally to construct a thread object without spawning. + pub(crate) fn new(id: ThreadId, name: String) -> Thread { + Self::new_inner(id, Some(ThreadNameString::from(name))) + } + pub(crate) fn new_unnamed(id: ThreadId) -> Thread { + Self::new_inner(id, None) + } + + /// Used in runtime to construct main thread + /// + /// # Safety + /// + /// This must only ever be called once, and must be called on the main thread. + pub(crate) unsafe fn new_main(thread_id: ThreadId) -> Thread { + // Safety: As this is only called once and on the main thread, nothing else is accessing MAIN_THREAD_INFO + // as the only other read occurs in `main_thread_info` *after* the main thread has been constructed, + // and this function is the only one that constructs the main thread. + // + // Pre-main thread spawning cannot hit this either, as the caller promises that this is only called on the main thread. + let main_thread_info = unsafe { &mut *MAIN_THREAD_INFO.get() }; + + unsafe { Parker::new_in_place((&raw mut main_thread_info.1).cast()) }; + main_thread_info.0.write(thread_id); + + // Store a `'static` ref to the initialised ThreadId and Parker, + // to avoid having to repeatedly prove initialisation. + Self(Inner::Main(unsafe { &*MAIN_THREAD_INFO.get().cast() })) + } + + fn new_inner(id: ThreadId, name: Option) -> Thread { // We have to use `unsafe` here to construct the `Parker` in-place, // which is required for the UNIX implementation. // // SAFETY: We pin the Arc immediately after creation, so its address never // changes. let inner = unsafe { - let mut arc = Arc::::new_uninit(); + let mut arc = Arc::::new_uninit(); let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); (&raw mut (*ptr).name).write(name); (&raw mut (*ptr).id).write(id); @@ -1434,7 +1434,7 @@ impl Thread { Pin::new_unchecked(arc.assume_init()) }; - Thread { inner } + Self(Inner::Other(inner)) } /// Like the public [`park`], but callable on any handle. This is used to @@ -1443,7 +1443,7 @@ impl Thread { /// # Safety /// May only be called from the thread to which this handle belongs. pub(crate) unsafe fn park(&self) { - unsafe { self.inner.as_ref().parker().park() } + unsafe { self.0.parker().park() } } /// Like the public [`park_timeout`], but callable on any handle. This is @@ -1452,7 +1452,7 @@ impl Thread { /// # Safety /// May only be called from the thread to which this handle belongs. pub(crate) unsafe fn park_timeout(&self, dur: Duration) { - unsafe { self.inner.as_ref().parker().park_timeout(dur) } + unsafe { self.0.parker().park_timeout(dur) } } /// Atomically makes the handle's token available if it is not already. @@ -1488,7 +1488,7 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn unpark(&self) { - self.inner.as_ref().parker().unpark(); + self.0.parker().unpark(); } /// Gets the thread's unique identifier. @@ -1508,7 +1508,7 @@ impl Thread { #[stable(feature = "thread_id", since = "1.19.0")] #[must_use] pub fn id(&self) -> ThreadId { - self.inner.id + self.0.id() } /// Gets the thread's name. @@ -1551,13 +1551,11 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub fn name(&self) -> Option<&str> { - if let Some(name) = &self.inner.name { - Some(name.as_str()) - } else if main_thread::get() == Some(self.inner.id) { - Some("main") - } else { - None - } + self.0.name() + } + + fn cname(&self) -> Option<&CStr> { + self.0.cname() } /// Consumes the `Thread`, returning a raw pointer. @@ -1581,9 +1579,7 @@ impl Thread { /// ``` #[unstable(feature = "thread_raw", issue = "97523")] pub fn into_raw(self) -> *const () { - // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. - let inner = unsafe { Pin::into_inner_unchecked(self.inner) }; - Arc::into_raw(inner) as *const () + self.0.into_raw() } /// Constructs a `Thread` from a raw pointer. @@ -1605,17 +1601,7 @@ impl Thread { #[unstable(feature = "thread_raw", issue = "97523")] pub unsafe fn from_raw(ptr: *const ()) -> Thread { // Safety: Upheld by caller. - unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } } - } - - fn cname(&self) -> Option<&CStr> { - if let Some(name) = &self.inner.name { - Some(name.as_cstr()) - } else if main_thread::get() == Some(self.inner.id) { - Some(c"main") - } else { - None - } + unsafe { Thread(Inner::from_raw(ptr)) } } } diff --git a/std/src/time.rs b/std/src/time.rs index 88b3e9e0ceba0..9f4f8a0d0880c 100644 --- a/std/src/time.rs +++ b/std/src/time.rs @@ -31,6 +31,9 @@ #![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/std/tests/time.rs b/std/src/time/tests.rs similarity index 81% rename from std/tests/time.rs rename to std/src/time/tests.rs index 40709eae37cfc..e88f2d5e80676 100644 --- a/std/tests/time.rs +++ b/std/src/time/tests.rs @@ -1,7 +1,9 @@ -#![feature(duration_constants)] +use core::fmt::Debug; -use std::fmt::Debug; -use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; +#[cfg(not(target_arch = "wasm32"))] +use test::{Bencher, black_box}; + +use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; macro_rules! assert_almost_eq { ($a:expr, $b:expr) => {{ @@ -27,10 +29,10 @@ fn instant_monotonic() { #[test] #[cfg(not(target_arch = "wasm32"))] -fn instant_monotonic_concurrent() -> std::thread::Result<()> { +fn instant_monotonic_concurrent() -> crate::thread::Result<()> { let threads: Vec<_> = (0..8) .map(|_| { - std::thread::spawn(|| { + crate::thread::spawn(|| { let mut old = Instant::now(); let count = if cfg!(miri) { 1_000 } else { 5_000_000 }; for _ in 0..count { @@ -227,3 +229,46 @@ 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/std/tests/common/mod.rs b/std/tests/common/mod.rs index 1e8e4cced6c03..7cf70c725e411 100644 --- a/std/tests/common/mod.rs +++ b/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::test_helpers +// Copied from std::sys_common::io pub(crate) struct TempDir(PathBuf); impl TempDir { diff --git a/std/tests/env.rs b/std/tests/env.rs index e754cf8263b0f..4e472b4ce9953 100644 --- a/std/tests/env.rs +++ b/std/tests/env.rs @@ -1,123 +1,163 @@ use std::env::*; -use std::path::Path; +use std::ffi::{OsStr, OsString}; + +use rand::distributions::{Alphanumeric, DistString}; mod common; +use std::thread; -#[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()); +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, b: Option<&str>) { + assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); } #[test] -fn test() { - assert!((!Path::new("test-path").is_absolute())); +fn test_set_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + eq(var_os(&n), Some("VALUE")); +} - #[cfg(not(target_env = "sgx"))] - current_dir().unwrap(); +#[test] +fn test_remove_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + remove_var(&n); + eq(var_os(&n), None); } #[test] -#[cfg(windows)] -fn split_paths_windows() { - use std::path::PathBuf; +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("")); +} - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() +#[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)); +} - 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_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] -#[cfg(unix)] -fn split_paths_unix() { - use std::path::PathBuf; +fn test_env_set_var() { + let n = make_rand_name(); - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() - } + let mut e = vars_os(); + set_var(&n, "VALUE"); + assert!(!e.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"])); + assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); } #[test] -#[cfg(unix)] -fn join_paths_unix() { - use std::ffi::OsStr; +#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))] +#[allow(deprecated)] +fn env_home_dir() { + use std::path::PathBuf; - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) + fn var_to_os_string(var: Result) -> Option { + match var { + Ok(var) => Some(OsString::from(var)), + Err(VarError::NotUnicode(var)) => Some(var), + _ => None, + } } - 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()); -} + cfg_if::cfg_if! { + if #[cfg(unix)] { + let oldhome = var_to_os_string(var("HOME")); -#[test] -#[cfg(windows)] -fn join_paths_windows() { - use std::ffi::OsStr; + set_var("HOME", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } + 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"))); + } - 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()); -} + 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")); -#[test] -fn args_debug() { - assert_eq!( - format!("Args {{ inner: {:?} }}", args().collect::>()), - format!("{:?}", args()) - ); -} + remove_var("HOME"); + remove_var("USERPROFILE"); -#[test] -fn args_os_debug() { - assert_eq!( - format!("ArgsOs {{ inner: {:?} }}", args_os().collect::>()), - format!("{:?}", args_os()) - ); -} + assert!(home_dir().is_some()); -#[test] -fn vars_debug() { - assert_eq!( - format!("Vars {{ inner: {:?} }}", vars().collect::>()), - format!("{:?}", vars()) - ); + set_var("HOME", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + + set_var("USERPROFILE", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + set_var("HOME", "/home/MountainView"); + set_var("USERPROFILE", "/home/PaloAlto"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + remove_var("USERPROFILE"); + + if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } + if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); } + } + } } -#[test] -fn vars_os_debug() { - assert_eq!( - format!("VarsOs {{ inner: {:?} }}", vars_os().collect::>()), - format!("{:?}", vars_os()) - ); +#[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/std/tests/env_modify.rs b/std/tests/env_modify.rs deleted file mode 100644 index 6074744735005..0000000000000 --- a/std/tests/env_modify.rs +++ /dev/null @@ -1,166 +0,0 @@ -// 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, 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) -> Option { - 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/std/tests/floats/lib.rs b/std/tests/floats/lib.rs deleted file mode 100644 index ad82f1a44e711..0000000000000 --- a/std/tests/floats/lib.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![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(ten: T, two: T) -where - T: PartialEq - + Add - + Sub - + Mul - + Div - + Rem - + 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/std/tests/istr.rs b/std/tests/istr.rs index e481872977abf..9a127ae803e77 100644 --- a/std/tests/istr.rs +++ b/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/std/tests/pipe_subprocess.rs b/std/tests/pipe_subprocess.rs index 00d99a578d580..1535742a83a21 100644 --- a/std/tests/pipe_subprocess.rs +++ b/std/tests/pipe_subprocess.rs @@ -1,9 +1,10 @@ #![feature(anonymous_pipe)] fn main() { - #[cfg(all(not(miri), any(unix, windows), not(target_os = "emscripten")))] + #[cfg(all(not(miri), any(unix, windows)))] { - use std::io::{Read, pipe}; + use std::io::Read; + use std::pipe::pipe; use std::{env, process}; if env::var("I_AM_THE_CHILD").is_ok() { diff --git a/std/tests/process_spawning.rs b/std/tests/process_spawning.rs index 43b45cb2d2b5c..3e72e371ade19 100644 --- a/std/tests/process_spawning.rs +++ b/std/tests/process_spawning.rs @@ -5,8 +5,7 @@ use std::{env, fs, process, str}; mod common; #[test] -// Process spawning not supported by Miri, Emscripten and wasi -#[cfg_attr(any(miri, target_os = "emscripten", target_os = "wasi"), ignore)] +#[cfg_attr(any(miri, target_os = "wasi"), ignore)] // Process spawning not supported by Miri and wasi 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/std/tests/seq-compare.rs b/std/tests/seq-compare.rs index ec39c5b603ccc..221f1c7cabde5 100644 --- a/std/tests/seq-compare.rs +++ b/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/std/tests/sync/lib.rs b/std/tests/sync/lib.rs deleted file mode 100644 index 51190f0894fb7..0000000000000 --- a/std/tests/sync/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![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/std/tests/thread_local/lib.rs b/std/tests/thread_local/lib.rs deleted file mode 100644 index c52914354253c..0000000000000 --- a/std/tests/thread_local/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] -mod tests; - -mod dynamic_tests; diff --git a/std/tests/win_delete_self.rs b/std/tests/win_delete_self.rs deleted file mode 100644 index 1c3ce4d710c38..0000000000000 --- a/std/tests/win_delete_self.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![cfg(windows)] - -/// Attempting to delete a running binary should return an error on Windows. -#[test] -fn win_delete_self() { - let path = std::env::current_exe().unwrap(); - assert!(std::fs::remove_file(path).is_err()); -} diff --git a/stdarch b/stdarch index 684de0d6fef70..e5e00aab0a8c8 160000 --- a/stdarch +++ b/stdarch @@ -1 +1 @@ -Subproject commit 684de0d6fef708cae08214fef9643dd9ec7296e1 +Subproject commit e5e00aab0a8c8fa35fb7865e88fa82366f615c53 diff --git a/test/src/cli.rs b/test/src/cli.rs index ef6786f431670..4ccd825bf8dd3 100644 --- a/test/src/cli.rs +++ b/test/src/cli.rs @@ -1,7 +1,7 @@ //! Module converting command-line arguments into test configuration. use std::env; -use std::io::{self, IsTerminal, Write}; +use std::io::{self, IsTerminal}; use std::path::PathBuf; use super::options::{ColorConfig, Options, OutputFormat, RunIgnored}; @@ -44,7 +44,7 @@ impl TestOpts { } /// Result of parsing the options. -pub(crate) type OptRes = Result; +pub type OptRes = Result; /// Result of parsing the option part. type OptPartRes = Result; @@ -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 (deprecated)", "PATH") + .optopt("", "logfile", "Write logs to the specified file", "PATH") .optflag( "", "nocapture", @@ -281,10 +281,6 @@ 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/test/src/console.rs b/test/src/console.rs index 024ef48fc5104..4d4cdcf4d7b6c 100644 --- a/test/src/console.rs +++ b/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(crate) enum OutputLocation { +pub enum OutputLocation { Pretty(Box), Raw(T), } @@ -41,7 +41,7 @@ impl Write for OutputLocation { } } -pub(crate) struct ConsoleTestDiscoveryState { +pub struct ConsoleTestDiscoveryState { pub log_out: Option, pub tests: usize, pub benchmarks: usize, @@ -49,7 +49,7 @@ pub(crate) struct ConsoleTestDiscoveryState { } impl ConsoleTestDiscoveryState { - pub(crate) fn new(opts: &TestOpts) -> io::Result { + pub fn new(opts: &TestOpts) -> io::Result { 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(crate) fn write_log(&mut self, msg: F) -> io::Result<()> + pub fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -74,7 +74,7 @@ impl ConsoleTestDiscoveryState { } } -pub(crate) struct ConsoleTestState { +pub struct ConsoleTestState { pub log_out: Option, pub total: usize, pub passed: usize, @@ -92,7 +92,7 @@ pub(crate) struct ConsoleTestState { } impl ConsoleTestState { - pub(crate) fn new(opts: &TestOpts) -> io::Result { + pub fn new(opts: &TestOpts) -> io::Result { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -116,7 +116,7 @@ impl ConsoleTestState { }) } - pub(crate) fn write_log(&mut self, msg: F) -> io::Result<()> + pub fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -131,7 +131,7 @@ impl ConsoleTestState { } } - pub(crate) fn write_log_result( + pub 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(crate) fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { +pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout().lock()), Some(t) => OutputLocation::Pretty(t), diff --git a/test/src/formatters/json.rs b/test/src/formatters/json.rs index 92c1c0716f1f2..aa1c50641cb54 100644 --- a/test/src/formatters/json.rs +++ b/test/src/formatters/json.rs @@ -13,7 +13,7 @@ pub(crate) struct JsonFormatter { } impl JsonFormatter { - pub(crate) fn new(out: OutputLocation) -> Self { + pub fn new(out: OutputLocation) -> Self { Self { out } } diff --git a/test/src/formatters/junit.rs b/test/src/formatters/junit.rs index 57b1b0feceefc..96b432008404b 100644 --- a/test/src/formatters/junit.rs +++ b/test/src/formatters/junit.rs @@ -8,13 +8,13 @@ use crate::test_result::TestResult; use crate::time; use crate::types::{TestDesc, TestType}; -pub(crate) struct JunitFormatter { +pub struct JunitFormatter { out: OutputLocation, results: Vec<(TestDesc, TestResult, Duration, Vec)>, } impl JunitFormatter { - pub(crate) fn new(out: OutputLocation) -> Self { + pub fn new(out: OutputLocation) -> Self { Self { out, results: Vec::new() } } diff --git a/test/src/formatters/pretty.rs b/test/src/formatters/pretty.rs index bf3fc40db4117..7089eae4330a0 100644 --- a/test/src/formatters/pretty.rs +++ b/test/src/formatters/pretty.rs @@ -20,7 +20,7 @@ pub(crate) struct PrettyFormatter { } impl PrettyFormatter { - pub(crate) fn new( + pub fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -31,19 +31,19 @@ impl PrettyFormatter { } #[cfg(test)] - pub(crate) fn output_location(&self) -> &OutputLocation { + pub fn output_location(&self) -> &OutputLocation { &self.out } - pub(crate) fn write_ok(&mut self) -> io::Result<()> { + pub fn write_ok(&mut self) -> io::Result<()> { self.write_short_result("ok", term::color::GREEN) } - pub(crate) fn write_failed(&mut self) -> io::Result<()> { + pub fn write_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED", term::color::RED) } - pub(crate) fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { + pub 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 PrettyFormatter { } } - pub(crate) fn write_time_failed(&mut self) -> io::Result<()> { + pub fn write_time_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED (time limit exceeded)", term::color::RED) } - pub(crate) fn write_bench(&mut self) -> io::Result<()> { + pub fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub(crate) fn write_short_result( + pub fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -67,7 +67,7 @@ impl PrettyFormatter { self.write_pretty(result, color) } - pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub 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 PrettyFormatter { } } - pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub fn write_plain>(&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 PrettyFormatter { Ok(()) } - pub(crate) fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.not_failures, "successes") } - pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.failures, "failures") } - pub(crate) fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.time_failures, "failures (time limit exceeded)") } diff --git a/test/src/formatters/terse.rs b/test/src/formatters/terse.rs index b28120ab56e69..534aa2f33110c 100644 --- a/test/src/formatters/terse.rs +++ b/test/src/formatters/terse.rs @@ -25,7 +25,7 @@ pub(crate) struct TerseFormatter { } impl TerseFormatter { - pub(crate) fn new( + pub fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -42,11 +42,11 @@ impl TerseFormatter { } } - pub(crate) fn write_ok(&mut self) -> io::Result<()> { + pub fn write_ok(&mut self) -> io::Result<()> { self.write_short_result(".", term::color::GREEN) } - pub(crate) fn write_failed(&mut self, name: &str) -> io::Result<()> { + pub 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 TerseFormatter { self.write_plain("\n") } - pub(crate) fn write_ignored(&mut self) -> io::Result<()> { + pub fn write_ignored(&mut self) -> io::Result<()> { self.write_short_result("i", term::color::YELLOW) } - pub(crate) fn write_bench(&mut self) -> io::Result<()> { + pub fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub(crate) fn write_short_result( + pub fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -95,7 +95,7 @@ impl TerseFormatter { Ok(()) } - pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub 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 TerseFormatter { } } - pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub fn write_plain>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() } - pub(crate) fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub 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 TerseFormatter { Ok(()) } - pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub 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/test/src/helpers/concurrency.rs b/test/src/helpers/concurrency.rs index 6648b669125f7..b1545cbec438a 100644 --- a/test/src/helpers/concurrency.rs +++ b/test/src/helpers/concurrency.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use std::{env, thread}; -pub(crate) fn get_concurrency() -> usize { +pub fn get_concurrency() -> usize { if let Ok(value) = env::var("RUST_TEST_THREADS") { match value.parse::>().ok() { Some(n) => n.get(), diff --git a/test/src/helpers/mod.rs b/test/src/helpers/mod.rs index 2fb29b4c7bee5..3c79b90b16754 100644 --- a/test/src/helpers/mod.rs +++ b/test/src/helpers/mod.rs @@ -1,6 +1,6 @@ //! Module with common helpers not directly related to tests //! but used in `libtest`. -pub(crate) mod concurrency; -pub(crate) mod metrics; -pub(crate) mod shuffle; +pub mod concurrency; +pub mod metrics; +pub mod shuffle; diff --git a/test/src/helpers/shuffle.rs b/test/src/helpers/shuffle.rs index 53d1d0e42d4e8..14389eb0e37af 100644 --- a/test/src/helpers/shuffle.rs +++ b/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(crate) fn get_shuffle_seed(opts: &TestOpts) -> Option { +pub fn get_shuffle_seed(opts: &TestOpts) -> Option { opts.shuffle_seed.or_else(|| { if opts.shuffle { Some( @@ -19,7 +19,7 @@ pub(crate) fn get_shuffle_seed(opts: &TestOpts) -> Option { }) } -pub(crate) fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { +pub 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/test/src/lib.rs b/test/src/lib.rs index 54f7e4ae79f18..47407df909bdf 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -27,7 +27,6 @@ #![feature(thread_spawn_hook)] #![allow(internal_features)] #![warn(rustdoc::unescaped_backticks)] -#![warn(unreachable_pub)] pub use cli::TestOpts; diff --git a/test/src/options.rs b/test/src/options.rs index 7a5c55f4e2411..3eaad59474a12 100644 --- a/test/src/options.rs +++ b/test/src/options.rs @@ -2,7 +2,7 @@ /// Number of times to run a benchmarked function #[derive(Clone, PartialEq, Eq)] -pub(crate) enum BenchMode { +pub enum BenchMode { Auto, Single, } diff --git a/test/src/stats/tests.rs b/test/src/stats/tests.rs index 7804ddc929132..4b209dcf214da 100644 --- a/test/src/stats/tests.rs +++ b/test/src/stats/tests.rs @@ -573,13 +573,13 @@ fn test_sum_f64_between_ints_that_sum_to_0() { } #[bench] -fn sum_three_items(b: &mut Bencher) { +pub fn sum_three_items(b: &mut Bencher) { b.iter(|| { [1e20f64, 1.5f64, -1e20f64].sum(); }) } #[bench] -fn sum_many_f64(b: &mut Bencher) { +pub 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::>(); @@ -589,4 +589,4 @@ fn sum_many_f64(b: &mut Bencher) { } #[bench] -fn no_iter(_: &mut Bencher) {} +pub fn no_iter(_: &mut Bencher) {} diff --git a/test/src/term.rs b/test/src/term.rs index d9880a776406d..e736e85d46966 100644 --- a/test/src/term.rs +++ b/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(crate) trait Terminal: Write { +pub 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/test/src/test_result.rs b/test/src/test_result.rs index 73dcc2e2a0cca..79fe07bc1ac5c 100644 --- a/test/src/test_result.rs +++ b/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(crate) const TR_OK: i32 = 50; +pub 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(crate) fn calc_result<'a>( +pub fn calc_result<'a>( desc: &TestDesc, task_result: Result<(), &'a (dyn Any + 'static + Send)>, time_opts: Option<&time::TestTimeOptions>, @@ -93,7 +93,7 @@ pub(crate) fn calc_result<'a>( } /// Creates a `TestResult` depending on the exit code of test subprocess. -pub(crate) fn get_result_from_exit_code( +pub fn get_result_from_exit_code( desc: &TestDesc, status: ExitStatus, time_opts: Option<&time::TestTimeOptions>, diff --git a/test/src/tests.rs b/test/src/tests.rs index 47f581fefae1f..e85e61090a91b 100644 --- a/test/src/tests.rs +++ b/test/src/tests.rs @@ -78,7 +78,7 @@ fn one_ignored_one_unignored_test() -> Vec { } #[test] -fn do_not_run_ignored_tests() { +pub fn do_not_run_ignored_tests() { fn f() -> Result<(), String> { panic!(); } @@ -106,7 +106,7 @@ fn do_not_run_ignored_tests() { } #[test] -fn ignored_tests_result_in_ignored() { +pub fn ignored_tests_result_in_ignored() { fn f() -> Result<(), String> { Ok(()) } @@ -133,7 +133,9 @@ 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> { @@ -162,7 +164,9 @@ 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> { @@ -191,7 +195,9 @@ 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; @@ -225,7 +231,9 @@ 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; @@ -264,7 +272,9 @@ 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")]; @@ -469,7 +479,7 @@ fn parse_include_ignored_flag() { } #[test] -fn filter_for_ignored_option() { +pub 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 @@ -486,7 +496,7 @@ fn filter_for_ignored_option() { } #[test] -fn run_include_ignored_option() { +pub 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 @@ -503,7 +513,7 @@ fn run_include_ignored_option() { } #[test] -fn exclude_should_panic_option() { +pub fn exclude_should_panic_option() { let mut opts = TestOpts::new(); opts.run_tests = true; opts.exclude_should_panic = true; @@ -534,7 +544,7 @@ fn exclude_should_panic_option() { } #[test] -fn exact_filter_match() { +pub fn exact_filter_match() { fn tests() -> Vec { ["base", "base::test", "base::test1", "base::test2"] .into_iter() @@ -657,7 +667,7 @@ fn sample_tests() -> Vec { } #[test] -fn shuffle_tests() { +pub fn shuffle_tests() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -676,7 +686,7 @@ fn shuffle_tests() { } #[test] -fn shuffle_tests_with_seed() { +pub fn shuffle_tests_with_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -694,7 +704,7 @@ fn shuffle_tests_with_seed() { } #[test] -fn order_depends_on_more_than_seed() { +pub fn order_depends_on_more_than_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -722,7 +732,7 @@ fn order_depends_on_more_than_seed() { } #[test] -fn test_metricmap_compare() { +pub fn test_metricmap_compare() { let mut m1 = MetricMap::new(); let mut m2 = MetricMap::new(); m1.insert_metric("in-both-noise", 1000.0, 200.0); @@ -745,7 +755,7 @@ fn test_metricmap_compare() { } #[test] -fn test_bench_once_no_iter() { +pub fn test_bench_once_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -753,7 +763,7 @@ fn test_bench_once_no_iter() { } #[test] -fn test_bench_once_iter() { +pub fn test_bench_once_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) @@ -762,7 +772,7 @@ fn test_bench_once_iter() { } #[test] -fn test_bench_no_iter() { +pub fn test_bench_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -789,7 +799,7 @@ fn test_bench_no_iter() { } #[test] -fn test_bench_iter() { +pub fn test_bench_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) diff --git a/test/src/time.rs b/test/src/time.rs index f63b156b3dc5a..02ae050db55bd 100644 --- a/test/src/time.rs +++ b/test/src/time.rs @@ -11,7 +11,7 @@ use std::{env, fmt}; use super::types::{TestDesc, TestType}; -pub(crate) const TEST_WARN_TIMEOUT_S: u64 = 60; +pub 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(crate) 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(crate) mod time_constants { +pub mod time_constants { use std::time::Duration; use super::TEST_WARN_TIMEOUT_S; /// Environment variable for overriding default threshold for unit-tests. - pub(crate) const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; + pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; // Unit tests are supposed to be really quick. - pub(crate) const UNIT_WARN: Duration = Duration::from_millis(50); - pub(crate) const UNIT_CRITICAL: Duration = Duration::from_millis(100); + pub const UNIT_WARN: Duration = Duration::from_millis(50); + pub const UNIT_CRITICAL: Duration = Duration::from_millis(100); /// Environment variable for overriding default threshold for unit-tests. - pub(crate) const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; + pub 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(crate) const INTEGRATION_WARN: Duration = Duration::from_millis(500); - pub(crate) const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); + pub const INTEGRATION_WARN: Duration = Duration::from_millis(500); + pub const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); /// Environment variable for overriding default threshold for unit-tests. - pub(crate) const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; + pub 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(crate) const DOCTEST_WARN: Duration = INTEGRATION_WARN; - pub(crate) const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; + pub const DOCTEST_WARN: Duration = INTEGRATION_WARN; + pub const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; // Do not suppose anything about unknown tests, base limits on the // `TEST_WARN_TIMEOUT_S` constant. - 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); + 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); } /// Returns an `Instance` object denoting when the test should be considered /// timed out. -pub(crate) fn get_default_test_timeout() -> Instant { +pub 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(crate) struct TestSuiteExecTime(pub Duration); +pub struct TestSuiteExecTime(pub Duration); impl fmt::Display for TestSuiteExecTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/unwind/Cargo.toml b/unwind/Cargo.toml index 66e8d1a3ffe5f..569a1b3299e5f 100644 --- a/unwind/Cargo.toml +++ b/unwind/Cargo.toml @@ -22,7 +22,7 @@ cfg-if = "1.0" libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false } [target.'cfg(target_os = "xous")'.dependencies] -unwinding = { version = "0.2.5", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } +unwinding = { version = "0.2.3", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } [features] @@ -37,4 +37,7 @@ system-llvm-libunwind = [] [lints.rust.unexpected_cfgs] level = "warn" -check-cfg = ['cfg(emscripten_wasm_eh)'] +check-cfg = [ + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', +] diff --git a/unwind/src/lib.rs b/unwind/src/lib.rs index e4ba2bc1ed874..79baa5b0b83ec 100644 --- a/unwind/src/lib.rs +++ b/unwind/src/lib.rs @@ -4,11 +4,10 @@ #![feature(staged_api)] #![cfg_attr(not(target_env = "msvc"), feature(libc))] #![cfg_attr( - all(target_family = "wasm", any(not(target_os = "emscripten"), emscripten_wasm_eh)), + all(target_family = "wasm", not(target_os = "emscripten")), feature(simd_wasm64, wasm_exception_handling_intrinsics) )] #![allow(internal_features)] -#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))] // Force libc to be included even if unused. This is required by many platforms. #[cfg(not(all(windows, target_env = "msvc")))] @@ -21,6 +20,7 @@ cfg_if::cfg_if! { target_os = "l4re", target_os = "none", target_os = "espidf", + target_os = "rtems", target_os = "nuttx", ))] { // These "unix" family members do not have unwinder. @@ -178,8 +178,3 @@ cfg_if::cfg_if! { #[cfg(target_os = "hurd")] #[link(name = "gcc_s")] 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" {} diff --git a/unwind/src/libunwind.rs b/unwind/src/libunwind.rs index 1fa9e480166b7..715f8b57876ae 100644 --- a/unwind/src/libunwind.rs +++ b/unwind/src/libunwind.rs @@ -102,9 +102,12 @@ pub type _Unwind_Exception_Cleanup_Fn = // rustc_codegen_ssa::src::back::symbol_export, rustc_middle::middle::exported_symbols // and RFC 2841 #[cfg_attr( - all( - feature = "llvm-libunwind", - any(target_os = "fuchsia", target_os = "linux", target_os = "xous") + any( + all( + feature = "llvm-libunwind", + any(target_os = "fuchsia", target_os = "linux", target_os = "xous") + ), + all(target_os = "windows", target_env = "gnu", target_abi = "llvm") ), link(name = "unwind", kind = "static", modifiers = "-bundle") )]