diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ad872841b..08fa44abb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,6 +8,7 @@ on: - rust-1.70.0 - rust-1.71.0 - rust-1.71.1 + - rust-1.72.0 pull_request: branches: - 1.67.1 @@ -15,6 +16,7 @@ on: - rust-1.70.0 - rust-1.71.0 - rust-1.71.1 + - rust-1.72.0 jobs: test_x86_84: @@ -116,7 +118,7 @@ jobs: export RUSTUP_HOME="$HOME/.rustup" curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y source "$HOME/.cargo/env" - rustup toolchain install 1.71.1 + rustup toolchain install 1.72.0 cargo --version - name: Print env diff --git a/Cargo.lock b/Cargo.lock index 2eb2b6dbf..43282ab36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.18.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ca9b76e919fd83ccfb509f51b28c333c0e03f2221616e347a129215cec4e4a9" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "compiler_builtins", "gimli", @@ -34,6 +34,12 @@ dependencies = [ "rand_xorshift", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "cc" version = "1.0.79" @@ -52,9 +58,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.92" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64518f1ae689f74db058bbfb3238dfe6eb53f59f4ae712f1ff4348628522e190" +checksum = "01a6d58e9c3408138099a396a98fd0d0e6cfb25d723594d2ae48b5004513fd5b" dependencies = [ "cc", "rustc-std-workspace-core", @@ -102,9 +108,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -113,10 +119,11 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" dependencies = [ + "allocator-api2", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -135,9 +142,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" dependencies = [ "rustc-std-workspace-core", ] @@ -154,9 +161,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", "compiler_builtins", @@ -166,9 +173,9 @@ dependencies = [ [[package]] name = "object" -version = "0.30.4" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" dependencies = [ "compiler_builtins", "memchr", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 95c07abf7..e5f828c4c 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -35,3 +35,4 @@ compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] compiler-builtins-no-asm = ["compiler_builtins/no-asm"] compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] +compiler-builtins-weak-intrinsics = ["compiler_builtins/weak-intrinsics"] diff --git a/library/alloc/benches/btree/map.rs b/library/alloc/benches/btree/map.rs index ec1b0a8eb..7d2366477 100644 --- a/library/alloc/benches/btree/map.rs +++ b/library/alloc/benches/btree/map.rs @@ -385,7 +385,7 @@ pub fn clone_slim_100_and_clear(b: &mut Bencher) { #[bench] pub fn clone_slim_100_and_drain_all(b: &mut Bencher) { let src = slim_map(100); - b.iter(|| src.clone().drain_filter(|_, _| true).count()) + b.iter(|| src.clone().extract_if(|_, _| true).count()) } #[bench] @@ -393,7 +393,7 @@ pub fn clone_slim_100_and_drain_half(b: &mut Bencher) { let src = slim_map(100); b.iter(|| { let mut map = src.clone(); - assert_eq!(map.drain_filter(|i, _| i % 2 == 0).count(), 100 / 2); + assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 100 / 2); assert_eq!(map.len(), 100 / 2); }) } @@ -456,7 +456,7 @@ pub fn clone_slim_10k_and_clear(b: &mut Bencher) { #[bench] pub fn clone_slim_10k_and_drain_all(b: &mut Bencher) { let src = slim_map(10_000); - b.iter(|| src.clone().drain_filter(|_, _| true).count()) + b.iter(|| src.clone().extract_if(|_, _| true).count()) } #[bench] @@ -464,7 +464,7 @@ pub fn clone_slim_10k_and_drain_half(b: &mut Bencher) { let src = slim_map(10_000); b.iter(|| { let mut map = src.clone(); - assert_eq!(map.drain_filter(|i, _| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 10_000 / 2); assert_eq!(map.len(), 10_000 / 2); }) } @@ -527,7 +527,7 @@ pub fn clone_fat_val_100_and_clear(b: &mut Bencher) { #[bench] pub fn clone_fat_val_100_and_drain_all(b: &mut Bencher) { let src = fat_val_map(100); - b.iter(|| src.clone().drain_filter(|_, _| true).count()) + b.iter(|| src.clone().extract_if(|_, _| true).count()) } #[bench] @@ -535,7 +535,7 @@ pub fn clone_fat_val_100_and_drain_half(b: &mut Bencher) { let src = fat_val_map(100); b.iter(|| { let mut map = src.clone(); - assert_eq!(map.drain_filter(|i, _| i % 2 == 0).count(), 100 / 2); + assert_eq!(map.extract_if(|i, _| i % 2 == 0).count(), 100 / 2); assert_eq!(map.len(), 100 / 2); }) } diff --git a/library/alloc/benches/btree/set.rs b/library/alloc/benches/btree/set.rs index 3f4b0e0f1..09d72c720 100644 --- a/library/alloc/benches/btree/set.rs +++ b/library/alloc/benches/btree/set.rs @@ -69,7 +69,7 @@ pub fn clone_100_and_clear(b: &mut Bencher) { #[bench] pub fn clone_100_and_drain_all(b: &mut Bencher) { let src = slim_set(100); - b.iter(|| src.clone().drain_filter(|_| true).count()) + b.iter(|| src.clone().extract_if(|_| true).count()) } #[bench] @@ -77,7 +77,7 @@ pub fn clone_100_and_drain_half(b: &mut Bencher) { let src = slim_set(100); b.iter(|| { let mut set = src.clone(); - assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 100 / 2); + assert_eq!(set.extract_if(|i| i % 2 == 0).count(), 100 / 2); assert_eq!(set.len(), 100 / 2); }) } @@ -140,7 +140,7 @@ pub fn clone_10k_and_clear(b: &mut Bencher) { #[bench] pub fn clone_10k_and_drain_all(b: &mut Bencher) { let src = slim_set(10_000); - b.iter(|| src.clone().drain_filter(|_| true).count()) + b.iter(|| src.clone().extract_if(|_| true).count()) } #[bench] @@ -148,7 +148,7 @@ pub fn clone_10k_and_drain_half(b: &mut Bencher) { let src = slim_set(10_000); b.iter(|| { let mut set = src.clone(); - assert_eq!(set.drain_filter(|i| i % 2 == 0).count(), 10_000 / 2); + assert_eq!(set.extract_if(|i| i % 2 == 0).count(), 10_000 / 2); assert_eq!(set.len(), 10_000 / 2); }) } diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs index b25d63d83..638f343fb 100644 --- a/library/alloc/benches/lib.rs +++ b/library/alloc/benches/lib.rs @@ -1,7 +1,7 @@ // Disabling on android for the time being // See https://github.com/rust-lang/rust/issues/73535#event-3477699747 #![cfg(not(target_os = "android"))] -#![feature(btree_drain_filter)] +#![feature(btree_extract_if)] #![feature(iter_next_chunk)] #![feature(repr_simd)] #![feature(slice_partition_dedup)] diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 01d1fdc9b..e24a0fe51 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -4,8 +4,10 @@ #[cfg(not(test))] use core::intrinsics; +#[cfg(all(bootstrap, not(test)))] use core::intrinsics::{min_align_of_val, size_of_val}; +#[cfg(all(bootstrap, not(test)))] use core::ptr::Unique; #[cfg(not(test))] use core::ptr::{self, NonNull}; @@ -38,7 +40,6 @@ extern "Rust" { #[rustc_nounwind] fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; - #[cfg(not(bootstrap))] static __rust_no_alloc_shim_is_unstable: u8; } @@ -96,7 +97,6 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 { unsafe { // Make sure we don't accidentally allow omitting the allocator shim in // stable code until it is actually stabilized. - #[cfg(not(bootstrap))] core::ptr::read_volatile(&__rust_no_alloc_shim_is_unstable); __rust_alloc(layout.size(), layout.align()) @@ -337,14 +337,15 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { } } -#[cfg_attr(not(test), lang = "box_free")] +#[cfg(all(bootstrap, not(test)))] +#[lang = "box_free"] #[inline] // This signature has to be the same as `Box`, otherwise an ICE will happen. // When an additional parameter to `Box` is added (like `A: Allocator`), this has to be added here as // well. // For example if `Box` is changed to `struct Box(Unique, A)`, // this function has to be changed to `fn box_free(Unique, A)` as well. -pub(crate) unsafe fn box_free(ptr: Unique, alloc: A) { +unsafe fn box_free(ptr: Unique, alloc: A) { unsafe { let size = size_of_val(ptr.as_ref()); let align = min_align_of_val(ptr.as_ref()); diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 1768687e8..8ef2bac92 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1211,8 +1211,16 @@ impl Box { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box { + #[inline] fn drop(&mut self) { - // FIXME: Do nothing, drop is currently performed by compiler. + // the T in the Box is dropped by the compiler before the destructor is run + + let ptr = self.0; + + unsafe { + let layout = Layout::for_value_raw(ptr.as_ptr()); + self.1.deallocate(From::from(ptr.cast()), layout) + } } } diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 2c089bb31..66573b90d 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -143,6 +143,7 @@ #![allow(missing_docs)] #![stable(feature = "rust1", since = "1.0.0")] +use core::alloc::Allocator; use core::fmt; use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedLen}; use core::mem::{self, swap, ManuallyDrop}; @@ -150,6 +151,7 @@ use core::num::NonZeroUsize; use core::ops::{Deref, DerefMut}; use core::ptr; +use crate::alloc::Global; use crate::collections::TryReserveError; use crate::slice; use crate::vec::{self, AsVecIntoIter, Vec}; @@ -271,8 +273,11 @@ mod tests; /// [peek\_mut]: BinaryHeap::peek_mut #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "BinaryHeap")] -pub struct BinaryHeap { - data: Vec, +pub struct BinaryHeap< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + data: Vec, } /// Structure wrapping a mutable reference to the greatest item on a @@ -283,22 +288,26 @@ pub struct BinaryHeap { /// /// [`peek_mut`]: BinaryHeap::peek_mut #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -pub struct PeekMut<'a, T: 'a + Ord> { - heap: &'a mut BinaryHeap, +pub struct PeekMut< + 'a, + T: 'a + Ord, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + heap: &'a mut BinaryHeap, // If a set_len + sift_down are required, this is Some. If a &mut T has not // yet been exposed to peek_mut()'s caller, it's None. original_len: Option, } #[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for PeekMut<'_, T> { +impl fmt::Debug for PeekMut<'_, T, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("PeekMut").field(&self.heap.data[0]).finish() } } #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl Drop for PeekMut<'_, T> { +impl Drop for PeekMut<'_, T, A> { fn drop(&mut self) { if let Some(original_len) = self.original_len { // SAFETY: That's how many elements were in the Vec at the time of @@ -315,7 +324,7 @@ impl Drop for PeekMut<'_, T> { } #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl Deref for PeekMut<'_, T> { +impl Deref for PeekMut<'_, T, A> { type Target = T; fn deref(&self) -> &T { debug_assert!(!self.heap.is_empty()); @@ -325,7 +334,7 @@ impl Deref for PeekMut<'_, T> { } #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl DerefMut for PeekMut<'_, T> { +impl DerefMut for PeekMut<'_, T, A> { fn deref_mut(&mut self) -> &mut T { debug_assert!(!self.heap.is_empty()); @@ -353,10 +362,10 @@ impl DerefMut for PeekMut<'_, T> { } } -impl<'a, T: Ord> PeekMut<'a, T> { +impl<'a, T: Ord, A: Allocator> PeekMut<'a, T, A> { /// Removes the peeked value from the heap and returns it. #[stable(feature = "binary_heap_peek_mut_pop", since = "1.18.0")] - pub fn pop(mut this: PeekMut<'a, T>) -> T { + pub fn pop(mut this: PeekMut<'a, T, A>) -> T { if let Some(original_len) = this.original_len.take() { // SAFETY: This is how many elements were in the Vec at the time of // the BinaryHeap::peek_mut call. @@ -371,7 +380,7 @@ impl<'a, T: Ord> PeekMut<'a, T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for BinaryHeap { +impl Clone for BinaryHeap { fn clone(&self) -> Self { BinaryHeap { data: self.data.clone() } } @@ -391,18 +400,22 @@ impl Default for BinaryHeap { } #[stable(feature = "binaryheap_debug", since = "1.4.0")] -impl fmt::Debug for BinaryHeap { +impl fmt::Debug for BinaryHeap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter()).finish() } } -struct RebuildOnDrop<'a, T: Ord> { - heap: &'a mut BinaryHeap, +struct RebuildOnDrop< + 'a, + T: Ord, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + heap: &'a mut BinaryHeap, rebuild_from: usize, } -impl<'a, T: Ord> Drop for RebuildOnDrop<'a, T> { +impl Drop for RebuildOnDrop<'_, T, A> { fn drop(&mut self) { self.heap.rebuild_tail(self.rebuild_from); } @@ -446,6 +459,52 @@ impl BinaryHeap { pub fn with_capacity(capacity: usize) -> BinaryHeap { BinaryHeap { data: Vec::with_capacity(capacity) } } +} + +impl BinaryHeap { + /// Creates an empty `BinaryHeap` as a max-heap, using `A` as allocator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new_in(System); + /// heap.push(4); + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + #[must_use] + pub fn new_in(alloc: A) -> BinaryHeap { + BinaryHeap { data: Vec::new_in(alloc) } + } + + /// Creates an empty `BinaryHeap` with at least the specified capacity, using `A` as allocator. + /// + /// 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 0, the binary heap will not allocate. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::with_capacity_in(10, System); + /// heap.push(4); + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + #[must_use] + pub fn with_capacity_in(capacity: usize, alloc: A) -> BinaryHeap { + BinaryHeap { data: Vec::with_capacity_in(capacity, alloc) } + } /// Returns a mutable reference to the greatest item in the binary heap, or /// `None` if it is empty. @@ -478,7 +537,7 @@ impl BinaryHeap { /// If the item is modified then the worst case time complexity is *O*(log(*n*)), /// otherwise it's *O*(1). #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] - pub fn peek_mut(&mut self) -> Option> { + pub fn peek_mut(&mut self) -> Option> { if self.is_empty() { None } else { Some(PeekMut { heap: self, original_len: None }) } } @@ -573,7 +632,7 @@ impl BinaryHeap { /// ``` #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] - pub fn into_sorted_vec(mut self) -> Vec { + pub fn into_sorted_vec(mut self) -> Vec { let mut end = self.len(); while end > 1 { end -= 1; @@ -831,7 +890,7 @@ impl BinaryHeap { /// ``` #[inline] #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] - pub fn drain_sorted(&mut self) -> DrainSorted<'_, T> { + pub fn drain_sorted(&mut self) -> DrainSorted<'_, T, A> { DrainSorted { inner: self } } @@ -874,7 +933,7 @@ impl BinaryHeap { } } -impl BinaryHeap { +impl BinaryHeap { /// Returns an iterator visiting all values in the underlying vector, in /// arbitrary order. /// @@ -911,7 +970,7 @@ impl BinaryHeap { /// assert_eq!(heap.into_iter_sorted().take(2).collect::>(), [5, 4]); /// ``` #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] - pub fn into_iter_sorted(self) -> IntoIterSorted { + pub fn into_iter_sorted(self) -> IntoIterSorted { IntoIterSorted { inner: self } } @@ -1178,10 +1237,17 @@ impl BinaryHeap { /// ``` #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] - pub fn into_vec(self) -> Vec { + pub fn into_vec(self) -> Vec { self.into() } + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn allocator(&self) -> &A { + self.data.allocator() + } + /// Returns the length of the binary heap. /// /// # Examples @@ -1249,7 +1315,7 @@ impl BinaryHeap { /// ``` #[inline] #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, T> { + pub fn drain(&mut self) -> Drain<'_, T, A> { Drain { iter: self.data.drain(..) } } @@ -1419,19 +1485,30 @@ impl FusedIterator for Iter<'_, T> {} /// [`into_iter`]: BinaryHeap::into_iter #[stable(feature = "rust1", since = "1.0.0")] #[derive(Clone)] -pub struct IntoIter { - iter: vec::IntoIter, +pub struct IntoIter< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + iter: vec::IntoIter, +} + +impl IntoIter { + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn allocator(&self) -> &A { + self.iter.allocator() + } } #[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for IntoIter { +impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("IntoIter").field(&self.iter.as_slice()).finish() } } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { +impl Iterator for IntoIter { type Item = T; #[inline] @@ -1446,7 +1523,7 @@ impl Iterator for IntoIter { } #[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { +impl DoubleEndedIterator for IntoIter { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back() @@ -1454,14 +1531,14 @@ impl DoubleEndedIterator for IntoIter { } #[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { +impl ExactSizeIterator for IntoIter { fn is_empty(&self) -> bool { self.iter.is_empty() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} +impl FusedIterator for IntoIter {} #[stable(feature = "default_iters", since = "1.70.0")] impl Default for IntoIter { @@ -1481,8 +1558,8 @@ impl Default for IntoIter { // also refer to the vec::in_place_collect module documentation to get an overview #[unstable(issue = "none", feature = "inplace_iteration")] #[doc(hidden)] -unsafe impl SourceIter for IntoIter { - type Source = IntoIter; +unsafe impl SourceIter for IntoIter { + type Source = IntoIter; #[inline] unsafe fn as_inner(&mut self) -> &mut Self::Source { @@ -1492,7 +1569,7 @@ unsafe impl SourceIter for IntoIter { #[unstable(issue = "none", feature = "inplace_iteration")] #[doc(hidden)] -unsafe impl InPlaceIterable for IntoIter {} +unsafe impl InPlaceIterable for IntoIter {} unsafe impl AsVecIntoIter for IntoIter { type Item = I; @@ -1505,12 +1582,23 @@ unsafe impl AsVecIntoIter for IntoIter { #[must_use = "iterators are lazy and do nothing unless consumed"] #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] #[derive(Clone, Debug)] -pub struct IntoIterSorted { - inner: BinaryHeap, +pub struct IntoIterSorted< + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + inner: BinaryHeap, +} + +impl IntoIterSorted { + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn allocator(&self) -> &A { + self.inner.allocator() + } } #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] -impl Iterator for IntoIterSorted { +impl Iterator for IntoIterSorted { type Item = T; #[inline] @@ -1526,13 +1614,13 @@ impl Iterator for IntoIterSorted { } #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] -impl ExactSizeIterator for IntoIterSorted {} +impl ExactSizeIterator for IntoIterSorted {} #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")] -impl FusedIterator for IntoIterSorted {} +impl FusedIterator for IntoIterSorted {} #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IntoIterSorted {} +unsafe impl TrustedLen for IntoIterSorted {} /// A draining iterator over the elements of a `BinaryHeap`. /// @@ -1542,12 +1630,24 @@ unsafe impl TrustedLen for IntoIterSorted {} /// [`drain`]: BinaryHeap::drain #[stable(feature = "drain", since = "1.6.0")] #[derive(Debug)] -pub struct Drain<'a, T: 'a> { - iter: vec::Drain<'a, T>, +pub struct Drain< + 'a, + T: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + iter: vec::Drain<'a, T, A>, +} + +impl Drain<'_, T, A> { + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn allocator(&self) -> &A { + self.iter.allocator() + } } #[stable(feature = "drain", since = "1.6.0")] -impl Iterator for Drain<'_, T> { +impl Iterator for Drain<'_, T, A> { type Item = T; #[inline] @@ -1562,7 +1662,7 @@ impl Iterator for Drain<'_, T> { } #[stable(feature = "drain", since = "1.6.0")] -impl DoubleEndedIterator for Drain<'_, T> { +impl DoubleEndedIterator for Drain<'_, T, A> { #[inline] fn next_back(&mut self) -> Option { self.iter.next_back() @@ -1570,14 +1670,14 @@ impl DoubleEndedIterator for Drain<'_, T> { } #[stable(feature = "drain", since = "1.6.0")] -impl ExactSizeIterator for Drain<'_, T> { +impl ExactSizeIterator for Drain<'_, T, A> { fn is_empty(&self) -> bool { self.iter.is_empty() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, T> {} +impl FusedIterator for Drain<'_, T, A> {} /// A draining iterator over the elements of a `BinaryHeap`. /// @@ -1587,17 +1687,29 @@ impl FusedIterator for Drain<'_, T> {} /// [`drain_sorted`]: BinaryHeap::drain_sorted #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] #[derive(Debug)] -pub struct DrainSorted<'a, T: Ord> { - inner: &'a mut BinaryHeap, +pub struct DrainSorted< + 'a, + T: Ord, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + inner: &'a mut BinaryHeap, +} + +impl<'a, T: Ord, A: Allocator> DrainSorted<'a, T, A> { + /// Returns a reference to the underlying allocator. + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn allocator(&self) -> &A { + self.inner.allocator() + } } #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] -impl<'a, T: Ord> Drop for DrainSorted<'a, T> { +impl<'a, T: Ord, A: Allocator> Drop for DrainSorted<'a, T, A> { /// Removes heap elements in heap order. fn drop(&mut self) { - struct DropGuard<'r, 'a, T: Ord>(&'r mut DrainSorted<'a, T>); + struct DropGuard<'r, 'a, T: Ord, A: Allocator>(&'r mut DrainSorted<'a, T, A>); - impl<'r, 'a, T: Ord> Drop for DropGuard<'r, 'a, T> { + impl<'r, 'a, T: Ord, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { fn drop(&mut self) { while self.0.inner.pop().is_some() {} } @@ -1612,7 +1724,7 @@ impl<'a, T: Ord> Drop for DrainSorted<'a, T> { } #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] -impl Iterator for DrainSorted<'_, T> { +impl Iterator for DrainSorted<'_, T, A> { type Item = T; #[inline] @@ -1628,20 +1740,20 @@ impl Iterator for DrainSorted<'_, T> { } #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] -impl ExactSizeIterator for DrainSorted<'_, T> {} +impl ExactSizeIterator for DrainSorted<'_, T, A> {} #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")] -impl FusedIterator for DrainSorted<'_, T> {} +impl FusedIterator for DrainSorted<'_, T, A> {} #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for DrainSorted<'_, T> {} +unsafe impl TrustedLen for DrainSorted<'_, T, A> {} #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] -impl From> for BinaryHeap { +impl From> for BinaryHeap { /// Converts a `Vec` into a `BinaryHeap`. /// /// This conversion happens in-place, and has *O*(*n*) time complexity. - fn from(vec: Vec) -> BinaryHeap { + fn from(vec: Vec) -> BinaryHeap { let mut heap = BinaryHeap { data: vec }; heap.rebuild(); heap @@ -1665,12 +1777,12 @@ impl From<[T; N]> for BinaryHeap { } #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] -impl From> for Vec { +impl From> for Vec { /// Converts a `BinaryHeap` into a `Vec`. /// /// This conversion requires no data movement or allocation, and has /// constant time complexity. - fn from(heap: BinaryHeap) -> Vec { + fn from(heap: BinaryHeap) -> Vec { heap.data } } @@ -1683,9 +1795,9 @@ impl FromIterator for BinaryHeap { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for BinaryHeap { +impl IntoIterator for BinaryHeap { type Item = T; - type IntoIter = IntoIter; + type IntoIter = IntoIter; /// Creates a consuming iterator, that is, one that moves each value out of /// the binary heap in arbitrary order. The binary heap cannot be used @@ -1705,13 +1817,13 @@ impl IntoIterator for BinaryHeap { /// println!("{x}"); /// } /// ``` - fn into_iter(self) -> IntoIter { + fn into_iter(self) -> IntoIter { IntoIter { iter: self.data.into_iter() } } } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a BinaryHeap { +impl<'a, T, A: Allocator> IntoIterator for &'a BinaryHeap { type Item = &'a T; type IntoIter = Iter<'a, T>; @@ -1721,7 +1833,7 @@ impl<'a, T> IntoIterator for &'a BinaryHeap { } #[stable(feature = "rust1", since = "1.0.0")] -impl Extend for BinaryHeap { +impl Extend for BinaryHeap { #[inline] fn extend>(&mut self, iter: I) { let guard = RebuildOnDrop { rebuild_from: self.len(), heap: self }; @@ -1740,7 +1852,7 @@ impl Extend for BinaryHeap { } #[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap { +impl<'a, T: 'a + Ord + Copy, A: Allocator> Extend<&'a T> for BinaryHeap { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } diff --git a/library/alloc/src/collections/binary_heap/tests.rs b/library/alloc/src/collections/binary_heap/tests.rs index 500caa356..565a7b797 100644 --- a/library/alloc/src/collections/binary_heap/tests.rs +++ b/library/alloc/src/collections/binary_heap/tests.rs @@ -309,6 +309,7 @@ fn test_drain_sorted() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drain_sorted_leak() { let d0 = CrashTestDummy::new(0); let d1 = CrashTestDummy::new(1); @@ -475,6 +476,7 @@ fn test_retain() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_retain_catch_unwind() { let mut heap = BinaryHeap::from(vec![3, 1, 2]); @@ -502,6 +504,7 @@ fn test_retain_catch_unwind() { // 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 rand::seq::SliceRandom; use std::cmp; diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 1f8a1ecba..ff908ec12 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1132,7 +1132,7 @@ impl BTreeMap { K: Ord, F: FnMut(&K, &mut V) -> bool, { - self.drain_filter(|k, v| !f(k, v)); + self.extract_if(|k, v| !f(k, v)).for_each(drop); } /// Moves all elements from `other` into `self`, leaving `other` empty. @@ -1395,40 +1395,37 @@ impl BTreeMap { /// The iterator also lets you mutate the value of each element in the /// closure, regardless of whether you choose to keep or remove it. /// - /// If the iterator is only partially consumed or not consumed at all, each - /// of the remaining elements is still subjected to the closure, which may - /// change its value and, by returning `true`, have the element removed and - /// dropped. + /// 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. /// - /// It is unspecified how many more elements will be subjected to the - /// closure if a panic occurs in the closure, or a panic occurs while - /// dropping an element, or if the `DrainFilter` value is leaked. + /// [`retain`]: BTreeMap::retain /// /// # Examples /// /// Splitting a map into even and odd keys, reusing the original map: /// /// ``` - /// #![feature(btree_drain_filter)] + /// #![feature(btree_extract_if)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap = (0..8).map(|x| (x, x)).collect(); - /// let evens: BTreeMap<_, _> = map.drain_filter(|k, _v| k % 2 == 0).collect(); + /// let evens: BTreeMap<_, _> = map.extract_if(|k, _v| k % 2 == 0).collect(); /// let odds = map; /// assert_eq!(evens.keys().copied().collect::>(), [0, 2, 4, 6]); /// assert_eq!(odds.keys().copied().collect::>(), [1, 3, 5, 7]); /// ``` - #[unstable(feature = "btree_drain_filter", issue = "70530")] - pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F, A> + #[unstable(feature = "btree_extract_if", issue = "70530")] + pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F, A> where K: Ord, F: FnMut(&K, &mut V) -> bool, { - let (inner, alloc) = self.drain_filter_inner(); - DrainFilter { pred, inner, alloc } + let (inner, alloc) = self.extract_if_inner(); + ExtractIf { pred, inner, alloc } } - pub(super) fn drain_filter_inner(&mut self) -> (DrainFilterInner<'_, K, V>, A) + pub(super) fn extract_if_inner(&mut self) -> (ExtractIfInner<'_, K, V>, A) where K: Ord, { @@ -1436,7 +1433,7 @@ impl BTreeMap { let (root, dormant_root) = DormantMutRef::new(root); let front = root.borrow_mut().first_leaf_edge(); ( - DrainFilterInner { + ExtractIfInner { length: &mut self.length, dormant_root: Some(dormant_root), cur_leaf_edge: Some(front), @@ -1445,7 +1442,7 @@ impl BTreeMap { ) } else { ( - DrainFilterInner { + ExtractIfInner { length: &mut self.length, dormant_root: None, cur_leaf_edge: None, @@ -1899,9 +1896,10 @@ impl Default for Values<'_, K, V> { } } -/// An iterator produced by calling `drain_filter` on BTreeMap. -#[unstable(feature = "btree_drain_filter", issue = "70530")] -pub struct DrainFilter< +/// An iterator produced by calling `extract_if` on BTreeMap. +#[unstable(feature = "btree_extract_if", issue = "70530")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf< 'a, K, V, @@ -1911,13 +1909,13 @@ pub struct DrainFilter< F: 'a + FnMut(&K, &mut V) -> bool, { pred: F, - inner: DrainFilterInner<'a, K, V>, + inner: ExtractIfInner<'a, K, V>, /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`. alloc: A, } -/// Most of the implementation of DrainFilter are generic over the type -/// of the predicate, thus also serving for BTreeSet::DrainFilter. -pub(super) struct DrainFilterInner<'a, K, V> { +/// Most of the implementation of ExtractIf are generic over the type +/// of the predicate, thus also serving for BTreeSet::ExtractIf. +pub(super) struct ExtractIfInner<'a, K, V> { /// Reference to the length field in the borrowed map, updated live. length: &'a mut usize, /// Buried reference to the root field in the borrowed map. @@ -1929,30 +1927,20 @@ pub(super) struct DrainFilterInner<'a, K, V> { cur_leaf_edge: Option, K, V, marker::Leaf>, marker::Edge>>, } -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl Drop for DrainFilter<'_, K, V, F, A> -where - F: FnMut(&K, &mut V) -> bool, -{ - fn drop(&mut self) { - self.for_each(drop); - } -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl fmt::Debug for DrainFilter<'_, K, V, F> +#[unstable(feature = "btree_extract_if", issue = "70530")] +impl fmt::Debug for ExtractIf<'_, K, V, F> where K: fmt::Debug, V: fmt::Debug, F: FnMut(&K, &mut V) -> bool, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("DrainFilter").field(&self.inner.peek()).finish() + f.debug_tuple("ExtractIf").field(&self.inner.peek()).finish() } } -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl Iterator for DrainFilter<'_, K, V, F, A> +#[unstable(feature = "btree_extract_if", issue = "70530")] +impl Iterator for ExtractIf<'_, K, V, F, A> where F: FnMut(&K, &mut V) -> bool, { @@ -1967,14 +1955,14 @@ where } } -impl<'a, K, V> DrainFilterInner<'a, K, V> { +impl<'a, K, V> ExtractIfInner<'a, K, V> { /// Allow Debug implementations to predict the next element. pub(super) fn peek(&self) -> Option<(&K, &V)> { let edge = self.cur_leaf_edge.as_ref()?; edge.reborrow().next_kv().ok().map(Handle::into_kv) } - /// Implementation of a typical `DrainFilter::next` method, given the predicate. + /// Implementation of a typical `ExtractIf::next` method, given the predicate. pub(super) fn next(&mut self, pred: &mut F, alloc: A) -> Option<(K, V)> where F: FnMut(&K, &mut V) -> bool, @@ -2001,7 +1989,7 @@ impl<'a, K, V> DrainFilterInner<'a, K, V> { None } - /// Implementation of a typical `DrainFilter::size_hint` method. + /// Implementation of a typical `ExtractIf::size_hint` method. pub(super) fn size_hint(&self) -> (usize, Option) { // In most of the btree iterators, `self.length` is the number of elements // yet to be visited. Here, it includes elements that were visited and that @@ -2011,8 +1999,8 @@ impl<'a, K, V> DrainFilterInner<'a, K, V> { } } -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} +#[unstable(feature = "btree_extract_if", issue = "70530")] +impl FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} #[stable(feature = "btree_range", since = "1.17.0")] impl<'a, K, V> Iterator for Range<'a, K, V> { diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 7ecffe3ee..8681cfcd6 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -941,13 +941,13 @@ fn test_retain() { assert_eq!(map[&6], 60); } -mod test_drain_filter { +mod test_extract_if { use super::*; #[test] fn empty() { let mut map: BTreeMap = BTreeMap::new(); - map.drain_filter(|_, _| unreachable!("there's nothing to decide on")); + map.extract_if(|_, _| unreachable!("there's nothing to decide on")).for_each(drop); assert_eq!(map.height(), None); map.check(); } @@ -957,7 +957,7 @@ mod test_drain_filter { fn consumed_keeping_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - assert!(map.drain_filter(|_, _| false).eq(iter::empty())); + assert!(map.extract_if(|_, _| false).eq(iter::empty())); map.check(); } @@ -966,7 +966,7 @@ mod test_drain_filter { fn consumed_removing_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs.clone()); - assert!(map.drain_filter(|_, _| true).eq(pairs)); + assert!(map.extract_if(|_, _| true).eq(pairs)); assert!(map.is_empty()); map.check(); } @@ -977,7 +977,7 @@ mod test_drain_filter { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); assert!( - map.drain_filter(|_, v| { + map.extract_if(|_, v| { *v += 6; false }) @@ -994,7 +994,7 @@ mod test_drain_filter { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); assert!( - map.drain_filter(|_, v| { + map.extract_if(|_, v| { *v += 6; true }) @@ -1008,7 +1008,7 @@ mod test_drain_filter { fn underfull_keeping_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.drain_filter(|_, _| false); + map.extract_if(|_, _| false).for_each(drop); assert!(map.keys().copied().eq(0..3)); map.check(); } @@ -1018,7 +1018,7 @@ mod test_drain_filter { let pairs = (0..3).map(|i| (i, i)); for doomed in 0..3 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.drain_filter(|i, _| *i == doomed); + map.extract_if(|i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), 2); map.check(); } @@ -1029,7 +1029,7 @@ mod test_drain_filter { let pairs = (0..3).map(|i| (i, i)); for sacred in 0..3 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.drain_filter(|i, _| *i != sacred); + map.extract_if(|i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1039,7 +1039,7 @@ mod test_drain_filter { fn underfull_removing_all() { let pairs = (0..3).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.drain_filter(|_, _| true); + map.extract_if(|_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1048,7 +1048,7 @@ mod test_drain_filter { fn height_0_keeping_all() { let pairs = (0..node::CAPACITY).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.drain_filter(|_, _| false); + map.extract_if(|_, _| false).for_each(drop); assert!(map.keys().copied().eq(0..node::CAPACITY)); map.check(); } @@ -1058,7 +1058,7 @@ mod test_drain_filter { let pairs = (0..node::CAPACITY).map(|i| (i, i)); for doomed in 0..node::CAPACITY { let mut map = BTreeMap::from_iter(pairs.clone()); - map.drain_filter(|i, _| *i == doomed); + map.extract_if(|i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), node::CAPACITY - 1); map.check(); } @@ -1069,7 +1069,7 @@ mod test_drain_filter { let pairs = (0..node::CAPACITY).map(|i| (i, i)); for sacred in 0..node::CAPACITY { let mut map = BTreeMap::from_iter(pairs.clone()); - map.drain_filter(|i, _| *i != sacred); + map.extract_if(|i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1079,7 +1079,7 @@ mod test_drain_filter { fn height_0_removing_all() { let pairs = (0..node::CAPACITY).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.drain_filter(|_, _| true); + map.extract_if(|_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1087,7 +1087,7 @@ mod test_drain_filter { #[test] fn height_0_keeping_half() { let mut map = BTreeMap::from_iter((0..16).map(|i| (i, i))); - assert_eq!(map.drain_filter(|i, _| *i % 2 == 0).count(), 8); + assert_eq!(map.extract_if(|i, _| *i % 2 == 0).count(), 8); assert_eq!(map.len(), 8); map.check(); } @@ -1096,7 +1096,7 @@ mod test_drain_filter { fn height_1_removing_all() { let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.drain_filter(|_, _| true); + map.extract_if(|_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } @@ -1106,7 +1106,7 @@ mod test_drain_filter { let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); for doomed in 0..MIN_INSERTS_HEIGHT_1 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.drain_filter(|i, _| *i == doomed); + map.extract_if(|i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), MIN_INSERTS_HEIGHT_1 - 1); map.check(); } @@ -1117,7 +1117,7 @@ mod test_drain_filter { let pairs = (0..MIN_INSERTS_HEIGHT_1).map(|i| (i, i)); for sacred in 0..MIN_INSERTS_HEIGHT_1 { let mut map = BTreeMap::from_iter(pairs.clone()); - map.drain_filter(|i, _| *i != sacred); + map.extract_if(|i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1128,7 +1128,7 @@ mod test_drain_filter { let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); for doomed in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { let mut map = BTreeMap::from_iter(pairs.clone()); - map.drain_filter(|i, _| *i == doomed); + map.extract_if(|i, _| *i == doomed).for_each(drop); assert_eq!(map.len(), MIN_INSERTS_HEIGHT_2 - 1); map.check(); } @@ -1139,7 +1139,7 @@ mod test_drain_filter { let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); for sacred in (0..MIN_INSERTS_HEIGHT_2).step_by(12) { let mut map = BTreeMap::from_iter(pairs.clone()); - map.drain_filter(|i, _| *i != sacred); + map.extract_if(|i, _| *i != sacred).for_each(drop); assert!(map.keys().copied().eq(sacred..=sacred)); map.check(); } @@ -1149,12 +1149,13 @@ mod test_drain_filter { fn height_2_removing_all() { let pairs = (0..MIN_INSERTS_HEIGHT_2).map(|i| (i, i)); let mut map = BTreeMap::from_iter(pairs); - map.drain_filter(|_, _| true); + map.extract_if(|_, _| true).for_each(drop); assert!(map.is_empty()); map.check(); } #[test] + #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn drop_panic_leak() { let a = CrashTestDummy::new(0); let b = CrashTestDummy::new(1); @@ -1164,7 +1165,8 @@ mod test_drain_filter { map.insert(b.spawn(Panic::InDrop), ()); map.insert(c.spawn(Panic::Never), ()); - catch_unwind(move || drop(map.drain_filter(|dummy, _| dummy.query(true)))).unwrap_err(); + catch_unwind(move || map.extract_if(|dummy, _| dummy.query(true)).for_each(drop)) + .unwrap_err(); assert_eq!(a.queried(), 1); assert_eq!(b.queried(), 1); @@ -1175,6 +1177,7 @@ mod test_drain_filter { } #[test] + #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn pred_panic_leak() { let a = CrashTestDummy::new(0); let b = CrashTestDummy::new(1); @@ -1184,8 +1187,10 @@ mod test_drain_filter { map.insert(b.spawn(Panic::InQuery), ()); map.insert(c.spawn(Panic::InQuery), ()); - catch_unwind(AssertUnwindSafe(|| drop(map.drain_filter(|dummy, _| dummy.query(true))))) - .unwrap_err(); + catch_unwind(AssertUnwindSafe(|| { + map.extract_if(|dummy, _| dummy.query(true)).for_each(drop) + })) + .unwrap_err(); assert_eq!(a.queried(), 1); assert_eq!(b.queried(), 1); @@ -1201,6 +1206,7 @@ mod test_drain_filter { // Same as above, but attempt to use the iterator again after the panic in the predicate #[test] + #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn pred_panic_reuse() { let a = CrashTestDummy::new(0); let b = CrashTestDummy::new(1); @@ -1211,7 +1217,7 @@ mod test_drain_filter { map.insert(c.spawn(Panic::InQuery), ()); { - let mut it = map.drain_filter(|dummy, _| dummy.query(true)); + let mut it = map.extract_if(|dummy, _| dummy.query(true)); catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); // Iterator behaviour after a panic is explicitly unspecified, // so this is just the current implementation: @@ -1449,6 +1455,7 @@ fn test_clear() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_clear_drop_panic_leak() { let a = CrashTestDummy::new(0); let b = CrashTestDummy::new(1); @@ -1540,11 +1547,13 @@ fn test_clone_panic_leak(size: usize) { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_clone_panic_leak_height_0() { test_clone_panic_leak(3) } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_clone_panic_leak_height_1() { test_clone_panic_leak(MIN_INSERTS_HEIGHT_1) } @@ -1651,8 +1660,8 @@ fn assert_sync() { v.into_values() } - fn drain_filter(v: &mut BTreeMap) -> impl Sync + '_ { - v.drain_filter(|_, _| false) + fn extract_if(v: &mut BTreeMap) -> impl Sync + '_ { + v.extract_if(|_, _| false) } fn iter(v: &BTreeMap) -> impl Sync + '_ { @@ -1720,8 +1729,8 @@ fn assert_send() { v.into_values() } - fn drain_filter(v: &mut BTreeMap) -> impl Send + '_ { - v.drain_filter(|_, _| false) + fn extract_if(v: &mut BTreeMap) -> impl Send + '_ { + v.extract_if(|_, _| false) } fn iter(v: &BTreeMap) -> impl Send + '_ { @@ -2099,6 +2108,7 @@ create_append_test!(test_append_239, 239); create_append_test!(test_append_1700, 1700); #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_append_drop_leak() { let a = CrashTestDummy::new(0); let b = CrashTestDummy::new(1); @@ -2240,6 +2250,7 @@ fn test_split_off_large_random_sorted() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_into_iter_drop_leak_height_0() { let a = CrashTestDummy::new(0); let b = CrashTestDummy::new(1); @@ -2263,6 +2274,7 @@ fn test_into_iter_drop_leak_height_0() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_into_iter_drop_leak_height_1() { let size = MIN_INSERTS_HEIGHT_1; for panic_point in vec![0, 1, size - 2, size - 1] { diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 940fa30af..c4461040b 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -999,7 +999,7 @@ impl BTreeSet { T: Ord, F: FnMut(&T) -> bool, { - self.drain_filter(|v| !f(v)); + self.extract_if(|v| !f(v)).for_each(drop); } /// Moves all elements from `other` into `self`, leaving `other` empty. @@ -1084,36 +1084,33 @@ impl BTreeSet { /// yielded. If the closure returns `false`, or panics, the element remains /// in the set and will not be yielded. /// - /// If the iterator is only partially consumed or not consumed at all, each - /// of the remaining elements is still subjected to the closure and removed - /// and dropped if it returns `true`. - /// - /// It is unspecified how many more elements will be subjected to the - /// closure if a panic occurs in the closure, or if a panic occurs while - /// dropping an element, or if the `DrainFilter` itself is leaked. + /// 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. /// + /// [`retain`]: BTreeSet::retain /// # Examples /// /// Splitting a set into even and odd values, reusing the original set: /// /// ``` - /// #![feature(btree_drain_filter)] + /// #![feature(btree_extract_if)] /// use std::collections::BTreeSet; /// /// let mut set: BTreeSet = (0..8).collect(); - /// let evens: BTreeSet<_> = set.drain_filter(|v| v % 2 == 0).collect(); + /// let evens: BTreeSet<_> = set.extract_if(|v| v % 2 == 0).collect(); /// let odds = set; /// assert_eq!(evens.into_iter().collect::>(), vec![0, 2, 4, 6]); /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 7]); /// ``` - #[unstable(feature = "btree_drain_filter", issue = "70530")] - pub fn drain_filter<'a, F>(&'a mut self, pred: F) -> DrainFilter<'a, T, F, A> + #[unstable(feature = "btree_extract_if", issue = "70530")] + pub fn extract_if<'a, F>(&'a mut self, pred: F) -> ExtractIf<'a, T, F, A> where T: Ord, F: 'a + FnMut(&T) -> bool, { - let (inner, alloc) = self.map.drain_filter_inner(); - DrainFilter { pred, inner, alloc } + let (inner, alloc) = self.map.extract_if_inner(); + ExtractIf { pred, inner, alloc } } /// Gets an iterator that visits the elements in the `BTreeSet` in ascending @@ -1275,9 +1272,10 @@ impl<'a, T, A: Allocator + Clone> IntoIterator for &'a BTreeSet { } } -/// An iterator produced by calling `drain_filter` on BTreeSet. -#[unstable(feature = "btree_drain_filter", issue = "70530")] -pub struct DrainFilter< +/// An iterator produced by calling `extract_if` on BTreeSet. +#[unstable(feature = "btree_extract_if", issue = "70530")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf< 'a, T, F, @@ -1287,34 +1285,24 @@ pub struct DrainFilter< F: 'a + FnMut(&T) -> bool, { pred: F, - inner: super::map::DrainFilterInner<'a, T, SetValZST>, + inner: super::map::ExtractIfInner<'a, T, SetValZST>, /// The BTreeMap will outlive this IntoIter so we don't care about drop order for `alloc`. alloc: A, } -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl Drop for DrainFilter<'_, T, F, A> -where - F: FnMut(&T) -> bool, -{ - fn drop(&mut self) { - self.for_each(drop); - } -} - -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl fmt::Debug for DrainFilter<'_, T, F, A> +#[unstable(feature = "btree_extract_if", issue = "70530")] +impl fmt::Debug for ExtractIf<'_, T, F, A> where T: fmt::Debug, F: FnMut(&T) -> bool, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("DrainFilter").field(&self.inner.peek().map(|(k, _)| k)).finish() + f.debug_tuple("ExtractIf").field(&self.inner.peek().map(|(k, _)| k)).finish() } } -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl<'a, T, F, A: Allocator + Clone> Iterator for DrainFilter<'_, T, F, A> +#[unstable(feature = "btree_extract_if", issue = "70530")] +impl<'a, T, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, F, A> where F: 'a + FnMut(&T) -> bool, { @@ -1331,11 +1319,8 @@ where } } -#[unstable(feature = "btree_drain_filter", issue = "70530")] -impl FusedIterator for DrainFilter<'_, T, F, A> where - F: FnMut(&T) -> bool -{ -} +#[unstable(feature = "btree_extract_if", issue = "70530")] +impl FusedIterator for ExtractIf<'_, T, F, A> where F: FnMut(&T) -> bool {} #[stable(feature = "rust1", since = "1.0.0")] impl Extend for BTreeSet { diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index a7c839d77..e05bf0e20 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -366,18 +366,19 @@ fn test_retain() { } #[test] -fn test_drain_filter() { +fn test_extract_if() { let mut x = BTreeSet::from([1]); let mut y = BTreeSet::from([1]); - x.drain_filter(|_| true); - y.drain_filter(|_| false); + x.extract_if(|_| true).for_each(drop); + y.extract_if(|_| false).for_each(drop); assert_eq!(x.len(), 0); assert_eq!(y.len(), 1); } #[test] -fn test_drain_filter_drop_panic_leak() { +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_extract_if_drop_panic_leak() { let a = CrashTestDummy::new(0); let b = CrashTestDummy::new(1); let c = CrashTestDummy::new(2); @@ -386,7 +387,7 @@ fn test_drain_filter_drop_panic_leak() { set.insert(b.spawn(Panic::InDrop)); set.insert(c.spawn(Panic::Never)); - catch_unwind(move || drop(set.drain_filter(|dummy| dummy.query(true)))).ok(); + catch_unwind(move || set.extract_if(|dummy| dummy.query(true)).for_each(drop)).ok(); assert_eq!(a.queried(), 1); assert_eq!(b.queried(), 1); @@ -397,7 +398,8 @@ fn test_drain_filter_drop_panic_leak() { } #[test] -fn test_drain_filter_pred_panic_leak() { +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_extract_if_pred_panic_leak() { let a = CrashTestDummy::new(0); let b = CrashTestDummy::new(1); let c = CrashTestDummy::new(2); @@ -406,7 +408,8 @@ fn test_drain_filter_pred_panic_leak() { set.insert(b.spawn(Panic::InQuery)); set.insert(c.spawn(Panic::InQuery)); - catch_unwind(AssertUnwindSafe(|| drop(set.drain_filter(|dummy| dummy.query(true))))).ok(); + catch_unwind(AssertUnwindSafe(|| set.extract_if(|dummy| dummy.query(true)).for_each(drop))) + .ok(); assert_eq!(a.queried(), 1); assert_eq!(b.queried(), 1); @@ -603,8 +606,8 @@ fn assert_sync() { v.range(..) } - fn drain_filter(v: &mut BTreeSet) -> impl Sync + '_ { - v.drain_filter(|_| false) + fn extract_if(v: &mut BTreeSet) -> impl Sync + '_ { + v.extract_if(|_| false) } fn difference(v: &BTreeSet) -> impl Sync + '_ { @@ -642,8 +645,8 @@ fn assert_send() { v.range(..) } - fn drain_filter(v: &mut BTreeSet) -> impl Send + '_ { - v.drain_filter(|_| false) + fn extract_if(v: &mut BTreeSet) -> impl Send + '_ { + v.extract_if(|_| false) } fn difference(v: &BTreeSet) -> impl Send + '_ { diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 4cd34ac2f..052edf453 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1030,7 +1030,11 @@ impl LinkedList { /// If the closure returns false, the element will remain in the list and will not be yielded /// by the iterator. /// - /// Note that `drain_filter` lets you mutate every element in the filter closure, regardless of + /// 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 `extract_if().for_each(drop)` if you do not need the returned iterator. + /// + /// Note that `extract_if` lets you mutate every element in the filter closure, regardless of /// whether you choose to keep or remove it. /// /// # Examples @@ -1038,20 +1042,20 @@ impl LinkedList { /// Splitting a list into evens and odds, reusing the original list: /// /// ``` - /// #![feature(drain_filter)] + /// #![feature(extract_if)] /// use std::collections::LinkedList; /// /// let mut numbers: LinkedList = LinkedList::new(); /// numbers.extend(&[1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]); /// - /// let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::>(); + /// let evens = numbers.extract_if(|x| *x % 2 == 0).collect::>(); /// let odds = numbers; /// /// assert_eq!(evens.into_iter().collect::>(), vec![2, 4, 6, 8, 14]); /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 9, 11, 13, 15]); /// ``` - #[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] - pub fn drain_filter(&mut self, filter: F) -> DrainFilter<'_, T, F, A> + #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] + pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, { @@ -1059,7 +1063,7 @@ impl LinkedList { let it = self.head; let old_len = self.len; - DrainFilter { list: self, it, pred: filter, idx: 0, old_len } + ExtractIf { list: self, it, pred: filter, idx: 0, old_len } } } @@ -1803,9 +1807,10 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { } } -/// An iterator produced by calling `drain_filter` on LinkedList. -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -pub struct DrainFilter< +/// An iterator produced by calling `extract_if` on LinkedList. +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf< 'a, T: 'a, F: 'a, @@ -1820,8 +1825,8 @@ pub struct DrainFilter< old_len: usize, } -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Iterator for DrainFilter<'_, T, F, A> +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl Iterator for ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, { @@ -1849,40 +1854,13 @@ where } } -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Drop for DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - fn drop(&mut self) { - struct DropGuard<'r, 'a, T, F, A: Allocator>(&'r mut DrainFilter<'a, T, F, A>) - where - F: FnMut(&mut T) -> bool; - - impl<'r, 'a, T, F, A: Allocator> Drop for DropGuard<'r, 'a, T, F, A> - where - F: FnMut(&mut T) -> bool, - { - fn drop(&mut self) { - self.0.for_each(drop); - } - } - - while let Some(item) = self.next() { - let guard = DropGuard(self); - drop(item); - mem::forget(guard); - } - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl fmt::Debug for DrainFilter<'_, T, F> +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +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("DrainFilter").field(&self.list).finish() + f.debug_tuple("ExtractIf").field(&self.list).finish() } } diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index 04594d55b..8dcd59d12 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -540,10 +540,10 @@ fn test_show() { } #[test] -fn drain_filter_test() { +fn extract_if_test() { let mut m: LinkedList = LinkedList::new(); m.extend(&[1, 2, 3, 4, 5, 6]); - let deleted = m.drain_filter(|v| *v < 4).collect::>(); + let deleted = m.extract_if(|v| *v < 4).collect::>(); check_links(&m); @@ -555,7 +555,7 @@ fn drain_filter_test() { fn drain_to_empty_test() { let mut m: LinkedList = LinkedList::new(); m.extend(&[1, 2, 3, 4, 5, 6]); - let deleted = m.drain_filter(|_| true).collect::>(); + let deleted = m.extract_if(|_| true).collect::>(); check_links(&m); @@ -811,11 +811,11 @@ fn test_contains() { } #[test] -fn drain_filter_empty() { +fn extract_if_empty() { let mut list: LinkedList = LinkedList::new(); { - let mut iter = list.drain_filter(|_| true); + let mut iter = list.extract_if(|_| true); assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.next(), None); assert_eq!(iter.size_hint(), (0, Some(0))); @@ -828,13 +828,13 @@ fn drain_filter_empty() { } #[test] -fn drain_filter_zst() { +fn extract_if_zst() { let mut list: LinkedList<_> = [(), (), (), (), ()].into_iter().collect(); let initial_len = list.len(); let mut count = 0; { - let mut iter = list.drain_filter(|_| true); + let mut iter = list.extract_if(|_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -851,14 +851,14 @@ fn drain_filter_zst() { } #[test] -fn drain_filter_false() { +fn extract_if_false() { let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); let initial_len = list.len(); let mut count = 0; { - let mut iter = list.drain_filter(|_| false); + let mut iter = list.extract_if(|_| false); assert_eq!(iter.size_hint(), (0, Some(initial_len))); for _ in iter.by_ref() { count += 1; @@ -874,14 +874,14 @@ fn drain_filter_false() { } #[test] -fn drain_filter_true() { +fn extract_if_true() { let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); let initial_len = list.len(); let mut count = 0; { - let mut iter = list.drain_filter(|_| true); + let mut iter = list.extract_if(|_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -898,7 +898,7 @@ fn drain_filter_true() { } #[test] -fn drain_filter_complex() { +fn extract_if_complex() { { // [+xxx++++++xxxxx++++x+x++] let mut list = [ @@ -908,7 +908,7 @@ fn drain_filter_complex() { .into_iter() .collect::>(); - let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + let removed = list.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]); @@ -926,7 +926,7 @@ fn drain_filter_complex() { .into_iter() .collect::>(); - let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + let removed = list.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]); @@ -944,7 +944,7 @@ fn drain_filter_complex() { .into_iter() .collect::>(); - let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + let removed = list.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]); @@ -961,7 +961,7 @@ fn drain_filter_complex() { .into_iter() .collect::>(); - let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + let removed = list.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]); @@ -975,7 +975,7 @@ fn drain_filter_complex() { .into_iter() .collect::>(); - let removed = list.drain_filter(|x| *x % 2 == 0).collect::>(); + let removed = list.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]); @@ -985,7 +985,8 @@ fn drain_filter_complex() { } #[test] -fn drain_filter_drop_panic_leak() { +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn extract_if_drop_panic_leak() { let d0 = CrashTestDummy::new(0); let d1 = CrashTestDummy::new(1); let d2 = CrashTestDummy::new(2); @@ -1004,21 +1005,28 @@ fn drain_filter_drop_panic_leak() { q.push_front(d1.spawn(Panic::InDrop)); q.push_front(d0.spawn(Panic::Never)); - catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).unwrap_err(); + catch_unwind(AssertUnwindSafe(|| q.extract_if(|_| true).for_each(drop))).unwrap_err(); assert_eq!(d0.dropped(), 1); assert_eq!(d1.dropped(), 1); + assert_eq!(d2.dropped(), 0); + assert_eq!(d3.dropped(), 0); + assert_eq!(d4.dropped(), 0); + assert_eq!(d5.dropped(), 0); + assert_eq!(d6.dropped(), 0); + assert_eq!(d7.dropped(), 0); + drop(q); assert_eq!(d2.dropped(), 1); assert_eq!(d3.dropped(), 1); assert_eq!(d4.dropped(), 1); assert_eq!(d5.dropped(), 1); assert_eq!(d6.dropped(), 1); assert_eq!(d7.dropped(), 1); - assert!(q.is_empty()); } #[test] -fn drain_filter_pred_panic_leak() { +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn extract_if_pred_panic_leak() { static mut DROPS: i32 = 0; #[derive(Debug)] @@ -1043,7 +1051,7 @@ fn drain_filter_pred_panic_leak() { q.push_front(D(0)); catch_unwind(AssertUnwindSafe(|| { - drop(q.drain_filter(|item| if item.0 >= 2 { panic!() } else { true })) + q.extract_if(|item| if item.0 >= 2 { panic!() } else { true }).for_each(drop) })) .ok(); @@ -1124,6 +1132,7 @@ fn test_drop_clear() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drop_panic() { static mut DROPS: i32 = 0; diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index f99395c72..62856fc9a 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -888,7 +888,7 @@ impl From<&CStr> for Arc { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Rc { /// Converts a [`CString`] into an [Rc]<[CStr]> by moving the [`CString`] - /// data into a new [`Arc`] buffer. + /// data into a new [`Rc`] buffer. #[inline] fn from(s: CString) -> Rc { let rc: Rc<[u8]> = Rc::from(s.into_inner()); diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 59fa91c10..967ad3a0e 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -56,6 +56,11 @@ //! [`Rc`]: rc //! [`RefCell`]: core::cell +// To run alloc tests without x.py without ending up with two copies of alloc, Miri needs to be +// able to "empty" this crate. See . +// rustc itself never sets the feature, so this line has no affect there. +#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] +// #![allow(unused_attributes)] #![stable(feature = "alloc", since = "1.36.0")] #![doc( @@ -75,11 +80,6 @@ ))] #![no_std] #![needs_allocator] -// To run alloc tests without x.py without ending up with two copies of alloc, Miri needs to be -// able to "empty" this crate. See . -// rustc itself never sets the feature, so this line has no affect there. -#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] -// // Lints: #![deny(unsafe_op_in_unsafe_fn)] #![deny(fuzzy_provenance_casts)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 38a711ac7..b3305b8ca 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -258,19 +258,19 @@ use core::iter; use core::marker::{PhantomData, Unsize}; #[cfg(not(no_global_oom_handling))] use core::mem::size_of_val; -use core::mem::{self, align_of_val_raw, forget}; -use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver}; +use core::mem::{self, align_of_val_raw, forget, ManuallyDrop}; +use core::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] use core::pin::Pin; -use core::ptr::{self, NonNull}; +use core::ptr::{self, drop_in_place, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; #[cfg(not(no_global_oom_handling))] -use crate::alloc::{box_free, WriteCloneIntoRaw}; +use crate::alloc::WriteCloneIntoRaw; use crate::alloc::{AllocError, Allocator, Global, Layout}; use crate::borrow::{Cow, ToOwned}; #[cfg(not(no_global_oom_handling))] @@ -1169,7 +1169,7 @@ impl Rc { #[inline] #[stable(feature = "ptr_eq", since = "1.17.0")] /// Returns `true` if the two `Rc`s point to the same allocation in a vein similar to - /// [`ptr::eq`]. See [that function][`ptr::eq`] for caveats when comparing `dyn Trait` pointers. + /// [`ptr::eq`]. This function ignores the metadata of `dyn Trait` pointers. /// /// # Examples /// @@ -1184,7 +1184,7 @@ impl Rc { /// assert!(!Rc::ptr_eq(&five, &other_five)); /// ``` pub fn ptr_eq(this: &Self, other: &Self) -> bool { - this.ptr.as_ptr() == other.ptr.as_ptr() + this.ptr.as_ptr() as *const () == other.ptr.as_ptr() as *const () } } @@ -1442,23 +1442,21 @@ impl Rc { } #[cfg(not(no_global_oom_handling))] - fn from_box(v: Box) -> Rc { + fn from_box(src: Box) -> Rc { unsafe { - let (box_unique, alloc) = Box::into_unique(v); - let bptr = box_unique.as_ptr(); - - let value_size = size_of_val(&*bptr); - let ptr = Self::allocate_for_ptr(bptr); + let value_size = size_of_val(&*src); + let ptr = Self::allocate_for_ptr(&*src); // Copy value as bytes ptr::copy_nonoverlapping( - bptr as *const T as *const u8, + &*src as *const T as *const u8, &mut (*ptr).value as *mut _ as *mut u8, value_size, ); // Free the allocation without dropping its contents - box_free(box_unique, alloc); + let src = Box::from_raw(Box::into_raw(src) as *mut mem::ManuallyDrop); + drop(src); Self::from_ptr(ptr) } @@ -2468,8 +2466,8 @@ impl Weak { } /// Returns `true` if the two `Weak`s point to the same allocation similar to [`ptr::eq`], or if - /// both don't point to any allocation (because they were created with `Weak::new()`). See [that - /// function][`ptr::eq`] for caveats when comparing `dyn Trait` pointers. + /// both don't point to any allocation (because they were created with `Weak::new()`). However, + /// this function ignores the metadata of `dyn Trait` pointers. /// /// # Notes /// @@ -2510,7 +2508,7 @@ impl Weak { #[must_use] #[stable(feature = "weak_ptr_eq", since = "1.39.0")] pub fn ptr_eq(&self, other: &Self) -> bool { - self.ptr.as_ptr() == other.ptr.as_ptr() + ptr::eq(self.ptr.as_ptr() as *const (), other.ptr.as_ptr() as *const ()) } } @@ -2746,3 +2744,139 @@ fn data_offset_align(align: usize) -> usize { let layout = Layout::new::>(); layout.size() + layout.padding_needed_for(align) } + +/// A uniquely owned `Rc` +/// +/// This represents an `Rc` that is known to be uniquely owned -- that is, have exactly one strong +/// reference. Multiple weak pointers can be created, but attempts to upgrade those to strong +/// references will fail unless the `UniqueRc` they point to has been converted into a regular `Rc`. +/// +/// Because they are uniquely owned, the contents of a `UniqueRc` can be freely mutated. A common +/// use case is to have an object be mutable during its initialization phase but then have it become +/// immutable and converted to a normal `Rc`. +/// +/// This can be used as a flexible way to create cyclic data structures, as in the example below. +/// +/// ``` +/// #![feature(unique_rc_arc)] +/// use std::rc::{Rc, Weak, UniqueRc}; +/// +/// struct Gadget { +/// #[allow(dead_code)] +/// me: Weak, +/// } +/// +/// fn create_gadget() -> Option> { +/// let mut rc = UniqueRc::new(Gadget { +/// me: Weak::new(), +/// }); +/// rc.me = UniqueRc::downgrade(&rc); +/// Some(UniqueRc::into_rc(rc)) +/// } +/// +/// create_gadget().unwrap(); +/// ``` +/// +/// An advantage of using `UniqueRc` over [`Rc::new_cyclic`] to build cyclic data structures is that +/// [`Rc::new_cyclic`]'s `data_fn` parameter cannot be async or return a [`Result`]. As shown in the +/// 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 { + ptr: NonNull>, + phantom: PhantomData>, +} + +impl UniqueRc { + /// Creates a new `UniqueRc` + /// + /// Weak references to this `UniqueRc` can be created with [`UniqueRc::downgrade`]. Upgrading + /// these weak references will fail before the `UniqueRc` has been converted into an [`Rc`]. + /// After converting the `UniqueRc` into an [`Rc`], any weak references created beforehand will + /// point to the new [`Rc`]. + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn new(value: T) -> Self { + Self { + ptr: Box::leak(Box::new(RcBox { + strong: Cell::new(0), + // keep one weak reference so if all the weak pointers that are created are dropped + // the UniqueRc still stays valid. + weak: Cell::new(1), + value, + })) + .into(), + phantom: PhantomData, + } + } + + /// Creates a new weak reference to the `UniqueRc` + /// + /// Attempting to upgrade this weak reference will fail before the `UniqueRc` has been converted + /// to a [`Rc`] using [`UniqueRc::into_rc`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn downgrade(this: &Self) -> Weak { + // SAFETY: This pointer was allocated at creation time and we guarantee that we only have + // one strong reference before converting to a regular Rc. + unsafe { + this.ptr.as_ref().inc_weak(); + } + Weak { ptr: this.ptr } + } + + /// Converts the `UniqueRc` into a regular [`Rc`] + /// + /// This consumes the `UniqueRc` and returns a regular [`Rc`] that contains the `value` that + /// is passed to `into_rc`. + /// + /// Any weak references created before this method is called can now be upgraded to strong + /// references. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn into_rc(this: Self) -> Rc { + let mut this = ManuallyDrop::new(this); + // SAFETY: This pointer was allocated at creation time so we know it is valid. + unsafe { + // Convert our weak reference into a strong reference + this.ptr.as_mut().strong.set(1); + Rc::from_inner(this.ptr) + } + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl Deref for UniqueRc { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: This pointer was allocated at creation time so we know it is valid. + unsafe { &self.ptr.as_ref().value } + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl DerefMut for UniqueRc { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: This pointer was allocated at creation time so we know it is valid. We know we + // have unique ownership and therefore it's safe to make a mutable reference because + // `UniqueRc` owns the only strong reference to itself. + unsafe { &mut (*self.ptr.as_ptr()).value } + } +} + +#[unstable(feature = "unique_rc_arc", issue = "112566")] +unsafe impl<#[may_dangle] T> Drop for UniqueRc { + fn drop(&mut self) { + unsafe { + // destroy the contained object + drop_in_place(DerefMut::deref_mut(self)); + + // remove the implicit "strong weak" pointer now that we've destroyed the contents. + self.ptr.as_ref().dec_weak(); + + if self.ptr.as_ref().weak() == 0 { + Global.deallocate(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); + } + } + } +} diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index 2784108e0..1f221b86f 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -574,3 +574,48 @@ fn test_rc_cyclic_with_two_ref() { assert_eq!(Rc::strong_count(&two_refs), 3); assert_eq!(Rc::weak_count(&two_refs), 2); } + +#[test] +fn test_unique_rc_weak() { + let rc = UniqueRc::new(42); + let weak = UniqueRc::downgrade(&rc); + assert!(weak.upgrade().is_none()); + + let _rc = UniqueRc::into_rc(rc); + assert_eq!(*weak.upgrade().unwrap(), 42); +} + +#[test] +fn test_unique_rc_drop_weak() { + let rc = UniqueRc::new(42); + let weak = UniqueRc::downgrade(&rc); + mem::drop(weak); + + let rc = UniqueRc::into_rc(rc); + assert_eq!(*rc, 42); +} + +#[test] +fn test_unique_rc_drops_contents() { + let mut dropped = false; + struct DropMe<'a>(&'a mut bool); + impl Drop for DropMe<'_> { + fn drop(&mut self) { + *self.0 = true; + } + } + { + let rc = UniqueRc::new(DropMe(&mut dropped)); + drop(rc); + } + assert!(dropped); +} + +#[test] +fn test_unique_rc_weak_clone_holding_ref() { + let mut v = UniqueRc::new(0u8); + let w = UniqueRc::downgrade(&v); + let r = &mut *v; + let _ = w.clone(); // touch weak count + *r = 123; +} diff --git a/library/alloc/src/slice/tests.rs b/library/alloc/src/slice/tests.rs index f674530aa..54bc4e77b 100644 --- a/library/alloc/src/slice/tests.rs +++ b/library/alloc/src/slice/tests.rs @@ -187,6 +187,7 @@ std::thread_local!(static SILENCE_PANIC: Cell = Cell::new(false)); #[test] #[cfg_attr(target_os = "emscripten", ignore)] // no threads +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_safe() { panic::update_hook(move |prev, info| { if !SILENCE_PANIC.with(|s| s.get()) { diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 59e3f887b..ad7b77f54 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1853,26 +1853,27 @@ impl String { /// Consumes and leaks the `String`, returning a mutable reference to the contents, /// `&'a mut str`. /// - /// This is mainly useful for data that lives for the remainder of - /// the program's life. Dropping the returned reference will cause a memory - /// leak. + /// The caller has free choice over the returned lifetime, including `'static`. Indeed, + /// this function is ideally used for data that lives for the remainder of the program's life, + /// as dropping the returned reference will cause a memory leak. /// /// It does not reallocate or shrink the `String`, /// so the leaked allocation may include unused capacity that is not part - /// of the returned slice. + /// of the returned slice. If you don't want that, call [`into_boxed_str`], + /// and then [`Box::leak`]. + /// + /// [`into_boxed_str`]: Self::into_boxed_str /// /// # Examples /// /// Simple usage: /// /// ``` - /// #![feature(string_leak)] - /// /// let x = String::from("bucket"); /// let static_ref: &'static mut str = x.leak(); /// assert_eq!(static_ref, "bucket"); /// ``` - #[unstable(feature = "string_leak", issue = "102929")] + #[stable(feature = "string_leak", since = "1.72.0")] #[inline] pub fn leak<'a>(self) -> &'a mut str { let slice = self.vec.leak(); diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index bfdb7a92b..5bb1a93ae 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -33,7 +33,7 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; #[cfg(not(no_global_oom_handling))] -use crate::alloc::{box_free, WriteCloneIntoRaw}; +use crate::alloc::WriteCloneIntoRaw; use crate::alloc::{AllocError, Allocator, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; @@ -1267,7 +1267,7 @@ impl Arc { } /// Returns `true` if the two `Arc`s point to the same allocation in a vein similar to - /// [`ptr::eq`]. See [that function][`ptr::eq`] for caveats when comparing `dyn Trait` pointers. + /// [`ptr::eq`]. This function ignores the metadata of `dyn Trait` pointers. /// /// # Examples /// @@ -1287,7 +1287,7 @@ impl Arc { #[must_use] #[stable(feature = "ptr_eq", since = "1.17.0")] pub fn ptr_eq(this: &Self, other: &Self) -> bool { - this.ptr.as_ptr() == other.ptr.as_ptr() + this.ptr.as_ptr() as *const () == other.ptr.as_ptr() as *const () } } @@ -1360,23 +1360,21 @@ impl Arc { } #[cfg(not(no_global_oom_handling))] - fn from_box(v: Box) -> Arc { + fn from_box(src: Box) -> Arc { unsafe { - let (box_unique, alloc) = Box::into_unique(v); - let bptr = box_unique.as_ptr(); - - let value_size = size_of_val(&*bptr); - let ptr = Self::allocate_for_ptr(bptr); + let value_size = size_of_val(&*src); + let ptr = Self::allocate_for_ptr(&*src); // Copy value as bytes ptr::copy_nonoverlapping( - bptr as *const T as *const u8, + &*src as *const T as *const u8, &mut (*ptr).data as *mut _ as *mut u8, value_size, ); // Free the allocation without dropping its contents - box_free(box_unique, alloc); + let src = Box::from_raw(Box::into_raw(src) as *mut mem::ManuallyDrop); + drop(src); Self::from_ptr(ptr) } @@ -2256,8 +2254,8 @@ impl Weak { } /// Returns `true` if the two `Weak`s point to the same allocation similar to [`ptr::eq`], or if - /// both don't point to any allocation (because they were created with `Weak::new()`). See [that - /// function][`ptr::eq`] for caveats when comparing `dyn Trait` pointers. + /// both don't point to any allocation (because they were created with `Weak::new()`). However, + /// this function ignores the metadata of `dyn Trait` pointers. /// /// # Notes /// @@ -2300,7 +2298,7 @@ impl Weak { #[must_use] #[stable(feature = "weak_ptr_eq", since = "1.39.0")] pub fn ptr_eq(&self, other: &Self) -> bool { - self.ptr.as_ptr() == other.ptr.as_ptr() + ptr::eq(self.ptr.as_ptr() as *const (), other.ptr.as_ptr() as *const ()) } } diff --git a/library/alloc/src/vec/drain_filter.rs b/library/alloc/src/vec/drain_filter.rs deleted file mode 100644 index 21b090234..000000000 --- a/library/alloc/src/vec/drain_filter.rs +++ /dev/null @@ -1,197 +0,0 @@ -use crate::alloc::{Allocator, Global}; -use core::mem::{ManuallyDrop, SizedTypeProperties}; -use core::ptr; -use core::slice; - -use super::Vec; - -/// An iterator which uses a closure to determine if an element should be removed. -/// -/// This struct is created by [`Vec::drain_filter`]. -/// See its documentation for more. -/// -/// # Example -/// -/// ``` -/// #![feature(drain_filter)] -/// -/// let mut v = vec![0, 1, 2]; -/// let iter: std::vec::DrainFilter<'_, _, _> = v.drain_filter(|x| *x % 2 == 0); -/// ``` -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -#[derive(Debug)] -pub struct DrainFilter< - 'a, - T, - F, - #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, -> 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`. - pub(super) idx: usize, - /// The number of items that have been drained (removed) thus far. - pub(super) del: usize, - /// The original length of `vec` prior to draining. - pub(super) old_len: usize, - /// The filter test predicate. - pub(super) pred: F, - /// A flag that indicates a panic has occurred in the filter test predicate. - /// This is used as a hint in the drop implementation to prevent consumption - /// of the remainder of the `DrainFilter`. Any unprocessed items will be - /// backshifted in the `vec`, but no further items will be dropped or - /// tested by the filter predicate. - pub(super) panic_flag: bool, -} - -impl DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - /// Returns a reference to the underlying allocator. - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub fn allocator(&self) -> &A { - self.vec.allocator() - } - - /// Keep unyielded elements in the source `Vec`. - /// - /// # Examples - /// - /// ``` - /// #![feature(drain_filter)] - /// #![feature(drain_keep_rest)] - /// - /// let mut vec = vec!['a', 'b', 'c']; - /// let mut drain = vec.drain_filter(|_| true); - /// - /// assert_eq!(drain.next().unwrap(), 'a'); - /// - /// // This call keeps 'b' and 'c' in the vec. - /// drain.keep_rest(); - /// - /// // If we wouldn't call `keep_rest()`, - /// // `vec` would be empty. - /// assert_eq!(vec, ['b', 'c']); - /// ``` - #[unstable(feature = "drain_keep_rest", issue = "101122")] - pub fn keep_rest(self) { - // At this moment layout looks like this: - // - // _____________________/-- old_len - // / \ - // [kept] [yielded] [tail] - // \_______/ ^-- idx - // \-- del - // - // Normally `Drop` impl would drop [tail] (via .for_each(drop), ie still calling `pred`) - // - // 1. Move [tail] after [kept] - // 2. Update length of the original vec to `old_len - del` - // a. In case of ZST, this is the only thing we want to do - // 3. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do - let mut this = ManuallyDrop::new(self); - - unsafe { - // ZSTs have no identity, so we don't need to move them around. - if !T::IS_ZST && this.idx < this.old_len && this.del > 0 { - let ptr = this.vec.as_mut_ptr(); - let src = ptr.add(this.idx); - let dst = src.sub(this.del); - let tail_len = this.old_len - this.idx; - src.copy_to(dst, tail_len); - } - - let new_len = this.old_len - this.del; - this.vec.set_len(new_len); - } - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Iterator for DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - type Item = T; - - fn next(&mut self) -> Option { - unsafe { - 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); - self.panic_flag = true; - let drained = (self.pred)(&mut v[i]); - self.panic_flag = false; - // Update the index *after* the predicate is called. If the index - // is updated prior and the predicate panics, the element at this - // index would be leaked. - self.idx += 1; - if drained { - self.del += 1; - return Some(ptr::read(&v[i])); - } else if self.del > 0 { - let del = self.del; - let src: *const T = &v[i]; - let dst: *mut T = &mut v[i - del]; - ptr::copy_nonoverlapping(src, dst, 1); - } - } - None - } - } - - fn size_hint(&self) -> (usize, Option) { - (0, Some(self.old_len - self.idx)) - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl Drop for DrainFilter<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ - fn drop(&mut self) { - struct BackshiftOnDrop<'a, 'b, T, F, A: Allocator> - where - F: FnMut(&mut T) -> bool, - { - drain: &'b mut DrainFilter<'a, T, F, A>, - } - - impl<'a, 'b, T, F, A: Allocator> Drop for BackshiftOnDrop<'a, 'b, T, F, A> - where - F: FnMut(&mut T) -> bool, - { - fn drop(&mut self) { - unsafe { - if self.drain.idx < self.drain.old_len && self.drain.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.drain.vec.as_mut_ptr(); - let src = ptr.add(self.drain.idx); - let dst = src.sub(self.drain.del); - let tail_len = self.drain.old_len - self.drain.idx; - src.copy_to(dst, tail_len); - } - self.drain.vec.set_len(self.drain.old_len - self.drain.del); - } - } - } - - let backshift = BackshiftOnDrop { drain: self }; - - // Attempt to consume any remaining elements if the filter predicate - // has not yet panicked. We'll backshift any remaining elements - // whether we've already panicked or if the consumption here panics. - if !backshift.drain.panic_flag { - backshift.drain.for_each(drop); - } - } -} diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs new file mode 100644 index 000000000..118cfdb36 --- /dev/null +++ b/library/alloc/src/vec/extract_if.rs @@ -0,0 +1,113 @@ +use crate::alloc::{Allocator, Global}; +use core::ptr; +use core::slice; + +use super::Vec; + +/// An iterator which uses a closure to determine if an element should be removed. +/// +/// This struct is created by [`Vec::extract_if`]. +/// See its documentation for more. +/// +/// # Example +/// +/// ``` +/// #![feature(extract_if)] +/// +/// let mut v = vec![0, 1, 2]; +/// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(|x| *x % 2 == 0); +/// ``` +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +#[derive(Debug)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf< + 'a, + T, + F, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> 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`. + pub(super) idx: usize, + /// The number of items that have been drained (removed) thus far. + pub(super) del: usize, + /// The original length of `vec` prior to draining. + pub(super) old_len: usize, + /// The filter test predicate. + pub(super) pred: F, +} + +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] + pub fn allocator(&self) -> &A { + self.vec.allocator() + } +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +impl Iterator for ExtractIf<'_, T, F, A> +where + F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + unsafe { + 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]); + // Update the index *after* the predicate is called. If the index + // is updated prior and the predicate panics, the element at this + // index would be leaked. + self.idx += 1; + if drained { + self.del += 1; + return Some(ptr::read(&v[i])); + } else if self.del > 0 { + let del = self.del; + let src: *const T = &v[i]; + let dst: *mut T = &mut v[i - del]; + ptr::copy_nonoverlapping(src, dst, 1); + } + } + None + } + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +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); + let tail_len = self.old_len - self.idx; + src.copy_to(dst, tail_len); + } + self.vec.set_len(self.old_len - self.del); + } + } +} diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 47661a3d3..598ecf05e 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -71,10 +71,10 @@ use crate::boxed::Box; use crate::collections::TryReserveError; use crate::raw_vec::RawVec; -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -pub use self::drain_filter::DrainFilter; +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +pub use self::extract_if::ExtractIf; -mod drain_filter; +mod extract_if; #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_splice", since = "1.21.0")] @@ -560,22 +560,20 @@ impl Vec { /// Using memory that was allocated elsewhere: /// /// ```rust - /// #![feature(allocator_api)] - /// - /// use std::alloc::{AllocError, Allocator, Global, Layout}; + /// use std::alloc::{alloc, Layout}; /// /// fn main() { /// let layout = Layout::array::(16).expect("overflow cannot happen"); /// /// let vec = unsafe { - /// let mem = match Global.allocate(layout) { - /// Ok(mem) => mem.cast::().as_ptr(), - /// Err(AllocError) => return, - /// }; + /// let mem = alloc(layout).cast::(); + /// if mem.is_null() { + /// return; + /// } /// /// mem.write(1_000_000); /// - /// Vec::from_raw_parts_in(mem, 1, 16, Global) + /// Vec::from_raw_parts(mem, 1, 16) /// }; /// /// assert_eq!(vec, &[1_000_000]); @@ -758,19 +756,22 @@ impl Vec { /// Using memory that was allocated elsewhere: /// /// ```rust - /// use std::alloc::{alloc, Layout}; + /// #![feature(allocator_api)] + /// + /// use std::alloc::{AllocError, Allocator, Global, Layout}; /// /// fn main() { /// let layout = Layout::array::(16).expect("overflow cannot happen"); + /// /// let vec = unsafe { - /// let mem = alloc(layout).cast::(); - /// if mem.is_null() { - /// return; - /// } + /// let mem = match Global.allocate(layout) { + /// Ok(mem) => mem.cast::().as_ptr(), + /// Err(AllocError) => return, + /// }; /// /// mem.write(1_000_000); /// - /// Vec::from_raw_parts(mem, 1, 16) + /// Vec::from_raw_parts_in(mem, 1, 16, Global) /// }; /// /// assert_eq!(vec, &[1_000_000]); @@ -2355,7 +2356,7 @@ impl Vec { let len = self.len(); if new_len > len { - self.extend_with(new_len - len, ExtendElement(value)) + self.extend_with(new_len - len, value) } else { self.truncate(new_len); } @@ -2469,26 +2470,10 @@ impl Vec<[T; N], A> { } } -// This code generalizes `extend_with_{element,default}`. -trait ExtendWith { - fn next(&mut self) -> T; - fn last(self) -> T; -} - -struct ExtendElement(T); -impl ExtendWith for ExtendElement { - fn next(&mut self) -> T { - self.0.clone() - } - fn last(self) -> T { - self.0 - } -} - -impl Vec { +impl Vec { #[cfg(not(no_global_oom_handling))] - /// Extend the vector by `n` values, using the given generator. - fn extend_with>(&mut self, n: usize, mut value: E) { + /// Extend the vector by `n` clones of value. + fn extend_with(&mut self, n: usize, value: T) { self.reserve(n); unsafe { @@ -2500,15 +2485,15 @@ impl Vec { // Write all elements except the last one for _ in 1..n { - ptr::write(ptr, value.next()); + ptr::write(ptr, value.clone()); ptr = ptr.add(1); - // Increment the length in every step in case next() panics + // Increment the length in every step in case clone() panics local_len.increment_len(1); } if n > 0 { // We can write the last element directly without cloning needlessly - ptr::write(ptr, value.last()); + ptr::write(ptr, value); local_len.increment_len(1); } @@ -2908,6 +2893,12 @@ impl Vec { /// If the closure returns false, the element will remain in the vector and will not be yielded /// by the iterator. /// + /// 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. + /// + /// [`retain`]: Vec::retain + /// /// Using this method is equivalent to the following code: /// /// ``` @@ -2926,10 +2917,10 @@ impl Vec { /// # assert_eq!(vec, vec![1, 4, 5]); /// ``` /// - /// But `drain_filter` is easier to use. `drain_filter` is also more efficient, + /// 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 `drain_filter` also lets you mutate every element in the filter closure, + /// 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 @@ -2937,17 +2928,17 @@ impl Vec { /// Splitting an array into evens and odds, reusing the original allocation: /// /// ``` - /// #![feature(drain_filter)] + /// #![feature(extract_if)] /// let mut numbers = vec![1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]; /// - /// let evens = numbers.drain_filter(|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]); /// ``` - #[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] - pub fn drain_filter(&mut self, filter: F) -> DrainFilter<'_, T, F, A> + #[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] + pub fn extract_if(&mut self, filter: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool, { @@ -2958,7 +2949,7 @@ impl Vec { self.set_len(0); } - DrainFilter { vec: self, idx: 0, del: 0, old_len, pred: filter, panic_flag: false } + ExtractIf { vec: self, idx: 0, del: 0, old_len, pred: filter } } } @@ -2988,9 +2979,14 @@ impl<'a, T: Copy + 'a, A: Allocator + 'a> Extend<&'a T> for Vec { /// Implements comparison of vectors, [lexicographically](Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Vec { +impl PartialOrd> for Vec +where + T: PartialOrd, + A1: Allocator, + A2: Allocator, +{ #[inline] - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Vec) -> Option { PartialOrd::partial_cmp(&**self, &**other) } } diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs index ff364c033..da43d17bf 100644 --- a/library/alloc/src/vec/spec_from_elem.rs +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -3,7 +3,7 @@ use core::ptr; use crate::alloc::Allocator; use crate::raw_vec::RawVec; -use super::{ExtendElement, IsZero, Vec}; +use super::{IsZero, Vec}; // Specialization trait used for Vec::from_elem pub(super) trait SpecFromElem: Sized { @@ -13,7 +13,7 @@ pub(super) trait SpecFromElem: Sized { impl SpecFromElem for T { default fn from_elem(elem: Self, n: usize, alloc: A) -> Vec { let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); + v.extend_with(n, elem); v } } @@ -25,7 +25,7 @@ impl SpecFromElem for T { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; } let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); + v.extend_with(n, elem); v } } diff --git a/library/alloc/tests/autotraits.rs b/library/alloc/tests/autotraits.rs index 879e32b3f..6a8e55bff 100644 --- a/library/alloc/tests/autotraits.rs +++ b/library/alloc/tests/autotraits.rs @@ -55,7 +55,7 @@ fn test_btree_map() { require_send_sync(async { let _v = None::< - alloc::collections::btree_map::DrainFilter< + alloc::collections::btree_map::ExtractIf< '_, &u32, &u32, @@ -149,7 +149,7 @@ fn test_btree_set() { }); require_send_sync(async { - let _v = None:: bool>>; + let _v = None:: bool>>; async {}.await; }); @@ -238,7 +238,7 @@ fn test_linked_list() { /* require_send_sync(async { let _v = - None:: bool>>; + None:: bool>>; async {}.await; }); */ diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 0eca4c9bb..aa7a331b3 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -1,7 +1,7 @@ #![feature(allocator_api)] #![feature(alloc_layout_extra)] #![feature(assert_matches)] -#![feature(btree_drain_filter)] +#![feature(btree_extract_if)] #![feature(cow_is_borrowed)] #![feature(const_cow_is_borrowed)] #![feature(const_heap)] @@ -10,7 +10,7 @@ #![feature(const_ptr_write)] #![feature(const_try)] #![feature(core_intrinsics)] -#![feature(drain_filter)] +#![feature(extract_if)] #![feature(exact_size_is_empty)] #![feature(linked_list_cursors)] #![feature(map_try_insert)] diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index 9aa5575ca..784839a3f 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1418,6 +1418,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::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index c1dbbde08..0ba5d088f 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(invalid_from_utf8))] + use std::assert_matches::assert_matches; use std::borrow::Cow; use std::cmp::Ordering::{Equal, Greater, Less}; diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index 99d1296a4..17d56d491 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -394,6 +394,7 @@ fn test_remove_matches() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_retain() { let mut s = String::from("α_β_γ"); diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index cc4c1f127..ddd93e9a4 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -314,6 +314,7 @@ fn test_retain_predicate_order() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_retain_pred_panic_with_hole() { let v = (0..5).map(Rc::new).collect::>(); catch_unwind(AssertUnwindSafe(|| { @@ -331,6 +332,7 @@ fn test_retain_pred_panic_with_hole() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_retain_pred_panic_no_hole() { let v = (0..5).map(Rc::new).collect::>(); catch_unwind(AssertUnwindSafe(|| { @@ -346,6 +348,7 @@ fn test_retain_pred_panic_no_hole() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_retain_drop_panic() { struct Wrap(Rc); @@ -806,6 +809,7 @@ fn test_drain_end_overflow() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drain_leak() { static mut DROPS: i32 = 0; @@ -1038,6 +1042,7 @@ fn test_into_iter_clone() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_into_iter_leak() { static mut DROPS: i32 = 0; @@ -1195,6 +1200,7 @@ fn test_from_iter_specialization_head_tail_drop() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_from_iter_specialization_panic_during_iteration_drops() { let drop_count: Vec<_> = (0..=2).map(|_| Rc::new(())).collect(); let src: Vec<_> = drop_count.iter().cloned().collect(); @@ -1219,6 +1225,7 @@ fn test_from_iter_specialization_panic_during_iteration_drops() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_from_iter_specialization_panic_during_drop_doesnt_leak() { static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5]; static mut DROP_COUNTER_NEW: [usize; 2] = [0; 2]; @@ -1340,11 +1347,11 @@ fn overaligned_allocations() { } #[test] -fn drain_filter_empty() { +fn extract_if_empty() { let mut vec: Vec = vec![]; { - let mut iter = vec.drain_filter(|_| 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))); @@ -1356,12 +1363,12 @@ fn drain_filter_empty() { } #[test] -fn drain_filter_zst() { +fn extract_if_zst() { let mut vec = vec![(), (), (), (), ()]; let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.drain_filter(|_| true); + let mut iter = vec.extract_if(|_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -1378,13 +1385,13 @@ fn drain_filter_zst() { } #[test] -fn drain_filter_false() { +fn extract_if_false() { let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.drain_filter(|_| false); + let mut iter = vec.extract_if(|_| false); assert_eq!(iter.size_hint(), (0, Some(initial_len))); for _ in iter.by_ref() { count += 1; @@ -1400,13 +1407,13 @@ fn drain_filter_false() { } #[test] -fn drain_filter_true() { +fn extract_if_true() { let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let initial_len = vec.len(); let mut count = 0; { - let mut iter = vec.drain_filter(|_| true); + let mut iter = vec.extract_if(|_| true); assert_eq!(iter.size_hint(), (0, Some(initial_len))); while let Some(_) = iter.next() { count += 1; @@ -1423,7 +1430,7 @@ fn drain_filter_true() { } #[test] -fn drain_filter_complex() { +fn extract_if_complex() { { // [+xxx++++++xxxxx++++x+x++] let mut vec = vec![ @@ -1431,7 +1438,7 @@ fn drain_filter_complex() { 39, ]; - let removed = vec.drain_filter(|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]); @@ -1445,7 +1452,7 @@ fn drain_filter_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.drain_filter(|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]); @@ -1458,7 +1465,7 @@ fn drain_filter_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.drain_filter(|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]); @@ -1470,7 +1477,7 @@ fn drain_filter_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.drain_filter(|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]); @@ -1482,7 +1489,7 @@ fn drain_filter_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.drain_filter(|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]); @@ -1494,7 +1501,8 @@ fn drain_filter_complex() { // FIXME: re-enable emscripten once it can unwind again #[test] #[cfg(not(target_os = "emscripten"))] -fn drain_filter_consumed_panic() { +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn extract_if_consumed_panic() { use std::rc::Rc; use std::sync::Mutex; @@ -1529,9 +1537,9 @@ fn drain_filter_consumed_panic() { } c.index < 6 }; - let drain = data.drain_filter(filter); + let drain = data.extract_if(filter); - // NOTE: The DrainFilter is explicitly consumed + // NOTE: The ExtractIf is explicitly consumed drain.for_each(drop); }); @@ -1546,7 +1554,8 @@ fn drain_filter_consumed_panic() { // FIXME: Re-enable emscripten once it can catch panics #[test] #[cfg(not(target_os = "emscripten"))] -fn drain_filter_unconsumed_panic() { +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn extract_if_unconsumed_panic() { use std::rc::Rc; use std::sync::Mutex; @@ -1581,9 +1590,9 @@ fn drain_filter_unconsumed_panic() { } c.index < 6 }; - let _drain = data.drain_filter(filter); + let _drain = data.extract_if(filter); - // NOTE: The DrainFilter is dropped without being consumed + // NOTE: The ExtractIf is dropped without being consumed }); let drop_counts = drop_counts.lock().unwrap(); @@ -1595,40 +1604,11 @@ fn drain_filter_unconsumed_panic() { } #[test] -fn drain_filter_unconsumed() { +fn extract_if_unconsumed() { let mut vec = vec![1, 2, 3, 4]; - let drain = vec.drain_filter(|&mut x| x % 2 != 0); + let drain = vec.extract_if(|&mut x| x % 2 != 0); drop(drain); - assert_eq!(vec, [2, 4]); -} - -#[test] -fn test_drain_filter_keep_rest() { - let mut v = vec![0, 1, 2, 3, 4, 5, 6]; - let mut drain = v.drain_filter(|&mut x| x % 2 == 0); - assert_eq!(drain.next(), Some(0)); - assert_eq!(drain.next(), Some(2)); - - drain.keep_rest(); - assert_eq!(v, &[1, 3, 4, 5, 6]); -} - -#[test] -fn test_drain_filter_keep_rest_all() { - let mut v = vec![0, 1, 2, 3, 4, 5, 6]; - v.drain_filter(|_| true).keep_rest(); - assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6]); -} - -#[test] -fn test_drain_filter_keep_rest_none() { - let mut v = vec![0, 1, 2, 3, 4, 5, 6]; - let mut drain = v.drain_filter(|_| true); - - drain.by_ref().for_each(drop); - - drain.keep_rest(); - assert_eq!(v, &[]); + assert_eq!(vec, [1, 2, 3, 4]); } #[test] @@ -1926,6 +1906,7 @@ fn test_stable_pointers() { assert_eq!(*v0, 13); // Smoke test that would fire even outside Miri if an actual relocation happened. + // Also ensures the pointer is still writeable after all this. *v0 -= 13; assert_eq!(v[0], 0); } @@ -2414,6 +2395,7 @@ fn test_vec_dedup() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_vec_dedup_panicking() { #[derive(Debug)] struct Panic<'a> { @@ -2470,6 +2452,7 @@ fn test_vec_dedup_panicking() { // Regression test for issue #82533 #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_extend_from_within_panicking_clone() { struct Panic<'dc> { drop_count: &'dc AtomicU32, diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index ddc27e34e..f6fb1f73e 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -747,6 +747,7 @@ fn test_drop_clear() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drop_panic() { static mut DROPS: i32 = 0; @@ -1601,6 +1602,7 @@ fn test_try_rfold_moves_iter() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn truncate_leak() { static mut DROPS: i32 = 0; @@ -1634,6 +1636,7 @@ fn truncate_leak() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_drain_leak() { static mut DROPS: i32 = 0; diff --git a/library/core/benches/iter.rs b/library/core/benches/iter.rs index 60ef83223..5ec22e514 100644 --- a/library/core/benches/iter.rs +++ b/library/core/benches/iter.rs @@ -2,6 +2,7 @@ use core::borrow::Borrow; use core::iter::*; use core::mem; use core::num::Wrapping; +use core::ops::Range; use test::{black_box, Bencher}; #[bench] @@ -69,6 +70,57 @@ fn bench_max(b: &mut Bencher) { }) } +#[bench] +fn bench_range_step_by_sum_reducible(b: &mut Bencher) { + let r = 0u32..1024; + b.iter(|| { + let r = black_box(r.clone()).step_by(8); + + let mut sum: u32 = 0; + for i in r { + sum += i; + } + + sum + }) +} + +#[bench] +fn bench_range_step_by_loop_u32(b: &mut Bencher) { + let r = 0..(u16::MAX as u32); + b.iter(|| { + let r = black_box(r.clone()).step_by(64); + + let mut sum: u32 = 0; + for i in r { + let i = i ^ i.wrapping_sub(1); + sum = sum.wrapping_add(i); + } + + sum + }) +} + +#[bench] +fn bench_range_step_by_fold_usize(b: &mut Bencher) { + let r: Range = 0..(u16::MAX as usize); + b.iter(|| { + let r = black_box(r.clone()); + r.step_by(64) + .map(|x: usize| x ^ (x.wrapping_sub(1))) + .fold(0usize, |acc, i| acc.wrapping_add(i)) + }) +} + +#[bench] +fn bench_range_step_by_fold_u16(b: &mut Bencher) { + let r: Range = 0..u16::MAX; + b.iter(|| { + let r = black_box(r.clone()); + r.step_by(64).map(|x: u16| x ^ (x.wrapping_sub(1))).fold(0u16, |acc, i| acc.wrapping_add(i)) + }) +} + pub fn copy_zip(xs: &[u8], ys: &mut [u8]) { for (a, b) in ys.iter_mut().zip(xs) { *a = *b; diff --git a/library/core/benches/slice.rs b/library/core/benches/slice.rs index 9b86a0ca9..3bfb35e68 100644 --- a/library/core/benches/slice.rs +++ b/library/core/benches/slice.rs @@ -1,3 +1,4 @@ +use core::ptr::NonNull; use test::black_box; use test::Bencher; @@ -162,3 +163,11 @@ fn fill_byte_sized(b: &mut Bencher) { black_box(slice.fill(black_box(NewType(42)))); }); } + +// Tests the ability of the compiler to recognize that only the last slice item is needed +// based on issue #106288 +#[bench] +fn fold_to_last(b: &mut Bencher) { + let slice: &[i32] = &[0; 1024]; + b.iter(|| black_box(slice).iter().fold(None, |_, r| Some(NonNull::from(r)))); +} diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index d6ae2b821..78091c017 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -94,8 +94,9 @@ impl fmt::Display for AllocError { /// /// # Safety /// -/// * Memory blocks returned from an allocator must point to valid memory and retain their validity -/// until the instance and all of its copies and clones are 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 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 this /// allocator. A copied or cloned allocator must behave like the same allocator, and diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 7969f4055..09f52d692 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -153,6 +153,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::fmt; +use crate::hash; use crate::intrinsics; /////////////////////////////////////////////////////////////////////////////// @@ -662,10 +663,10 @@ impl dyn Any + Send + Sync { /// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth /// noting that the hashes and ordering will vary between Rust releases. Beware /// of relying on them inside of your code! -#[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { - t: u64, + t: u128, } #[stable(feature = "rust1", since = "1.0.0")] @@ -696,7 +697,31 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub const fn of() -> TypeId { - TypeId { t: intrinsics::type_id::() } + #[cfg(bootstrap)] + let t = intrinsics::type_id::() as u128; + #[cfg(not(bootstrap))] + let t: u128 = intrinsics::type_id::(); + TypeId { t } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for TypeId { + #[inline] + fn hash(&self, state: &mut H) { + // We only hash the lower 64 bits of our (128 bit) internal numeric ID, + // because: + // - The hashing algorithm which backs `TypeId` is expected to be + // unbiased and high quality, meaning further mixing would be somewhat + // redundant compared to choosing (the lower) 64 bits arbitrarily. + // - `Hasher::finish` returns a u64 anyway, so the extra entropy we'd + // get from hashing the full value would probably not be useful + // (especially given the previous point about the lower 64 bits being + // high quality on their own). + // - It is correct to do so -- only hashing a subset of `self` is still + // with an `Eq` implementation that considers the entire value, as + // ours does. + (self.t as u64).hash(state); } } diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index fec92320a..76b3589b9 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -538,29 +538,6 @@ impl [T; N] { drain_array_with(self, |iter| try_from_trusted_iterator(iter.map(f))) } - /// 'Zips up' two arrays into a single array of pairs. - /// - /// `zip()` returns a new array where every element is a tuple where the - /// first element comes from the first array, and the second element comes - /// from the second array. In other words, it zips two arrays together, - /// into a single one. - /// - /// # Examples - /// - /// ``` - /// #![feature(array_zip)] - /// let x = [1, 2, 3]; - /// let y = [4, 5, 6]; - /// let z = x.zip(y); - /// assert_eq!(z, [(1, 4), (2, 5), (3, 6)]); - /// ``` - #[unstable(feature = "array_zip", issue = "80094")] - pub fn zip(self, rhs: [U; N]) -> [(T, U); N] { - drain_array_with(self, |lhs| { - drain_array_with(rhs, |rhs| from_trusted_iterator(crate::iter::zip(lhs, rhs))) - }) - } - /// Returns a slice containing the entire array. Equivalent to `&s[..]`. #[stable(feature = "array_as_slice", since = "1.57.0")] #[rustc_const_stable(feature = "array_as_slice", since = "1.57.0")] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 744767aae..909b32547 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -59,7 +59,7 @@ //! [`borrow`](`RefCell::borrow`), and a mutable borrow (`&mut T`) can be obtained with //! [`borrow_mut`](`RefCell::borrow_mut`). When these functions are called, they first verify that //! Rust's borrow rules will be satisfied: any number of immutable borrows are allowed or a -//! single immutable borrow is allowed, but never both. If a borrow is attempted that would violate +//! single mutable borrow is allowed, but never both. If a borrow is attempted that would violate //! these rules, the thread will panic. //! //! The corresponding [`Sync`] version of `RefCell` is [`RwLock`]. @@ -1374,7 +1374,7 @@ impl Clone for BorrowRef<'_> { debug_assert!(is_reading(borrow)); // Prevent the borrow counter from overflowing into // a writing borrow. - assert!(borrow != isize::MAX); + assert!(borrow != BorrowFlag::MAX); self.borrow.set(borrow + 1); BorrowRef { borrow: self.borrow } } @@ -1756,7 +1756,7 @@ impl<'b> BorrowRefMut<'b> { let borrow = self.borrow.get(); debug_assert!(is_writing(borrow)); // Prevent the borrow counter from underflowing. - assert!(borrow != isize::MIN); + assert!(borrow != BorrowFlag::MIN); self.borrow.set(borrow - 1); BorrowRefMut { borrow: self.borrow } } diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 38a6d1ccd..ff5a4c913 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -495,8 +495,7 @@ pub trait Into: Sized { /// By converting underlying error types to our own custom error type that encapsulates the /// underlying error type, we can return a single error type without losing information on the /// underlying cause. The '?' operator automatically converts the underlying error type to our -/// custom error type by calling `Into::into` which is automatically provided when -/// implementing `From`. The compiler then infers which implementation of `Into` should be used. +/// custom error type with `From::from`. /// /// ``` /// use std::fs; @@ -533,7 +532,7 @@ pub trait Into: Sized { #[rustc_diagnostic_item = "From"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented(on( - all(_Self = "&str", T = "std::string::String"), + all(_Self = "&str", any(T = "alloc::string::String", T = "std::string::String")), note = "to coerce a `{T}` into a `{Self}`, use `&*` as a prefix", ))] pub trait From: Sized { diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 09dbc9581..1f7be85d3 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -190,7 +190,7 @@ macro_rules! default_impl { ($t:ty, $v:expr, $doc:tt) => { #[stable(feature = "rust1", since = "1.0.0")] impl Default for $t { - #[inline] + #[inline(always)] #[doc = $doc] fn default() -> $t { $v diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index e1e1a9b40..39f795c1f 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -81,7 +81,7 @@ use crate::str; #[derive(Hash)] #[stable(feature = "core_c_str", since = "1.64.0")] #[rustc_has_incoherent_inherent_impls] -#[cfg_attr(not(bootstrap), lang = "CStr")] +#[lang = "CStr"] // FIXME: // `fn from` in `impl From<&CStr> for Box` current implementation relies // on `CStr` being layout-compatible with `[u8]`. @@ -241,7 +241,7 @@ impl CStr { /// ``` /// /// ``` - /// #![feature(const_cstr_methods)] + /// #![feature(const_cstr_from_ptr)] /// /// use std::ffi::{c_char, CStr}; /// @@ -256,7 +256,7 @@ impl CStr { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")] + #[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "101719")] pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr { // SAFETY: The caller has provided a pointer that points to a valid C // string with a NUL terminator of size less than `isize::MAX`, whose @@ -377,7 +377,7 @@ impl CStr { /// assert!(cstr.is_err()); /// ``` #[stable(feature = "cstr_from_bytes", since = "1.10.0")] - #[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")] + #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> { let nul_pos = memchr::memchr(0, bytes); match nul_pos { @@ -561,10 +561,12 @@ impl CStr { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - pub fn to_bytes(&self) -> &[u8] { + #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] + pub const fn to_bytes(&self) -> &[u8] { let bytes = self.to_bytes_with_nul(); + // FIXME(const-hack) replace with range index // SAFETY: to_bytes_with_nul returns slice with length at least 1 - unsafe { bytes.get_unchecked(..bytes.len() - 1) } + unsafe { slice::from_raw_parts(bytes.as_ptr(), bytes.len() - 1) } } /// Converts this C string to a byte slice containing the trailing 0 byte. @@ -588,7 +590,7 @@ impl CStr { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")] + #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] pub const fn to_bytes_with_nul(&self) -> &[u8] { // SAFETY: Transmuting a slice of `c_char`s to a slice of `u8`s // is safe on all supported targets. @@ -612,7 +614,8 @@ impl CStr { /// assert_eq!(cstr.to_str(), Ok("foo")); /// ``` #[stable(feature = "cstr_to_str", since = "1.4.0")] - pub fn to_str(&self) -> Result<&str, str::Utf8Error> { + #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] + pub const fn to_str(&self) -> Result<&str, str::Utf8Error> { // N.B., when `CStr` is changed to perform the length check in `.to_bytes()` // instead of in `from_ptr()`, it may be worth considering if this should // be rewritten to do the UTF-8 check inline with the length calculation diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index b73abbbac..0488c8076 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -132,7 +132,12 @@ mod c_char_definition { ), all( target_os = "netbsd", - any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "riscv64" + ) ), all( target_os = "vxworks", @@ -202,7 +207,7 @@ mod c_long_definition { // would be uninhabited and at least dereferencing such pointers would // be UB. #[doc = include_str!("c_void.md")] -#[cfg_attr(not(bootstrap), lang = "c_void")] +#[lang = "c_void"] #[cfg_attr(not(doc), repr(u8))] // work around https://github.com/rust-lang/rust/issues/90435 #[stable(feature = "core_c_void", since = "1.30.0")] pub enum c_void { @@ -238,7 +243,7 @@ impl fmt::Debug for c_void { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")), + all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")), target_family = "wasm", target_arch = "asmjs", target_os = "uefi", @@ -267,7 +272,7 @@ pub struct VaListImpl<'f> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")), + all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")), target_family = "wasm", target_arch = "asmjs", target_os = "uefi", @@ -292,7 +297,7 @@ impl<'f> fmt::Debug for VaListImpl<'f> { /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf #[cfg(all( target_arch = "aarch64", - not(any(target_os = "macos", target_os = "ios")), + not(any(target_os = "macos", target_os = "ios", target_os = "tvos")), not(target_os = "uefi"), not(windows), ))] @@ -389,7 +394,10 @@ pub struct VaList<'a, 'f: 'a> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")), + all( + target_arch = "aarch64", + any(target_os = "macos", target_os = "ios", target_os = "tvos") + ), target_family = "wasm", target_arch = "asmjs", target_os = "uefi", @@ -404,7 +412,10 @@ pub struct VaList<'a, 'f: 'a> { target_arch = "s390x", target_arch = "x86_64" ), - any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))), + any( + not(target_arch = "aarch64"), + not(any(target_os = "macos", target_os = "ios", target_os = "tvos")) + ), not(target_family = "wasm"), not(target_arch = "asmjs"), not(target_os = "uefi"), @@ -422,7 +433,7 @@ pub struct VaList<'a, 'f: 'a> { not(target_arch = "s390x"), not(target_arch = "x86_64") ), - all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")), + all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios", target_os = "tvos")), target_family = "wasm", target_arch = "asmjs", target_os = "uefi", @@ -449,7 +460,10 @@ impl<'f> VaListImpl<'f> { target_arch = "s390x", target_arch = "x86_64" ), - any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))), + any( + not(target_arch = "aarch64"), + not(any(target_os = "macos", target_os = "ios", target_os = "tvos")) + ), not(target_family = "wasm"), not(target_arch = "asmjs"), not(target_os = "uefi"), diff --git a/library/core/src/future/poll_fn.rs b/library/core/src/future/poll_fn.rs index 90cb79739..d27a9dfc1 100644 --- a/library/core/src/future/poll_fn.rs +++ b/library/core/src/future/poll_fn.rs @@ -24,6 +24,93 @@ use crate::task::{Context, Poll}; /// assert_eq!(read_future.await, "Hello, World!".to_owned()); /// # } /// ``` +/// +/// ## Capturing a pinned state +/// +/// Example of a closure wrapping inner futures: +/// +/// ``` +/// # async fn run() { +/// use core::future::{self, Future}; +/// use core::task::Poll; +/// +/// /// Resolves to the first future that completes. In the event of a tie, `a` wins. +/// fn naive_select( +/// a: impl Future, +/// b: impl Future, +/// ) -> impl Future +/// { +/// let (mut a, mut b) = (Box::pin(a), Box::pin(b)); +/// future::poll_fn(move |cx| { +/// if let Poll::Ready(r) = a.as_mut().poll(cx) { +/// Poll::Ready(r) +/// } else if let Poll::Ready(r) = b.as_mut().poll(cx) { +/// Poll::Ready(r) +/// } else { +/// Poll::Pending +/// } +/// }) +/// } +/// +/// let a = async { 42 }; +/// let b = future::pending(); +/// let v = naive_select(a, b).await; +/// assert_eq!(v, 42); +/// +/// let a = future::pending(); +/// let b = async { 27 }; +/// let v = naive_select(a, b).await; +/// assert_eq!(v, 27); +/// +/// let a = async { 42 }; +/// let b = async { 27 }; +/// let v = naive_select(a, b).await; +/// assert_eq!(v, 42); // biased towards `a` in case of tie! +/// # } +/// ``` +/// +/// This time without [`Box::pin`]ning: +/// +/// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin +/// +/// ``` +/// # async fn run() { +/// use core::future::{self, Future}; +/// use core::pin::pin; +/// use core::task::Poll; +/// +/// /// Resolves to the first future that completes. In the event of a tie, `a` wins. +/// fn naive_select( +/// a: impl Future, +/// b: impl Future, +/// ) -> impl Future +/// { +/// async { +/// let (mut a, mut b) = (pin!(a), pin!(b)); +/// future::poll_fn(move |cx| { +/// if let Poll::Ready(r) = a.as_mut().poll(cx) { +/// Poll::Ready(r) +/// } else if let Poll::Ready(r) = b.as_mut().poll(cx) { +/// Poll::Ready(r) +/// } else { +/// Poll::Pending +/// } +/// }).await +/// } +/// } +/// +/// let a = async { 42 }; +/// let b = future::pending(); +/// let v = naive_select(a, b).await; +/// assert_eq!(v, 42); +/// # } +/// ``` +/// +/// - Notice how, by virtue of being in an `async` context, we have been able to make the [`pin!`] +/// macro work, thereby avoiding any need for the `unsafe` +/// [Pin::new_unchecked](&mut fut) constructor. +/// +/// [`pin!`]: crate::pin::pin! #[stable(feature = "future_poll_fn", since = "1.64.0")] pub fn poll_fn(f: F) -> PollFn where diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index f5c5dd29f..5a9a7013a 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1057,8 +1057,25 @@ extern "rust-intrinsic" { #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] #[rustc_safe_intrinsic] #[rustc_nounwind] + #[cfg(bootstrap)] pub fn type_id() -> u64; + /// Gets an identifier which is globally unique to the specified type. This + /// function will return the same value for a type regardless of whichever + /// crate it is invoked in. + /// + /// 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 [`core::any::TypeId::of`]. + #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn type_id() -> u128; + /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: /// This will statically either panic, or do nothing. /// @@ -1385,7 +1402,6 @@ extern "rust-intrinsic" { /// /// This is not expected to ever be exposed directly to users, rather it /// may eventually be exposed through some more-constrained API. - #[cfg(not(bootstrap))] #[rustc_const_stable(feature = "const_transmute", since = "1.56.0")] #[rustc_nounwind] pub fn transmute_unchecked(src: Src) -> Dst; @@ -1425,19 +1441,11 @@ extern "rust-intrinsic" { /// returned value will result in undefined behavior. /// /// The stabilized version of this intrinsic is [`pointer::offset`]. - #[cfg(not(bootstrap))] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] #[rustc_nounwind] pub fn offset(dst: Ptr, offset: Delta) -> Ptr; - /// The bootstrap version of this is more restricted. - #[cfg(bootstrap)] - #[must_use = "returns a new pointer rather than modifying its argument"] - #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] - #[rustc_nounwind] - pub fn offset(dst: *const T, offset: isize) -> *const T; - /// Calculates the offset from a pointer, potentially wrapping. /// /// This is implemented as an intrinsic to avoid converting to and from an @@ -2270,7 +2278,6 @@ extern "rust-intrinsic" { /// 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. - #[cfg(not(bootstrap))] #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] #[rustc_nounwind] pub fn write_via_move(ptr: *mut T, value: T); @@ -2650,7 +2657,7 @@ pub(crate) fn is_nonoverlapping(src: *const T, dst: *const T, count: usize) - #[stable(feature = "rust1", since = "1.0.0")] #[rustc_allowed_through_unstable_modules] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")] -#[inline] +#[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { @@ -2741,7 +2748,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us #[stable(feature = "rust1", since = "1.0.0")] #[rustc_allowed_through_unstable_modules] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")] -#[inline] +#[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { @@ -2814,7 +2821,7 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_allowed_through_unstable_modules] #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] -#[inline] +#[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { extern "rust-intrinsic" { @@ -2832,24 +2839,3 @@ pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) { write_bytes(dst, val, count) } } - -/// Polyfill for bootstrap -#[cfg(bootstrap)] -pub const unsafe fn transmute_unchecked(src: Src) -> Dst { - use crate::mem::*; - // SAFETY: It's a transmute -- the caller promised it's fine. - unsafe { transmute_copy(&ManuallyDrop::new(src)) } -} - -/// Polyfill for bootstrap -#[cfg(bootstrap)] -pub const unsafe fn write_via_move(ptr: *mut T, value: T) { - use crate::mem::*; - // SAFETY: the caller must guarantee that `dst` is valid for writes. - // `dst` cannot overlap `src` because the caller has mutable access - // to `dst` while `src` is owned by this function. - unsafe { - copy_nonoverlapping::(&value, ptr, 1); - forget(value); - } -} diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs index 2568aaf34..d3e454563 100644 --- a/library/core/src/iter/adapters/flatten.rs +++ b/library/core/src/iter/adapters/flatten.rs @@ -310,7 +310,7 @@ where /// Real logic of both `Flatten` and `FlatMap` which simply delegate to /// this type. #[derive(Clone, Debug)] -#[unstable(feature = "trusted_len", issue = "37572")] +#[cfg_attr(bootstrap, unstable(feature = "trusted_len", issue = "37572"))] struct FlattenCompat { iter: Fuse, frontiter: Option, @@ -464,7 +464,7 @@ where } } -#[unstable(feature = "trusted_len", issue = "37572")] +#[cfg_attr(bootstrap, unstable(feature = "trusted_len", issue = "37572"))] impl Iterator for FlattenCompat where I: Iterator>, @@ -579,7 +579,7 @@ where } } -#[unstable(feature = "trusted_len", issue = "37572")] +#[cfg_attr(bootstrap, unstable(feature = "trusted_len", issue = "37572"))] impl DoubleEndedIterator for FlattenCompat where I: DoubleEndedIterator>, @@ -649,7 +649,7 @@ where } } -#[unstable(feature = "trusted_len", issue = "37572")] +#[cfg_attr(bootstrap, unstable(feature = "trusted_len", issue = "37572"))] unsafe impl TrustedLen for FlattenCompat::IntoIter> where @@ -657,7 +657,7 @@ where { } -#[unstable(feature = "trusted_len", issue = "37572")] +#[cfg_attr(bootstrap, unstable(feature = "trusted_len", issue = "37572"))] unsafe impl<'a, const N: usize, I, T> TrustedLen for FlattenCompat::IntoIter> where @@ -665,7 +665,7 @@ where { } -#[unstable(feature = "trusted_len", issue = "37572")] +#[cfg_attr(bootstrap, unstable(feature = "trusted_len", issue = "37572"))] unsafe impl<'a, const N: usize, I, T> TrustedLen for FlattenCompat::IntoIter> where diff --git a/library/core/src/iter/adapters/step_by.rs b/library/core/src/iter/adapters/step_by.rs index 4252c34a0..7f58f7d17 100644 --- a/library/core/src/iter/adapters/step_by.rs +++ b/library/core/src/iter/adapters/step_by.rs @@ -1,4 +1,9 @@ -use crate::{intrinsics, iter::from_fn, ops::Try}; +use crate::convert::TryFrom; +use crate::{ + intrinsics, + iter::{from_fn, TrustedLen}, + ops::{Range, Try}, +}; /// An iterator for stepping iterators by a custom amount. /// @@ -11,14 +16,22 @@ use crate::{intrinsics, iter::from_fn, ops::Try}; #[stable(feature = "iterator_step_by", since = "1.28.0")] #[derive(Clone, Debug)] pub struct StepBy { + /// This field is guaranteed to be preprocessed by the specialized `SpecRangeSetup::setup` + /// in the constructor. + /// For most iterators that processing is a no-op, but for Range<{integer}> types it is lossy + /// which means the inner iterator cannot be returned to user code. + /// Additionally this type-dependent preprocessing means specialized implementations + /// cannot be used interchangeably. iter: I, step: usize, first_take: bool, } impl StepBy { + #[inline] pub(in crate::iter) fn new(iter: I, step: usize) -> StepBy { assert!(step != 0); + let iter = >::setup(iter, step); StepBy { iter, step: step - 1, first_take: true } } } @@ -32,16 +45,174 @@ where #[inline] fn next(&mut self) -> Option { + self.spec_next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.spec_size_hint() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.spec_nth(n) + } + + fn try_fold(&mut self, acc: Acc, f: F) -> R + where + F: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.spec_try_fold(acc, f) + } + + #[inline] + fn fold(self, acc: Acc, f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + self.spec_fold(acc, f) + } +} + +impl StepBy +where + I: ExactSizeIterator, +{ + // The zero-based index starting from the end of the iterator of the + // last element. Used in the `DoubleEndedIterator` implementation. + fn next_back_index(&self) -> usize { + let rem = self.iter.len() % (self.step + 1); if self.first_take { - self.first_take = false; - self.iter.next() + if rem == 0 { self.step } else { rem - 1 } } else { - self.iter.nth(self.step) + rem } } +} +#[stable(feature = "double_ended_step_by_iterator", since = "1.38.0")] +impl DoubleEndedIterator for StepBy +where + I: DoubleEndedIterator + ExactSizeIterator, +{ #[inline] - fn size_hint(&self) -> (usize, Option) { + fn next_back(&mut self) -> Option { + self.spec_next_back() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + self.spec_nth_back(n) + } + + fn try_rfold(&mut self, init: Acc, f: F) -> R + where + F: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.spec_try_rfold(init, f) + } + + #[inline] + fn rfold(self, init: Acc, f: F) -> Acc + where + Self: Sized, + F: FnMut(Acc, Self::Item) -> Acc, + { + self.spec_rfold(init, f) + } +} + +// StepBy can only make the iterator shorter, so the len will still fit. +#[stable(feature = "iterator_step_by", since = "1.28.0")] +impl ExactSizeIterator for StepBy where I: ExactSizeIterator {} + +trait SpecRangeSetup { + fn setup(inner: T, step: usize) -> T; +} + +impl SpecRangeSetup for T { + #[inline] + default fn setup(inner: T, _step: usize) -> T { + inner + } +} + +/// Specialization trait to optimize `StepBy>` iteration. +/// +/// # Safety +/// +/// Technically this is safe to implement (look ma, no unsafe!), but in reality +/// a lot of unsafe code relies on ranges over integers being correct. +/// +/// For correctness *all* public StepBy methods must be specialized +/// because `setup` drastically alters the meaning of the struct fields so that mixing +/// different implementations would lead to incorrect results. +unsafe trait StepByImpl { + type Item; + + fn spec_next(&mut self) -> Option; + + fn spec_size_hint(&self) -> (usize, Option); + + fn spec_nth(&mut self, n: usize) -> Option; + + fn spec_try_fold(&mut self, acc: Acc, f: F) -> R + where + F: FnMut(Acc, Self::Item) -> R, + R: Try; + + fn spec_fold(self, acc: Acc, f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc; +} + +/// Specialization trait for double-ended iteration. +/// +/// See also: `StepByImpl` +/// +/// # Safety +/// +/// The specializations must be implemented together with `StepByImpl` +/// where applicable. I.e. if `StepBy` does support backwards iteration +/// for a given iterator and that is specialized for forward iteration then +/// it must also be specialized for backwards iteration. +unsafe trait StepByBackImpl { + type Item; + + fn spec_next_back(&mut self) -> Option + where + I: DoubleEndedIterator + ExactSizeIterator; + + fn spec_nth_back(&mut self, n: usize) -> Option + where + I: DoubleEndedIterator + ExactSizeIterator; + + fn spec_try_rfold(&mut self, init: Acc, f: F) -> R + where + I: DoubleEndedIterator + ExactSizeIterator, + F: FnMut(Acc, Self::Item) -> R, + R: Try; + + fn spec_rfold(self, init: Acc, f: F) -> Acc + where + I: DoubleEndedIterator + ExactSizeIterator, + F: FnMut(Acc, Self::Item) -> Acc; +} + +unsafe impl StepByImpl for StepBy { + type Item = I::Item; + + #[inline] + default fn spec_next(&mut self) -> Option { + let step_size = if self.first_take { 0 } else { self.step }; + self.first_take = false; + self.iter.nth(step_size) + } + + #[inline] + default fn spec_size_hint(&self) -> (usize, Option) { #[inline] fn first_size(step: usize) -> impl Fn(usize) -> usize { move |n| if n == 0 { 0 } else { 1 + (n - 1) / (step + 1) } @@ -64,7 +235,7 @@ where } #[inline] - fn nth(&mut self, mut n: usize) -> Option { + default fn spec_nth(&mut self, mut n: usize) -> Option { if self.first_take { self.first_take = false; let first = self.iter.next(); @@ -108,7 +279,7 @@ where } } - fn try_fold(&mut self, mut acc: Acc, mut f: F) -> R + default fn spec_try_fold(&mut self, mut acc: Acc, mut f: F) -> R where F: FnMut(Acc, Self::Item) -> R, R: Try, @@ -128,7 +299,7 @@ where from_fn(nth(&mut self.iter, self.step)).try_fold(acc, f) } - fn fold(mut self, mut acc: Acc, mut f: F) -> Acc + default fn spec_fold(mut self, mut acc: Acc, mut f: F) -> Acc where F: FnMut(Acc, Self::Item) -> Acc, { @@ -148,34 +319,16 @@ where } } -impl StepBy -where - I: ExactSizeIterator, -{ - // The zero-based index starting from the end of the iterator of the - // last element. Used in the `DoubleEndedIterator` implementation. - fn next_back_index(&self) -> usize { - let rem = self.iter.len() % (self.step + 1); - if self.first_take { - if rem == 0 { self.step } else { rem - 1 } - } else { - rem - } - } -} +unsafe impl StepByBackImpl for StepBy { + type Item = I::Item; -#[stable(feature = "double_ended_step_by_iterator", since = "1.38.0")] -impl DoubleEndedIterator for StepBy -where - I: DoubleEndedIterator + ExactSizeIterator, -{ #[inline] - fn next_back(&mut self) -> Option { + default fn spec_next_back(&mut self) -> Option { self.iter.nth_back(self.next_back_index()) } #[inline] - fn nth_back(&mut self, n: usize) -> Option { + default fn spec_nth_back(&mut self, n: usize) -> Option { // `self.iter.nth_back(usize::MAX)` does the right thing here when `n` // is out of bounds because the length of `self.iter` does not exceed // `usize::MAX` (because `I: ExactSizeIterator`) and `nth_back` is @@ -184,7 +337,7 @@ where self.iter.nth_back(n) } - fn try_rfold(&mut self, init: Acc, mut f: F) -> R + default fn spec_try_rfold(&mut self, init: Acc, mut f: F) -> R where F: FnMut(Acc, Self::Item) -> R, R: Try, @@ -207,10 +360,10 @@ where } #[inline] - fn rfold(mut self, init: Acc, mut f: F) -> Acc + default fn spec_rfold(mut self, init: Acc, mut f: F) -> Acc where Self: Sized, - F: FnMut(Acc, Self::Item) -> Acc, + F: FnMut(Acc, I::Item) -> Acc, { #[inline] fn nth_back( @@ -230,6 +383,192 @@ where } } -// StepBy can only make the iterator shorter, so the len will still fit. -#[stable(feature = "iterator_step_by", since = "1.28.0")] -impl ExactSizeIterator for StepBy where I: ExactSizeIterator {} +/// For these implementations, `SpecRangeSetup` calculates the number +/// of iterations that will be needed and stores that in `iter.end`. +/// +/// The various iterator implementations then rely on that to not need +/// overflow checking, letting loops just be counted instead. +/// +/// These only work for unsigned types, and will need to be reworked +/// if you want to use it to specialize on signed types. +/// +/// Currently these are only implemented for integers up to usize due to +/// correctness issues around ExactSizeIterator impls on 16bit platforms. +/// And since ExactSizeIterator is a prerequisite for backwards iteration +/// and we must consistently specialize backwards and forwards iteration +/// that makes the situation complicated enough that it's not covered +/// for now. +macro_rules! spec_int_ranges { + ($($t:ty)*) => ($( + + const _: () = assert!(usize::BITS >= <$t>::BITS); + + impl SpecRangeSetup> for Range<$t> { + #[inline] + fn setup(mut r: Range<$t>, step: usize) -> Range<$t> { + let inner_len = r.size_hint().0; + // If step exceeds $t::MAX, then the count will be at most 1 and + // thus always fit into $t. + let yield_count = inner_len.div_ceil(step); + // Turn the range end into an iteration counter + r.end = yield_count as $t; + r + } + } + + unsafe impl StepByImpl> for StepBy> { + #[inline] + fn spec_next(&mut self) -> Option<$t> { + // if a step size larger than the type has been specified fall back to + // t::MAX, in which case remaining will be at most 1. + // The `+ 1` can't overflow since the constructor substracted 1 from the original value. + let step = <$t>::try_from(self.step + 1).unwrap_or(<$t>::MAX); + let remaining = self.iter.end; + if remaining > 0 { + let val = self.iter.start; + // this can only overflow during the last step, after which the value + // will not be used + self.iter.start = val.wrapping_add(step); + self.iter.end = remaining - 1; + Some(val) + } else { + None + } + } + + #[inline] + fn spec_size_hint(&self) -> (usize, Option) { + let remaining = self.iter.end as usize; + (remaining, Some(remaining)) + } + + // The methods below are all copied from the Iterator trait default impls. + // We have to repeat them here so that the specialization overrides the StepByImpl defaults + + #[inline] + fn spec_nth(&mut self, n: usize) -> Option { + self.advance_by(n).ok()?; + self.next() + } + + #[inline] + fn spec_try_fold(&mut self, init: Acc, mut f: F) -> R + where + F: FnMut(Acc, Self::Item) -> R, + R: Try + { + let mut accum = init; + while let Some(x) = self.next() { + accum = f(accum, x)?; + } + try { accum } + } + + #[inline] + fn spec_fold(self, init: Acc, mut f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc + { + // if a step size larger than the type has been specified fall back to + // t::MAX, in which case remaining will be at most 1. + let step = <$t>::try_from(self.step + 1).unwrap_or(<$t>::MAX); + let remaining = self.iter.end; + let mut acc = init; + let mut val = self.iter.start; + for _ in 0..remaining { + acc = f(acc, val); + // this can only overflow during the last step, after which the value + // will no longer be used + val = val.wrapping_add(step); + } + acc + } + } + + /// Safety: This macro is only applied to ranges over types <= usize + /// which means the inner length is guaranteed to fit into a usize and so + /// the outer length calculation won't encounter clamped values + #[unstable(feature = "trusted_len", issue = "37572")] + unsafe impl TrustedLen for StepBy> {} + )*) +} + +macro_rules! spec_int_ranges_r { + ($($t:ty)*) => ($( + const _: () = assert!(usize::BITS >= <$t>::BITS); + + unsafe impl StepByBackImpl> for StepBy> { + + #[inline] + fn spec_next_back(&mut self) -> Option + where Range<$t>: DoubleEndedIterator + ExactSizeIterator, + { + let step = (self.step + 1) as $t; + let remaining = self.iter.end; + if remaining > 0 { + let start = self.iter.start; + self.iter.end = remaining - 1; + Some(start + step * (remaining - 1)) + } else { + None + } + } + + // The methods below are all copied from the Iterator trait default impls. + // We have to repeat them here so that the specialization overrides the StepByImplBack defaults + + #[inline] + fn spec_nth_back(&mut self, n: usize) -> Option + where Self: DoubleEndedIterator, + { + if self.advance_back_by(n).is_err() { + return None; + } + self.next_back() + } + + #[inline] + fn spec_try_rfold(&mut self, init: Acc, mut f: F) -> R + where + Self: DoubleEndedIterator, + F: FnMut(Acc, Self::Item) -> R, + R: Try + { + let mut accum = init; + while let Some(x) = self.next_back() { + accum = f(accum, x)?; + } + try { accum } + } + + #[inline] + fn spec_rfold(mut self, init: Acc, mut f: F) -> Acc + where + Self: DoubleEndedIterator, + F: FnMut(Acc, Self::Item) -> Acc + { + let mut accum = init; + while let Some(x) = self.next_back() { + accum = f(accum, x); + } + accum + } + } + )*) +} + +#[cfg(target_pointer_width = "64")] +spec_int_ranges!(u8 u16 u32 u64 usize); +// DoubleEndedIterator requires ExactSizeIterator, which isn't implemented for Range +#[cfg(target_pointer_width = "64")] +spec_int_ranges_r!(u8 u16 u32 usize); + +#[cfg(target_pointer_width = "32")] +spec_int_ranges!(u8 u16 u32 usize); +#[cfg(target_pointer_width = "32")] +spec_int_ranges_r!(u8 u16 u32 usize); + +#[cfg(target_pointer_width = "16")] +spec_int_ranges!(u8 u16 usize); +#[cfg(target_pointer_width = "16")] +spec_int_ranges_r!(u8 u16 usize); diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 0171d8981..462f7170a 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -619,9 +619,10 @@ impl RangeIteratorImpl for ops::Range { #[inline] fn spec_next(&mut self) -> Option { if self.start < self.end { + let old = self.start; // SAFETY: just checked precondition - let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; - Some(mem::replace(&mut self.start, n)) + self.start = unsafe { Step::forward_unchecked(old, 1) }; + Some(old) } else { None } @@ -629,15 +630,15 @@ impl RangeIteratorImpl for ops::Range { #[inline] fn spec_nth(&mut self, n: usize) -> Option { - if let Some(plus_n) = Step::forward_checked(self.start.clone(), n) { + if let Some(plus_n) = Step::forward_checked(self.start, n) { if plus_n < self.end { // SAFETY: just checked precondition - self.start = unsafe { Step::forward_unchecked(plus_n.clone(), 1) }; + self.start = unsafe { Step::forward_unchecked(plus_n, 1) }; return Some(plus_n); } } - self.start = self.end.clone(); + self.start = self.end; None } @@ -655,7 +656,7 @@ impl RangeIteratorImpl for ops::Range { // then steps_between either returns a bound to which we clamp or returns None which // together with the initial inequality implies more than usize::MAX steps. // Otherwise 0 is returned which always safe to use. - self.start = unsafe { Step::forward_unchecked(self.start.clone(), taken) }; + self.start = unsafe { Step::forward_unchecked(self.start, taken) }; NonZeroUsize::new(n - taken).map_or(Ok(()), Err) } @@ -664,8 +665,8 @@ impl RangeIteratorImpl for ops::Range { fn spec_next_back(&mut self) -> Option { if self.start < self.end { // SAFETY: just checked precondition - self.end = unsafe { Step::backward_unchecked(self.end.clone(), 1) }; - Some(self.end.clone()) + self.end = unsafe { Step::backward_unchecked(self.end, 1) }; + Some(self.end) } else { None } @@ -673,15 +674,15 @@ impl RangeIteratorImpl for ops::Range { #[inline] fn spec_nth_back(&mut self, n: usize) -> Option { - if let Some(minus_n) = Step::backward_checked(self.end.clone(), n) { + if let Some(minus_n) = Step::backward_checked(self.end, n) { if minus_n > self.start { // SAFETY: just checked precondition self.end = unsafe { Step::backward_unchecked(minus_n, 1) }; - return Some(self.end.clone()); + return Some(self.end); } } - self.end = self.start.clone(); + self.end = self.start; None } @@ -696,7 +697,7 @@ impl RangeIteratorImpl for ops::Range { let taken = available.min(n); // SAFETY: same as the spec_advance_by() implementation - self.end = unsafe { Step::backward_unchecked(self.end.clone(), taken) }; + self.end = unsafe { Step::backward_unchecked(self.end, taken) }; NonZeroUsize::new(n - taken).map_or(Ok(()), Err) } diff --git a/library/core/src/iter/sources/successors.rs b/library/core/src/iter/sources/successors.rs index 99f058a90..6a6cbe905 100644 --- a/library/core/src/iter/sources/successors.rs +++ b/library/core/src/iter/sources/successors.rs @@ -22,7 +22,7 @@ where Successors { next: first, succ } } -/// An new iterator where each successive item is computed based on the preceding one. +/// A new iterator where each successive item is computed based on the preceding one. /// /// This `struct` is created by the [`iter::successors()`] function. /// See its documentation for more. diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index dabfce144..988352283 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -26,13 +26,13 @@ fn _assert_is_object_safe(_: &dyn Iterator) {} #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented( on( - _Self = "std::ops::RangeTo", + any(_Self = "core::ops::RangeTo", _Self = "std::ops::RangeTo"), label = "if you meant to iterate until a value, add a starting value", note = "`..end` is a `RangeTo`, which cannot be iterated on; you might have meant to have a \ bounded `Range`: `0..end`" ), on( - _Self = "std::ops::RangeToInclusive", + any(_Self = "core::ops::RangeToInclusive", _Self = "std::ops::RangeToInclusive"), label = "if you meant to iterate until a value (including it), add a starting value", note = "`..=end` is a `RangeToInclusive`, which cannot be iterated on; you might have meant \ to have a bounded `RangeInclusive`: `0..=end`" @@ -43,7 +43,7 @@ fn _assert_is_object_safe(_: &dyn Iterator) {} ), on(_Self = "&[]", label = "`{Self}` is not an iterator; try calling `.iter()`"), on( - _Self = "std::vec::Vec", + any(_Self = "alloc::vec::Vec", _Self = "std::vec::Vec"), label = "`{Self}` is not an iterator; try calling `.into_iter()` or `.iter()`" ), on( @@ -51,7 +51,7 @@ fn _assert_is_object_safe(_: &dyn Iterator) {} label = "`{Self}` is not an iterator; try calling `.chars()` or `.bytes()`" ), on( - _Self = "std::string::String", + any(_Self = "alloc::string::String", _Self = "std::string::String"), label = "`{Self}` is not an iterator; try calling `.chars()` or `.bytes()`" ), on( diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs index af0284823..c21a2aac1 100644 --- a/library/core/src/iter/traits/marker.rs +++ b/library/core/src/iter/traits/marker.rs @@ -86,4 +86,4 @@ pub unsafe trait InPlaceIterable: Iterator {} /// for details. Consumers are free to rely on the invariants in unsafe code. #[unstable(feature = "trusted_step", issue = "85731")] #[rustc_specialization_trait] -pub unsafe trait TrustedStep: Step {} +pub unsafe trait TrustedStep: Step + Copy {} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 6c419eb16..05876f5fc 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -113,7 +113,6 @@ #![feature(const_caller_location)] #![feature(const_cell_into_inner)] #![feature(const_char_from_u32_unchecked)] -#![feature(const_cstr_methods)] #![feature(const_discriminant)] #![feature(const_eval_select)] #![feature(const_exact_div)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 4ca650199..1053141cc 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -961,6 +961,8 @@ pub(crate) mod builtin { /// /// A compile time error is never emitted when using this macro regardless /// of whether the environment variable is present or not. + /// To emit a compile error if the environment variable is not present, + /// use the [`env!`] macro instead. /// /// # Examples /// diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 8dab8d1a6..e251015dd 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -140,7 +140,8 @@ unsafe impl Send for &T {} )] #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable #[rustc_specialization_trait] -#[rustc_deny_explicit_impl] +#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl(implement_via_object = false))] +#[cfg_attr(bootstrap, rustc_deny_explicit_impl)] #[rustc_coinductive] pub trait Sized { // Empty. @@ -173,7 +174,8 @@ pub trait Sized { /// [nomicon-coerce]: ../../nomicon/coercions.html #[unstable(feature = "unsize", issue = "18598")] #[lang = "unsize"] -#[rustc_deny_explicit_impl] +#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl(implement_via_object = false))] +#[cfg_attr(bootstrap, rustc_deny_explicit_impl)] pub trait Unsize { // Empty. } @@ -205,6 +207,20 @@ pub trait StructuralPartialEq { // Empty. } +marker_impls! { + #[unstable(feature = "structural_match", issue = "31434")] + StructuralPartialEq for + usize, u8, u16, u32, u64, u128, + isize, i8, i16, i32, i64, i128, + bool, + char, + str /* Technically requires `[u8]: StructuralEq` */, + (), + {T, const N: usize} [T; N], + {T} [T], + {T: ?Sized} &T, +} + /// Required trait for constants used in pattern matches. /// /// Any type that derives `Eq` automatically implements this trait, *regardless* @@ -267,6 +283,7 @@ marker_impls! { bool, char, str /* Technically requires `[u8]: StructuralEq` */, + (), {T, const N: usize} [T; N], {T} [T], {T: ?Sized} &T, @@ -558,59 +575,59 @@ impl Copy for &T {} #[lang = "sync"] #[rustc_on_unimplemented( on( - _Self = "std::cell::OnceCell", + any(_Self = "core::cell:OnceCell", _Self = "std::cell::OnceCell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead" ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU16` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU32` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU64` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicUsize` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI8` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI16` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI64` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicIsize` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead", ), on( - _Self = "std::cell::Cell", + any(_Self = "core::cell::Cell", _Self = "std::cell::Cell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`", ), on( - _Self = "std::cell::RefCell", + any(_Self = "core::cell::RefCell", _Self = "std::cell::RefCell"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead", ), message = "`{Self}` cannot be shared between threads safely", @@ -839,7 +856,8 @@ impl StructuralEq for PhantomData {} reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" )] #[lang = "discriminant_kind"] -#[rustc_deny_explicit_impl] +#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl(implement_via_object = false))] +#[cfg_attr(bootstrap, rustc_deny_explicit_impl)] pub trait DiscriminantKind { /// The type of the discriminant, which must satisfy the trait /// bounds required by `mem::Discriminant`. @@ -944,7 +962,8 @@ marker_impls! { #[unstable(feature = "const_trait_impl", issue = "67792")] #[lang = "destruct"] #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] -#[rustc_deny_explicit_impl] +#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl(implement_via_object = false))] +#[cfg_attr(bootstrap, rustc_deny_explicit_impl)] #[const_trait] pub trait Destruct {} @@ -955,7 +974,8 @@ pub trait Destruct {} #[unstable(feature = "tuple_trait", issue = "none")] #[lang = "tuple_trait"] #[rustc_on_unimplemented(message = "`{Self}` is not a tuple")] -#[rustc_deny_explicit_impl] +#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl(implement_via_object = false))] +#[cfg_attr(bootstrap, rustc_deny_explicit_impl)] pub trait Tuple {} /// A marker for pointer-like types. @@ -971,21 +991,20 @@ pub trait Tuple {} pub trait PointerLike {} /// A marker for types which can be used as types of `const` generic parameters. -#[cfg_attr(not(bootstrap), lang = "const_param_ty")] +#[lang = "const_param_ty"] #[unstable(feature = "adt_const_params", issue = "95174")] #[rustc_on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] -pub trait ConstParamTy: StructuralEq {} +#[allow(multiple_supertrait_upcastable)] +pub trait ConstParamTy: StructuralEq + StructuralPartialEq {} /// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] #[unstable(feature = "adt_const_params", issue = "95174")] -#[cfg(not(bootstrap))] pub macro ConstParamTy($item:item) { /* compiler built-in */ } -// FIXME(generic_const_parameter_types): handle `ty::FnDef`/`ty::Closure` -// FIXME(generic_const_parameter_types): handle `ty::Tuple` +// FIXME(adt_const_params): handle `ty::FnDef`/`ty::Closure` marker_impls! { #[unstable(feature = "adt_const_params", issue = "95174")] ConstParamTy for @@ -999,6 +1018,11 @@ marker_impls! { {T: ?Sized + ConstParamTy} &T, } +// FIXME(adt_const_params): Add to marker_impls call above once not in bootstrap +#[unstable(feature = "adt_const_params", issue = "95174")] +#[cfg(not(bootstrap))] +impl ConstParamTy for () {} + /// A common trait implemented by all function pointers. #[unstable( feature = "fn_ptr_trait", @@ -1006,7 +1030,8 @@ marker_impls! { reason = "internal trait for implementing various traits for all function pointers" )] #[lang = "fn_ptr_trait"] -#[rustc_deny_explicit_impl] +#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl(implement_via_object = false))] +#[cfg_attr(bootstrap, rustc_deny_explicit_impl)] pub trait FnPtr: Copy + Clone { /// Returns the address of the function pointer. #[lang = "fn_ptr_addr"] diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index afbfd6d36..2fff3f0ef 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -968,7 +968,7 @@ pub const fn replace(dest: &mut T, src: T) -> T { /// Integers and other types implementing [`Copy`] are unaffected by `drop`. /// /// ``` -/// # #![cfg_attr(not(bootstrap), allow(dropping_copy_types))] +/// # #![allow(dropping_copy_types)] /// #[derive(Copy, Clone)] /// struct Foo(u8); /// @@ -1316,9 +1316,9 @@ impl SizedTypeProperties for T {} /// /// assert_eq!(mem::offset_of!(NestedA, b.0), 0); /// ``` -#[cfg(not(bootstrap))] #[unstable(feature = "offset_of", issue = "106655")] -#[allow_internal_unstable(builtin_syntax)] +#[allow_internal_unstable(builtin_syntax, hint_must_use)] pub macro offset_of($Container:ty, $($fields:tt).+ $(,)?) { - builtin # offset_of($Container, $($fields).+) + // The `{}` is for better error messages + crate::hint::must_use({builtin # offset_of($Container, $($fields).+)}) } diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 87ae30619..3805d149b 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -1,3 +1,5 @@ +use crate::marker::ConstParamTy; + /// Are values of a type transmutable into values of another type? /// /// This trait is implemented on-the-fly by the compiler for types `Src` and `Self` when the bits of @@ -5,6 +7,8 @@ /// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied. #[unstable(feature = "transmutability", issue = "99571")] #[lang = "transmute_trait"] +#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl(implement_via_object = false))] +#[cfg_attr(bootstrap, rustc_deny_explicit_impl)] pub unsafe trait BikeshedIntrinsicFrom where Src: ?Sized, @@ -33,6 +37,9 @@ pub struct Assume { pub validity: bool, } +#[unstable(feature = "transmutability", issue = "99571")] +impl ConstParamTy for Assume {} + impl Assume { /// Do not assume that *you* have ensured any safety properties are met. #[unstable(feature = "transmutability", issue = "99571")] diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 954d88d54..c51913fa8 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -1770,14 +1770,8 @@ impl fmt::Display for Ipv6Addr { f.write_str("::") } else if self.is_loopback() { f.write_str("::1") - } else if let Some(ipv4) = self.to_ipv4() { - match segments[5] { - // IPv4 Compatible address - 0 => write!(f, "::{}", ipv4), - // IPv4 Mapped address - 0xffff => write!(f, "::ffff:{}", ipv4), - _ => unreachable!(), - } + } else if let Some(ipv4) = self.to_ipv4_mapped() { + write!(f, "::ffff:{}", ipv4) } else { #[derive(Copy, Clone, Default)] struct Span { diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 4a035ad61..d050d21c8 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -403,6 +403,7 @@ impl f32 { /// and the stability of its representation over Rust versions /// and target platforms isn't guaranteed. #[stable(feature = "assoc_int_consts", since = "1.43.0")] + #[rustc_diagnostic_item = "f32_nan"] pub const NAN: f32 = 0.0_f32 / 0.0_f32; /// Infinity (∞). #[stable(feature = "assoc_int_consts", since = "1.43.0")] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 3aafc435f..d9a738191 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -401,6 +401,7 @@ impl f64 { /// This constant isn't guaranteed to equal to any specific NaN bitpattern, /// and the stability of its representation over Rust versions /// and target platforms isn't guaranteed. + #[rustc_diagnostic_item = "f64_nan"] #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NAN: f64 = 0.0_f64 / 0.0_f64; /// Infinity (∞). diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index c9baa09f4..95dcaf5dd 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -3,7 +3,6 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::ascii; -use crate::convert::TryInto; use crate::intrinsics; use crate::mem; use crate::ops::{Add, Mul, Sub}; @@ -278,18 +277,12 @@ macro_rules! widening_impl { macro_rules! conv_rhs_for_unchecked_shift { ($SelfT:ty, $x:expr) => {{ - #[inline] - fn conv(x: u32) -> $SelfT { - // FIXME(const-hack) replace with `.try_into().ok().unwrap_unchecked()`. - // SAFETY: Any legal shift amount must be losslessly representable in the self type. - unsafe { x.try_into().ok().unwrap_unchecked() } - } - #[inline] - const fn const_conv(x: u32) -> $SelfT { - x as _ + // If the `as` cast will truncate, ensure we still tell the backend + // that the pre-truncation value was also small. + if <$SelfT>::BITS < 32 { + intrinsics::assume($x <= (<$SelfT>::MAX as u32)); } - - intrinsics::const_eval_select(($x,), const_conv, conv) + $x as $SelfT }}; } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 7f06e170a..5939dedbd 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -348,7 +348,7 @@ macro_rules! nonzero_unsigned_operations { } /// Adds an unsigned integer to a non-zero value. - #[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")] + #[doc = concat!("Return [`", stringify!($Ty), "::MAX`] on overflow.")] /// /// # Examples /// @@ -579,7 +579,7 @@ macro_rules! nonzero_signed_operations { /// Checked absolute value. /// Checks for overflow and returns [`None`] if - #[doc = concat!("`self == ", stringify!($Int), "::MIN`.")] + #[doc = concat!("`self == ", stringify!($Ty), "::MIN`.")] /// The result cannot be zero. /// /// # Example @@ -800,7 +800,8 @@ macro_rules! nonzero_signed_operations { self.get().is_negative() } - /// Checked negation. Computes `-self`, returning `None` if `self == i32::MIN`. + /// Checked negation. Computes `-self`, + #[doc = concat!("returning `None` if `self == ", stringify!($Ty), "::MIN`.")] /// /// # Example /// @@ -859,8 +860,10 @@ macro_rules! nonzero_signed_operations { ((unsafe { $Ty::new_unchecked(result) }), overflow) } - /// Saturating negation. Computes `-self`, returning `MAX` if - /// `self == i32::MIN` instead of overflowing. + /// Saturating negation. Computes `-self`, + #[doc = concat!("returning [`", stringify!($Ty), "::MAX`]")] + #[doc = concat!("if `self == ", stringify!($Ty), "::MIN`")] + /// instead of overflowing. /// /// # Example /// @@ -993,7 +996,7 @@ macro_rules! nonzero_unsigned_signed_operations { } /// Multiplies two non-zero integers together. - #[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")] + #[doc = concat!("Return [`", stringify!($Ty), "::MAX`] on overflow.")] /// /// # Examples /// @@ -1102,11 +1105,11 @@ macro_rules! nonzero_unsigned_signed_operations { #[doc = sign_dependent_expr!{ $signedness ? if signed { - concat!("Return [`", stringify!($Int), "::MIN`] ", - "or [`", stringify!($Int), "::MAX`] on overflow.") + concat!("Return [`", stringify!($Ty), "::MIN`] ", + "or [`", stringify!($Ty), "::MAX`] on overflow.") } if unsigned { - concat!("Return [`", stringify!($Int), "::MAX`] on overflow.") + concat!("Return [`", stringify!($Ty), "::MAX`] on overflow.") } }] /// diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs index 1f1784ec9..f4649be54 100644 --- a/library/core/src/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -153,7 +153,7 @@ see chapter in The Book " ), on( - _Self = "std::string::String", + any(_Self = "alloc::string::String", _Self = "std::string::String"), note = "you can use `.chars().nth()` or `.bytes().nth()` see chapter in The Book " ), diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index b4f69d0b2..17625dacc 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -226,8 +226,14 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "std::result::Result", - R = "std::option::Option" + any( + _Self = "core::result::Result", + _Self = "std::result::Result", + ), + any( + R = "core::option::Option", + R = "std::option::Option", + ) ), message = "the `?` operator can only be used on `Result`s, not `Option`s, \ in {ItemContext} that returns `Result`", @@ -237,7 +243,10 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "std::result::Result", + any( + _Self = "core::result::Result", + _Self = "std::result::Result", + ) ), // There's a special error message in the trait selection code for // `From` in `?`, so this is not shown for result-in-result errors, @@ -250,8 +259,14 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "std::option::Option", - R = "std::result::Result", + any( + _Self = "core::option::Option", + _Self = "std::option::Option", + ), + any( + R = "core::result::Result", + R = "std::result::Result", + ) ), message = "the `?` operator can only be used on `Option`s, not `Result`s, \ in {ItemContext} that returns `Option`", @@ -261,7 +276,10 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "std::option::Option", + any( + _Self = "core::option::Option", + _Self = "std::option::Option", + ) ), // `Option`-in-`Option` always works, as there's only one possible // residual, so this can also be phrased strongly. @@ -273,8 +291,14 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "std::ops::ControlFlow", - R = "std::ops::ControlFlow", + any( + _Self = "core::ops::ControlFlow", + _Self = "std::ops::ControlFlow", + ), + any( + R = "core::ops::ControlFlow", + R = "std::ops::ControlFlow", + ) ), message = "the `?` operator in {ItemContext} that returns `ControlFlow` \ can only be used on other `ControlFlow`s (with the same Break type)", @@ -285,7 +309,10 @@ pub trait Try: FromResidual { on( all( from_desugaring = "QuestionMark", - _Self = "std::ops::ControlFlow", + any( + _Self = "core::ops::ControlFlow", + _Self = "std::ops::ControlFlow", + ) // `R` is not a `ControlFlow`, as that case was matched previously ), message = "the `?` operator can only be used on `ControlFlow`s \ diff --git a/library/core/src/option.rs b/library/core/src/option.rs index ec1ef3cf4..9b6ff76b2 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1138,7 +1138,7 @@ impl Option { /// Computes a default function result (if none), or /// applies a different function to the contained value (if any). /// - /// # Examples + /// # Basic examples /// /// ``` /// let k = 21; @@ -1149,6 +1149,25 @@ impl Option { /// let x: Option<&str> = None; /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); /// ``` + /// + /// # Handling a Result-based fallback + /// + /// A somewhat common occurrence when dealing with optional values + /// in combination with [`Result`] is the case where one wants to invoke + /// a fallible fallback if the option is not present. This example + /// parses a command line argument (if present), or the contents of a file to + /// an integer. However, unlike accessing the command line argument, reading + /// the file is fallible, so it must be wrapped with `Ok`. + /// + /// ```no_run + /// # fn main() -> Result<(), Box> { + /// let v: u64 = std::env::args() + /// .nth(1) + /// .map_or_else(|| std::fs::read_to_string("/etc/someconfig.conf"), Ok)? + /// .parse()?; + /// # Ok(()) + /// # } + /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn map_or_else(self, default: D, f: F) -> U @@ -1383,6 +1402,7 @@ impl Option { /// let item_2_0 = arr_2d.get(2).and_then(|row| row.get(0)); /// assert_eq!(item_2_0, None); /// ``` + #[doc(alias = "flatmap")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn and_then(self, f: F) -> Option diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 81be3fb22..f0fcdab00 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -166,14 +166,15 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] #[lang = "panic_misaligned_pointer_dereference"] // needed by codegen for panic on misaligned pointer deref +#[rustc_nounwind] // `CheckAlignment` MIR pass requires this function to never unwind fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { if cfg!(feature = "panic_immediate_abort") { super::intrinsics::abort() } - panic!( + panic_nounwind_fmt(format_args!( "misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}" - ) + )) } /// Panic because we cannot unwind out of a function. diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 8266e8990..80289ca08 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -308,7 +308,7 @@ mod prim_never {} /// /// ```no_run /// // Undefined behaviour -/// unsafe { char::from_u32_unchecked(0x110000) }; +/// let _ = unsafe { char::from_u32_unchecked(0x110000) }; /// ``` /// /// USVs are also the exact set of values that may be encoded in UTF-8. Because diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 6e1e862d3..926189a17 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -104,6 +104,7 @@ impl *const T { /// refactored. #[stable(feature = "ptr_const_cast", since = "1.65.0")] #[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")] + #[rustc_diagnostic_item = "ptr_cast_mut"] #[inline(always)] pub const fn cast_mut(self) -> *mut T { self as _ @@ -916,16 +917,8 @@ impl *const T { where T: Sized, { - #[cfg(bootstrap)] // SAFETY: the caller must uphold the safety contract for `offset`. - unsafe { - self.offset(count as isize) - } - #[cfg(not(bootstrap))] - // SAFETY: the caller must uphold the safety contract for `offset`. - unsafe { - intrinsics::offset(self, count) - } + unsafe { intrinsics::offset(self, count) } } /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`). diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 2ea032d4a..daaa44b1d 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -50,7 +50,8 @@ use crate::hash::{Hash, Hasher}; /// /// [`to_raw_parts`]: *const::to_raw_parts #[lang = "pointee_trait"] -#[rustc_deny_explicit_impl] +#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl(implement_via_object = false))] +#[cfg_attr(bootstrap, rustc_deny_explicit_impl)] pub trait Pointee { /// The type for metadata in pointers and references to `Self`. #[lang = "metadata_type"] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index d0cb2f715..acc9ca29d 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -698,6 +698,7 @@ where #[inline(always)] #[must_use] #[unstable(feature = "ptr_from_ref", issue = "106116")] +#[rustc_diagnostic_item = "ptr_from_ref"] pub const fn from_ref(r: &T) -> *const T { r } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 2fe5164c3..c6f438578 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -473,20 +473,10 @@ impl *mut T { where T: Sized, { - #[cfg(bootstrap)] // SAFETY: the caller must uphold the safety contract for `offset`. // The obtained pointer is valid for writes since the caller must // guarantee that it points to the same allocated object as `self`. - unsafe { - intrinsics::offset(self, count) as *mut T - } - #[cfg(not(bootstrap))] - // SAFETY: the caller must uphold the safety contract for `offset`. - // The obtained pointer is valid for writes since the caller must - // guarantee that it points to the same allocated object as `self`. - unsafe { - intrinsics::offset(self, count) - } + unsafe { intrinsics::offset(self, count) } } /// Calculates the offset from a pointer in bytes. @@ -1026,16 +1016,8 @@ impl *mut T { where T: Sized, { - #[cfg(bootstrap)] - // SAFETY: the caller must uphold the safety contract for `offset`. - unsafe { - self.offset(count as isize) - } - #[cfg(not(bootstrap))] // SAFETY: the caller must uphold the safety contract for `offset`. - unsafe { - intrinsics::offset(self, count) - } + unsafe { intrinsics::offset(self, count) } } /// Calculates the offset from a pointer in bytes (convenience for `.byte_offset(count as isize)`). diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index a853f15ed..ff7e91d3e 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -32,6 +32,8 @@ use crate::ptr::NonNull; )] #[doc(hidden)] #[repr(transparent)] +// Lang item used experimentally by Miri to define the semantics of `Unique`. +#[cfg_attr(not(bootstrap), lang = "ptr_unique")] pub struct Unique { pointer: NonNull, // NOTE: this marker has no consequences for variance, but is necessary diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 6ef9f9c95..e1e3bcc05 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -152,7 +152,10 @@ mod private_slice_index { #[rustc_on_unimplemented( on(T = "str", label = "string indices are ranges of `usize`",), on( - all(any(T = "str", T = "&str", T = "std::string::String"), _Self = "{integer}"), + all( + any(T = "str", T = "&str", T = "alloc::string::String", T = "std::string::String"), + _Self = "{integer}" + ), note = "you can use `.chars().nth()` or `.bytes().nth()`\n\ for more information, see chapter 8 in The Book: \ " diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 3462c0e02..96a145e22 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -191,6 +191,39 @@ macro_rules! iterator { self.next_back() } + #[inline] + fn fold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + // this implementation consists of the following optimizations compared to the + // default implementation: + // - do-while loop, as is llvm's preferred loop shape, + // see https://releases.llvm.org/16.0.0/docs/LoopTerminology.html#more-canonical-loops + // - bumps an index instead of a pointer since the latter case inhibits + // some optimizations, see #111603 + // - avoids Option wrapping/matching + if is_empty!(self) { + return init; + } + let mut acc = init; + let mut i = 0; + let len = len!(self); + loop { + // SAFETY: the loop iterates `i in 0..len`, which always is in bounds of + // the slice allocation + acc = f(acc, unsafe { & $( $mut_ )? *self.ptr.add(i).as_ptr() }); + // SAFETY: `i` can't overflow since it'll only reach usize::MAX if the + // slice had that length, in which case we'll break out of the loop + // after the increment + i = unsafe { i.unchecked_add(1) }; + if i == len { + break; + } + } + acc + } + // We override the default implementation, which uses `try_fold`, // because this simple implementation generates less LLVM IR and is // faster to compile. diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index ea0181e35..e2a2428fb 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -851,6 +851,8 @@ impl [T] { /// Swaps two elements in the slice. /// + /// If `a` equals to `b`, it's guaranteed that elements won't change value. + /// /// # Arguments /// /// * a - The index of the first element @@ -2995,7 +2997,7 @@ impl [T] { /// 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 *O*(*n*) on average. The worst-case performance is *O*(*n* log *n*). + /// (i.e. does not allocate), and runs in *O*(*n*) time. /// This function is also known as "kth element" in other libraries. /// /// It returns a triplet of the following from the reordered slice: @@ -3045,9 +3047,8 @@ impl [T] { /// 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 *O*(*n*) on average. - /// The worst-case performance is *O*(*n* log *n*). This function is also known as - /// "kth element" in other libraries. + /// 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. /// /// It returns a triplet of the following from /// the slice reordered according to the provided comparator function: the subslice prior to @@ -3101,8 +3102,7 @@ impl [T] { /// 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 *O*(*n*) on average. - /// The worst-case performance is *O*(*n* log *n*). + /// 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. /// /// It returns a triplet of the following from @@ -3113,8 +3113,9 @@ impl [T] { /// /// # Current implementation /// - /// The current algorithm is based on the quickselect portion of the same quicksort algorithm - /// used for [`sort_unstable`]. + /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also + /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for + /// pivot selection, which guarantees linear runtime for all inputs. /// /// [`sort_unstable`]: slice::sort_unstable /// diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 052fd34d0..48a6eb03b 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -32,7 +32,8 @@ use crate::ptr; /// * The memory referenced by the returned slice must not be mutated for the duration /// of lifetime `'a`, except inside an `UnsafeCell`. /// -/// * The total size `len * mem::size_of::()` of the slice must be no larger than `isize::MAX`. +/// * The total size `len * mem::size_of::()` of the slice must be no larger than `isize::MAX`, +/// and adding that size to `data` must not "wrap around" the address space. /// See the safety documentation of [`pointer::offset`]. /// /// # Caveat @@ -125,7 +126,8 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] /// (not derived from the return value) for the duration of lifetime `'a`. /// Both read and write accesses are forbidden. /// -/// * The total size `len * mem::size_of::()` of the slice must be no larger than `isize::MAX`. +/// * The total size `len * mem::size_of::()` of the slice must be no larger than `isize::MAX`, +/// and adding that size to `data` must not "wrap around" the address space. /// See the safety documentation of [`pointer::offset`]. /// /// [valid]: ptr#safety @@ -179,15 +181,16 @@ pub const fn from_mut(s: &mut T) -> &mut [T] { /// the last element, such that the offset from the end to the start pointer is /// the length of the slice. /// -/// * The range must contain `N` consecutive properly initialized values of type `T`: +/// * The entire memory range of this slice must be contained within a single allocated object! +/// Slices can never span across multiple allocated objects. /// -/// * The entire memory range of this slice must be contained within a single allocated object! -/// Slices can never span across multiple allocated objects. +/// * The range must contain `N` consecutive properly initialized values of type `T`. /// /// * The memory referenced by the returned slice must not be mutated for the duration /// of lifetime `'a`, except inside an `UnsafeCell`. /// -/// * The total length of the range must be no larger than `isize::MAX`. +/// * The total length of the range must be no larger than `isize::MAX`, +/// and adding that size to `data` must not "wrap around" the address space. /// See the safety documentation of [`pointer::offset`]. /// /// Note that a range created from [`slice::as_ptr_range`] fulfills these requirements. @@ -247,16 +250,17 @@ pub const unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] { /// the last element, such that the offset from the end to the start pointer is /// the length of the slice. /// -/// * The range must contain `N` consecutive properly initialized values of type `T`: +/// * The entire memory range of this slice must be contained within a single allocated object! +/// Slices can never span across multiple allocated objects. /// -/// * The entire memory range of this slice must be contained within a single allocated object! -/// Slices can never span across multiple allocated objects. +/// * The range must contain `N` consecutive properly initialized values of type `T`. /// /// * The memory referenced by the returned slice must not be accessed through any other pointer /// (not derived from the return value) for the duration of lifetime `'a`. /// Both read and write accesses are forbidden. /// -/// * The total length of the range must be no larger than `isize::MAX`. +/// * The total length of the range must be no larger than `isize::MAX`, +/// and adding that size to `data` must not "wrap around" the address space. /// See the safety documentation of [`pointer::offset`]. /// /// Note that a range created from [`slice::as_mut_ptr_range`] fulfills these requirements. diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index 5f8748206..0f23cf7ae 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -84,6 +84,7 @@ use super::Utf8Error; #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_from_utf8_shared", since = "1.63.0")] #[rustc_allow_const_fn_unstable(str_internals)] +#[rustc_diagnostic_item = "str_from_utf8"] pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { // FIXME: This should use `?` again, once it's `const` match run_utf8_validation(v) { @@ -127,6 +128,7 @@ pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { /// errors that can be returned. #[stable(feature = "str_mut_extras", since = "1.20.0")] #[rustc_const_unstable(feature = "const_str_from_utf8", issue = "91006")] +#[rustc_diagnostic_item = "str_from_utf8_mut"] pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { // This should use `?` again, once it's `const` match run_utf8_validation(v) { @@ -167,6 +169,7 @@ pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_from_utf8_unchecked", since = "1.55.0")] +#[rustc_diagnostic_item = "str_from_utf8_unchecked"] pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { // SAFETY: the caller must guarantee that the bytes `v` are valid UTF-8. // Also relies on `&str` and `&[u8]` having the same layout. @@ -194,6 +197,7 @@ pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { #[must_use] #[stable(feature = "str_mut_extras", since = "1.20.0")] #[rustc_const_unstable(feature = "const_str_from_utf8_unchecked_mut", issue = "91005")] +#[rustc_diagnostic_item = "str_from_utf8_unchecked_mut"] pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { // SAFETY: the caller must guarantee that the bytes `v` // are valid UTF-8, thus the cast to `*mut str` is safe. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index ef05b25fd..9a93bb729 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -144,8 +144,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let len = "foo".len(); /// assert_eq!(3, len); @@ -165,8 +163,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let s = ""; /// assert!(s.is_empty()); @@ -311,8 +307,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let bytes = "bors".as_bytes(); /// assert_eq!(b"bors", bytes); @@ -387,8 +381,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let s = "Hello"; /// let ptr = s.as_ptr(); @@ -570,8 +562,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let s = "Löwe 老虎 Léopard"; /// @@ -649,8 +639,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let s = "Per Martin-Löf"; /// @@ -691,8 +679,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let mut s = "Per Martin-Löf".to_string(); /// { @@ -840,8 +826,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let mut bytes = "bors".bytes(); /// @@ -1020,8 +1004,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let text = "Zażółć gęślą jaźń"; /// @@ -1050,8 +1032,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let bananas = "bananas"; /// @@ -1077,8 +1057,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let bananas = "bananas"; /// @@ -1103,8 +1081,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let bananas = "bananas"; /// @@ -1463,8 +1439,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let v: Vec<&str> = "A.B.".split_terminator('.').collect(); /// assert_eq!(v, ["A", "B"]); @@ -1696,8 +1670,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let v: Vec<&str> = "abcXXXabcYYYabc".matches("abc").collect(); /// assert_eq!(v, ["abc", "abc", "abc"]); @@ -1732,8 +1704,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let v: Vec<&str> = "abcXXXabcYYYabc".rmatches("abc").collect(); /// assert_eq!(v, ["abc", "abc", "abc"]); @@ -1775,8 +1745,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect(); /// assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]); @@ -1817,8 +1785,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let v: Vec<_> = "abcXXXabcYYYabc".rmatch_indices("abc").collect(); /// assert_eq!(v, [(12, "abc"), (6, "abc"), (0, "abc")]); @@ -1845,8 +1811,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// let s = "\n Hello\tworld\t\n"; /// @@ -2085,8 +2049,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11"); /// assert_eq!("123foo1bar123".trim_start_matches(char::is_numeric), "foo1bar123"); @@ -2232,8 +2194,6 @@ impl str { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11"); /// assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123"); diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 7043ab5ff..b63fd5c90 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -2,6 +2,7 @@ use crate::fmt; use crate::marker::{PhantomData, Unpin}; +use crate::ptr; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`] /// which provides customized wakeup behavior. @@ -322,6 +323,45 @@ impl Waker { Waker { waker } } + /// Creates a new `Waker` that does nothing when `wake` is called. + /// + /// This is mostly useful for writing tests that need a [`Context`] to poll + /// some futures, but are not expecting those futures to wake the waker or + /// do not need to do anything specific if it happens. + /// + /// # Examples + /// + /// ``` + /// #![feature(noop_waker)] + /// + /// use std::future::Future; + /// use std::task; + /// + /// let waker = task::Waker::noop(); + /// let mut cx = task::Context::from_waker(&waker); + /// + /// let mut future = Box::pin(async { 10 }); + /// assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10)); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "noop_waker", issue = "98286")] + pub const fn noop() -> Waker { + const VTABLE: RawWakerVTable = RawWakerVTable::new( + // Cloning just returns a new no-op raw waker + |_| RAW, + // `wake` does nothing + |_| {}, + // `wake_by_ref` does nothing + |_| {}, + // Dropping does nothing as we don't allocate anything + |_| {}, + ); + const RAW: RawWaker = RawWaker::new(ptr::null(), &VTABLE); + + Waker { waker: RAW } + } + /// Get a reference to the underlying [`RawWaker`]. #[inline] #[must_use] diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index a1388dfee..ac8d04a82 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,6 +1,9 @@ // See src/libstd/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; +#[cfg(not(bootstrap))] +use crate::marker::ConstParamTy; +use crate::marker::{StructuralEq, StructuralPartialEq}; // Recursive macro for implementing n-ary tuple functions and operations // @@ -45,6 +48,28 @@ macro_rules! tuple_impls { {} } + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "structural_match", issue = "31434")] + #[cfg(not(bootstrap))] + impl<$($T: ConstParamTy),+> ConstParamTy for ($($T,)+) + {} + } + + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "structural_match", issue = "31434")] + impl<$($T),+> StructuralPartialEq for ($($T,)+) + {} + } + + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "structural_match", issue = "31434")] + impl<$($T),+> StructuralEq for ($($T,)+) + {} + } + maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/unicode/printable.py b/library/core/src/unicode/printable.py index 7c37f5f09..4d39ace06 100755 --- a/library/core/src/unicode/printable.py +++ b/library/core/src/unicode/printable.py @@ -119,7 +119,7 @@ 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(l) for l in lowers[i:i+8]))) + print(" {}".format(" ".join("{:#04x},".format(x) for x in lowers[i:i+8]))) print("];") def print_normal(normal, normalname): diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index 0869644c0..982d7853f 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -257,14 +257,8 @@ fn iterator_drops() { assert_eq!(i.get(), 5); } -// This test does not work on targets without panic=unwind support. -// To work around this problem, test is marked is should_panic, so it will -// be automagically skipped on unsuitable targets, such as -// wasm32-unknown-unknown. -// -// It means that we use panic for indicating success. -#[test] -#[should_panic(expected = "test succeeded")] +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn array_default_impl_avoids_leaks_on_panic() { use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; static COUNTER: AtomicUsize = AtomicUsize::new(0); @@ -296,7 +290,6 @@ fn array_default_impl_avoids_leaks_on_panic() { assert_eq!(*panic_msg, "bomb limit exceeded"); // check that all bombs are successfully dropped assert_eq!(COUNTER.load(Relaxed), 0); - panic!("test succeeded") } #[test] @@ -317,9 +310,8 @@ fn array_map() { assert_eq!(b, [1, 2, 3]); } -// See note on above test for why `should_panic` is used. #[test] -#[should_panic(expected = "test succeeded")] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn array_map_drop_safety() { static DROPPED: AtomicUsize = AtomicUsize::new(0); struct DropCounter; @@ -341,7 +333,6 @@ fn array_map_drop_safety() { }); assert!(success.is_err()); assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create); - panic!("test succeeded") } #[test] diff --git a/library/core/tests/clone.rs b/library/core/tests/clone.rs index aafe5ced2..64193e115 100644 --- a/library/core/tests/clone.rs +++ b/library/core/tests/clone.rs @@ -1,5 +1,5 @@ #[test] -#[cfg_attr(not(bootstrap), allow(suspicious_double_ref_op))] +#[allow(suspicious_double_ref_op)] fn test_borrowed_clone() { let x = 5; let y: &i32 = &x; diff --git a/library/core/tests/future.rs b/library/core/tests/future.rs index 74b6f74e4..db417256d 100644 --- a/library/core/tests/future.rs +++ b/library/core/tests/future.rs @@ -30,7 +30,6 @@ fn poll_n(val: usize, num: usize) -> PollN { } #[test] -#[cfg_attr(miri, ignore)] // self-referential generators do not work with Miri's aliasing checks fn test_join() { block_on(async move { let x = join!(async { 0 }).await; diff --git a/library/core/tests/iter/adapters/step_by.rs b/library/core/tests/iter/adapters/step_by.rs index 94f2fa8c2..4c5b1dd9a 100644 --- a/library/core/tests/iter/adapters/step_by.rs +++ b/library/core/tests/iter/adapters/step_by.rs @@ -244,3 +244,58 @@ fn test_step_by_skip() { assert_eq!((0..=50).step_by(10).nth(3), Some(30)); assert_eq!((200..=255u8).step_by(10).nth(3), Some(230)); } + + +struct DeOpt(I); + +impl Iterator for DeOpt { + type Item = I::Item; + + fn next(&mut self) -> core::option::Option { + self.0.next() + } +} + +impl DoubleEndedIterator for DeOpt { + fn next_back(&mut self) -> core::option::Option { + self.0.next_back() + } +} + +#[test] +fn test_step_by_fold_range_specialization() { + macro_rules! t { + ($range:expr, $var: ident, $body:tt) => { + { + // run the same tests for the non-optimized version + let mut $var = DeOpt($range); + $body + } + { + let mut $var = $range; + $body + } + } + } + + t!((1usize..5).step_by(1), r, { + assert_eq!(r.next_back(), Some(4)); + assert_eq!(r.sum::(), 6); + }); + + t!((0usize..4).step_by(2), r, { + assert_eq!(r.next(), Some(0)); + assert_eq!(r.sum::(), 2); + }); + + + t!((0usize..5).step_by(2), r, { + assert_eq!(r.next(), Some(0)); + assert_eq!(r.sum::(), 6); + }); + + t!((usize::MAX - 6 .. usize::MAX).step_by(5), r, { + assert_eq!(r.next(), Some(usize::MAX - 6)); + assert_eq!(r.sum::(), usize::MAX - 1); + }); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 3933e3289..3e6d31fcd 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -109,7 +109,7 @@ #![feature(utf8_chunks)] #![feature(is_ascii_octdigit)] #![feature(get_many_mut)] -#![cfg_attr(not(bootstrap), feature(offset_of))] +#![feature(offset_of)] #![deny(unsafe_op_in_unsafe_fn)] #![deny(fuzzy_provenance_casts)] diff --git a/library/core/tests/manually_drop.rs b/library/core/tests/manually_drop.rs index 9eac27973..22d72d219 100644 --- a/library/core/tests/manually_drop.rs +++ b/library/core/tests/manually_drop.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(undropped_manually_drops))] + use core::mem::ManuallyDrop; #[test] diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index aee9c89b5..5c2e18745 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -366,7 +366,6 @@ fn const_maybe_uninit() { } #[test] -#[cfg(not(bootstrap))] fn offset_of() { #[repr(C)] struct Foo { @@ -391,7 +390,7 @@ fn offset_of() { struct Generic { x: u8, y: u32, - z: T + z: T, } trait Trait {} @@ -409,7 +408,6 @@ fn offset_of() { } #[test] -#[cfg(not(bootstrap))] fn offset_of_union() { #[repr(C)] union Foo { @@ -429,7 +427,6 @@ fn offset_of_union() { } #[test] -#[cfg(not(bootstrap))] fn offset_of_dst() { #[repr(C)] struct Alpha { @@ -469,7 +466,6 @@ fn offset_of_dst() { } #[test] -#[cfg(not(bootstrap))] fn offset_of_packed() { #[repr(C, packed)] struct Foo { @@ -482,7 +478,6 @@ fn offset_of_packed() { } #[test] -#[cfg(not(bootstrap))] fn offset_of_projection() { #[repr(C)] struct Foo { @@ -503,7 +498,6 @@ fn offset_of_projection() { } #[test] -#[cfg(not(bootstrap))] fn offset_of_alias() { #[repr(C)] struct Foo { @@ -518,7 +512,6 @@ fn offset_of_alias() { } #[test] -#[cfg(not(bootstrap))] fn const_offset_of() { #[repr(C)] struct Foo { @@ -534,7 +527,6 @@ fn const_offset_of() { } #[test] -#[cfg(not(bootstrap))] fn offset_of_without_const_promotion() { #[repr(C)] struct Foo { @@ -555,7 +547,6 @@ fn offset_of_without_const_promotion() { } #[test] -#[cfg(not(bootstrap))] fn offset_of_addr() { #[repr(C)] struct Foo { diff --git a/library/core/tests/net/ip_addr.rs b/library/core/tests/net/ip_addr.rs index 5a6ac08c0..7530fc084 100644 --- a/library/core/tests/net/ip_addr.rs +++ b/library/core/tests/net/ip_addr.rs @@ -139,7 +139,7 @@ fn ipv6_addr_to_string() { // ipv4-compatible address let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280); - assert_eq!(a1.to_string(), "::192.0.2.128"); + assert_eq!(a1.to_string(), "::c000:280"); // v6 address with no zero segments assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f"); @@ -316,7 +316,7 @@ fn ip_properties() { check!("::", unspec); check!("::1", loopback); - check!("::0.0.0.2", global); + check!("::2", global); check!("1::", global); check!("fc00::"); check!("fdff:ffff::"); @@ -607,7 +607,7 @@ fn ipv6_properties() { check!("::1", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], loopback); - check!("::0.0.0.2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], global | unicast_global); + check!("::2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], global | unicast_global); check!("1::", &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global); diff --git a/library/core/tests/net/socket_addr.rs b/library/core/tests/net/socket_addr.rs index 68c7cd94d..35a69cead 100644 --- a/library/core/tests/net/socket_addr.rs +++ b/library/core/tests/net/socket_addr.rs @@ -34,7 +34,7 @@ fn ipv6_socket_addr_to_string() { // IPv4-compatible address. assert_eq!( SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(), - "[::192.0.2.128]:8080" + "[::c000:280]:8080" ); // IPv6 address with no zero segments. diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index c02cd99cc..ee885adfe 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1001,7 +1001,7 @@ fn nonnull_tagged_pointer_with_provenance() { assert_eq!(p.tag(), 3); assert_eq!(unsafe { *p.pointer().as_ptr() }, 10); - unsafe { Box::from_raw(p.pointer().as_ptr()) }; + unsafe { drop(Box::from_raw(p.pointer().as_ptr())) }; /// A non-null pointer type which carries several bits of metadata and maintains provenance. #[repr(transparent)] diff --git a/library/portable-simd/.github/workflows/ci.yml b/library/portable-simd/.github/workflows/ci.yml index acd47a3da..1ff377fce 100644 --- a/library/portable-simd/.github/workflows/ci.yml +++ b/library/portable-simd/.github/workflows/ci.yml @@ -38,8 +38,9 @@ jobs: - i586-unknown-linux-gnu - aarch64-unknown-linux-gnu - armv7-unknown-linux-gnueabihf - - mips-unknown-linux-gnu - - mips64-unknown-linux-gnuabi64 + # non-nightly since https://github.com/rust-lang/rust/pull/113274 + # - mips-unknown-linux-gnu + # - mips64-unknown-linux-gnuabi64 - powerpc-unknown-linux-gnu - powerpc64-unknown-linux-gnu - riscv64gc-unknown-linux-gnu @@ -191,8 +192,8 @@ jobs: # Note: The issue above means neither of these mips targets will use # MSA (mips simd) but MIPS uses a nonstandard binary representation # for NaNs which makes it worth testing on despite that. - - mips-unknown-linux-gnu - - mips64-unknown-linux-gnuabi64 + # - mips-unknown-linux-gnu + # - mips64-unknown-linux-gnuabi64 - riscv64gc-unknown-linux-gnu # TODO this test works, but it appears to time out # - powerpc-unknown-linux-gnu diff --git a/library/portable-simd/Cargo.toml b/library/portable-simd/Cargo.toml index 9802386e4..d1732aaec 100644 --- a/library/portable-simd/Cargo.toml +++ b/library/portable-simd/Cargo.toml @@ -1,5 +1,5 @@ [workspace] - +resolver = "1" members = [ "crates/core_simd", "crates/std_float", diff --git a/library/portable-simd/crates/core_simd/src/cast.rs b/library/portable-simd/crates/core_simd/src/cast.rs index 65a3f845f..1c3592f80 100644 --- a/library/portable-simd/crates/core_simd/src/cast.rs +++ b/library/portable-simd/crates/core_simd/src/cast.rs @@ -1,55 +1,51 @@ use crate::simd::SimdElement; +mod sealed { + /// Cast vector elements to other types. + /// + /// # Safety + /// Implementing this trait asserts that the type is a valid vector element for the `simd_cast` + /// or `simd_as` intrinsics. + pub unsafe trait Sealed {} +} +use sealed::Sealed; + /// Supporting trait for `Simd::cast`. Typically doesn't need to be used directly. -/// -/// # Safety -/// Implementing this trait asserts that the type is a valid vector element for the `simd_cast` or -/// `simd_as` intrinsics. -pub unsafe trait SimdCast: SimdElement {} +pub trait SimdCast: Sealed + SimdElement {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for i8 {} +unsafe impl Sealed for i8 {} +impl SimdCast for i8 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for i16 {} +unsafe impl Sealed for i16 {} +impl SimdCast for i16 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for i32 {} +unsafe impl Sealed for i32 {} +impl SimdCast for i32 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for i64 {} +unsafe impl Sealed for i64 {} +impl SimdCast for i64 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for isize {} +unsafe impl Sealed for isize {} +impl SimdCast for isize {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for u8 {} +unsafe impl Sealed for u8 {} +impl SimdCast for u8 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for u16 {} +unsafe impl Sealed for u16 {} +impl SimdCast for u16 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for u32 {} +unsafe impl Sealed for u32 {} +impl SimdCast for u32 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for u64 {} +unsafe impl Sealed for u64 {} +impl SimdCast for u64 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for usize {} +unsafe impl Sealed for usize {} +impl SimdCast for usize {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for f32 {} +unsafe impl Sealed for f32 {} +impl SimdCast for f32 {} // Safety: primitive number types can be cast to other primitive number types -unsafe impl SimdCast for f64 {} - -/// Supporting trait for `Simd::cast_ptr`. Typically doesn't need to be used directly. -/// -/// # Safety -/// Implementing this trait asserts that the type is a valid vector element for the `simd_cast_ptr` -/// intrinsic. -pub unsafe trait SimdCastPtr {} - -// Safety: pointers can be cast to other pointer types -unsafe impl SimdCastPtr for *const U -where - U: core::ptr::Pointee, - T: core::ptr::Pointee, -{ -} -// Safety: pointers can be cast to other pointer types -unsafe impl SimdCastPtr for *mut U -where - U: core::ptr::Pointee, - T: core::ptr::Pointee, -{ -} +unsafe impl Sealed for f64 {} +impl SimdCast for f64 {} diff --git a/library/portable-simd/crates/core_simd/src/elements/const_ptr.rs b/library/portable-simd/crates/core_simd/src/elements/const_ptr.rs index 0ef9802b5..f215f9a61 100644 --- a/library/portable-simd/crates/core_simd/src/elements/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/elements/const_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount}; +use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SimdUint, SupportedLaneCount}; /// Operations on SIMD vectors of constant pointers. pub trait SimdConstPtr: Copy + Sealed { @@ -9,6 +9,9 @@ pub trait SimdConstPtr: Copy + Sealed { /// Vector of `isize` with the same number of lanes. type Isize; + /// Vector of const pointers with the same number of lanes. + type CastPtr; + /// Vector of mutable pointers to the same type. type MutPtr; @@ -18,6 +21,11 @@ pub trait SimdConstPtr: Copy + Sealed { /// Returns `true` for each lane that is null. fn is_null(self) -> Self::Mask; + /// Casts to a pointer of another type. + /// + /// Equivalent to calling [`pointer::cast`] on each lane. + fn cast(self) -> Self::CastPtr; + /// Changes constness without changing the type. /// /// Equivalent to calling [`pointer::cast_mut`] on each lane. @@ -78,6 +86,7 @@ where { type Usize = Simd; type Isize = Simd; + type CastPtr = Simd<*const U, LANES>; type MutPtr = Simd<*mut T, LANES>; type Mask = Mask; @@ -86,9 +95,22 @@ where Simd::splat(core::ptr::null()).simd_eq(self) } + #[inline] + fn cast(self) -> Self::CastPtr { + // SimdElement currently requires zero-sized metadata, so this should never fail. + // If this ever changes, `simd_cast_ptr` should produce a post-mono error. + use core::{mem::size_of, ptr::Pointee}; + assert_eq!(size_of::<::Metadata>(), 0); + assert_eq!(size_of::<::Metadata>(), 0); + + // Safety: pointers can be cast + unsafe { intrinsics::simd_cast_ptr(self) } + } + #[inline] fn cast_mut(self) -> Self::MutPtr { - self.cast_ptr() + // Safety: pointers can be cast + unsafe { intrinsics::simd_cast_ptr(self) } } #[inline] @@ -106,9 +128,9 @@ where // In the mean-time, this operation is defined to be "as if" it was // a wrapping_offset, so we can emulate it as such. This should properly // restore pointer provenance even under today's compiler. - self.cast_ptr::<*const u8>() + self.cast::() .wrapping_offset(addr.cast::() - self.addr().cast::()) - .cast_ptr() + .cast() } #[inline] diff --git a/library/portable-simd/crates/core_simd/src/elements/float.rs b/library/portable-simd/crates/core_simd/src/elements/float.rs index d60223270..501c1c5dd 100644 --- a/library/portable-simd/crates/core_simd/src/elements/float.rs +++ b/library/portable-simd/crates/core_simd/src/elements/float.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - intrinsics, LaneCount, Mask, Simd, SimdElement, SimdPartialEq, SimdPartialOrd, + intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialEq, SimdPartialOrd, SupportedLaneCount, }; @@ -15,6 +15,53 @@ pub trait SimdFloat: Copy + Sealed { /// Bit representation of this SIMD vector type. type Bits; + /// A SIMD vector with a different element type. + type Cast; + + /// Performs elementwise conversion of this vector's elements to another SIMD-valid type. + /// + /// This follows the semantics of Rust's `as` conversion for floats (truncating or saturating + /// at the limits) for each element. + /// + /// # Example + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{SimdFloat, SimdInt, Simd}; + /// let floats: Simd = Simd::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]); + /// let ints = floats.cast::(); + /// assert_eq!(ints, Simd::from_array([1, -4, i32::MAX, 0])); + /// + /// // Formally equivalent, but `Simd::cast` can optimize better. + /// assert_eq!(ints, Simd::from_array(floats.to_array().map(|x| x as i32))); + /// + /// // The float conversion does not round-trip. + /// let floats_again = ints.cast(); + /// assert_ne!(floats, floats_again); + /// assert_eq!(floats_again, Simd::from_array([1.0, -4.0, 2147483647.0, 0.0])); + /// ``` + #[must_use] + fn cast(self) -> Self::Cast; + + /// Rounds toward zero and converts to the same-width integer type, assuming that + /// the value is finite and fits in that type. + /// + /// # Safety + /// The value must: + /// + /// * Not be NaN + /// * Not be infinite + /// * Be representable in the return type, after truncating off its fractional part + /// + /// If these requirements are infeasible or costly, consider using the safe function [cast], + /// which saturates on conversion. + /// + /// [cast]: Simd::cast + unsafe fn to_int_unchecked(self) -> Self::Cast + where + Self::Scalar: core::convert::FloatToInt; + /// Raw transmutation to an unsigned integer vector type with the /// same size and number of lanes. #[must_use = "method returns a new vector and does not mutate the original value"] @@ -206,6 +253,24 @@ macro_rules! impl_trait { type Mask = Mask<<$mask_ty as SimdElement>::Mask, LANES>; type Scalar = $ty; type Bits = Simd<$bits_ty, LANES>; + type Cast = Simd; + + #[inline] + fn cast(self) -> Self::Cast + { + // Safety: supported types are guaranteed by SimdCast + unsafe { intrinsics::simd_as(self) } + } + + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + unsafe fn to_int_unchecked(self) -> Self::Cast + where + Self::Scalar: core::convert::FloatToInt, + { + // Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants + unsafe { intrinsics::simd_cast(self) } + } #[inline] fn to_bits(self) -> Simd<$bits_ty, LANES> { diff --git a/library/portable-simd/crates/core_simd/src/elements/int.rs b/library/portable-simd/crates/core_simd/src/elements/int.rs index 9b8c37ed4..6db89ff9a 100644 --- a/library/portable-simd/crates/core_simd/src/elements/int.rs +++ b/library/portable-simd/crates/core_simd/src/elements/int.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - intrinsics, LaneCount, Mask, Simd, SimdElement, SimdPartialOrd, SupportedLaneCount, + intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialOrd, SupportedLaneCount, }; /// Operations on SIMD vectors of signed integers. @@ -11,6 +11,16 @@ pub trait SimdInt: Copy + Sealed { /// Scalar type contained by this SIMD vector type. type Scalar; + /// A SIMD vector with a different element type. + type Cast; + + /// Performs elementwise conversion of this vector's elements to another SIMD-valid type. + /// + /// This follows the semantics of Rust's `as` conversion for casting integers (wrapping to + /// other integer types, and saturating to float types). + #[must_use] + fn cast(self) -> Self::Cast; + /// Lanewise saturating add. /// /// # Examples @@ -198,6 +208,13 @@ macro_rules! impl_trait { { type Mask = Mask<<$ty as SimdElement>::Mask, LANES>; type Scalar = $ty; + type Cast = Simd; + + #[inline] + fn cast(self) -> Self::Cast { + // Safety: supported types are guaranteed by SimdCast + unsafe { intrinsics::simd_as(self) } + } #[inline] fn saturating_add(self, second: Self) -> Self { diff --git a/library/portable-simd/crates/core_simd/src/elements/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/elements/mut_ptr.rs index d87986b4a..4bdc6a14c 100644 --- a/library/portable-simd/crates/core_simd/src/elements/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/elements/mut_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount}; +use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SimdUint, SupportedLaneCount}; /// Operations on SIMD vectors of mutable pointers. pub trait SimdMutPtr: Copy + Sealed { @@ -9,6 +9,9 @@ pub trait SimdMutPtr: Copy + Sealed { /// Vector of `isize` with the same number of lanes. type Isize; + /// Vector of const pointers with the same number of lanes. + type CastPtr; + /// Vector of constant pointers to the same type. type ConstPtr; @@ -18,6 +21,11 @@ pub trait SimdMutPtr: Copy + Sealed { /// Returns `true` for each lane that is null. fn is_null(self) -> Self::Mask; + /// Casts to a pointer of another type. + /// + /// Equivalent to calling [`pointer::cast`] on each lane. + fn cast(self) -> Self::CastPtr; + /// Changes constness without changing the type. /// /// Equivalent to calling [`pointer::cast_const`] on each lane. @@ -73,6 +81,7 @@ where { type Usize = Simd; type Isize = Simd; + type CastPtr = Simd<*mut U, LANES>; type ConstPtr = Simd<*const T, LANES>; type Mask = Mask; @@ -81,9 +90,22 @@ where Simd::splat(core::ptr::null_mut()).simd_eq(self) } + #[inline] + fn cast(self) -> Self::CastPtr { + // SimdElement currently requires zero-sized metadata, so this should never fail. + // If this ever changes, `simd_cast_ptr` should produce a post-mono error. + use core::{mem::size_of, ptr::Pointee}; + assert_eq!(size_of::<::Metadata>(), 0); + assert_eq!(size_of::<::Metadata>(), 0); + + // Safety: pointers can be cast + unsafe { intrinsics::simd_cast_ptr(self) } + } + #[inline] fn cast_const(self) -> Self::ConstPtr { - self.cast_ptr() + // Safety: pointers can be cast + unsafe { intrinsics::simd_cast_ptr(self) } } #[inline] @@ -101,9 +123,9 @@ where // In the mean-time, this operation is defined to be "as if" it was // a wrapping_offset, so we can emulate it as such. This should properly // restore pointer provenance even under today's compiler. - self.cast_ptr::<*mut u8>() + self.cast::() .wrapping_offset(addr.cast::() - self.addr().cast::()) - .cast_ptr() + .cast() } #[inline] diff --git a/library/portable-simd/crates/core_simd/src/elements/uint.rs b/library/portable-simd/crates/core_simd/src/elements/uint.rs index 21e7e76eb..3926c395e 100644 --- a/library/portable-simd/crates/core_simd/src/elements/uint.rs +++ b/library/portable-simd/crates/core_simd/src/elements/uint.rs @@ -1,11 +1,21 @@ use super::sealed::Sealed; -use crate::simd::{intrinsics, LaneCount, Simd, SupportedLaneCount}; +use crate::simd::{intrinsics, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { /// Scalar type contained by this SIMD vector type. type Scalar; + /// A SIMD vector with a different element type. + type Cast; + + /// Performs elementwise conversion of this vector's elements to another SIMD-valid type. + /// + /// This follows the semantics of Rust's `as` conversion for casting integers (wrapping to + /// other integer types, and saturating to float types). + #[must_use] + fn cast(self) -> Self::Cast; + /// Lanewise saturating add. /// /// # Examples @@ -77,6 +87,13 @@ macro_rules! impl_trait { LaneCount: SupportedLaneCount, { type Scalar = $ty; + type Cast = Simd; + + #[inline] + fn cast(self) -> Self::Cast { + // Safety: supported types are guaranteed by SimdCast + unsafe { intrinsics::simd_as(self) } + } #[inline] fn saturating_add(self, second: Self) -> Self { diff --git a/library/portable-simd/crates/core_simd/src/iter.rs b/library/portable-simd/crates/core_simd/src/iter.rs index 3275b4db8..328c995b8 100644 --- a/library/portable-simd/crates/core_simd/src/iter.rs +++ b/library/portable-simd/crates/core_simd/src/iter.rs @@ -10,6 +10,7 @@ macro_rules! impl_traits { where LaneCount: SupportedLaneCount, { + #[inline] fn sum>(iter: I) -> Self { iter.fold(Simd::splat(0 as $type), Add::add) } @@ -19,6 +20,7 @@ macro_rules! impl_traits { where LaneCount: SupportedLaneCount, { + #[inline] fn product>(iter: I) -> Self { iter.fold(Simd::splat(1 as $type), Mul::mul) } @@ -28,6 +30,7 @@ macro_rules! impl_traits { where LaneCount: SupportedLaneCount, { + #[inline] fn sum>(iter: I) -> Self { iter.fold(Simd::splat(0 as $type), Add::add) } @@ -37,6 +40,7 @@ macro_rules! impl_traits { where LaneCount: SupportedLaneCount, { + #[inline] fn product>(iter: I) -> Self { iter.fold(Simd::splat(1 as $type), Mul::mul) } diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index e5307de21..fde406bda 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -16,7 +16,7 @@ )] #![cfg_attr(feature = "generic_const_exprs", feature(generic_const_exprs))] #![cfg_attr(feature = "generic_const_exprs", allow(incomplete_features))] -#![warn(missing_docs)] +#![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index e0f3c7bee..fea687bdc 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -179,6 +179,7 @@ where /// Panics if any lane is not 0 or -1. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] + #[track_caller] pub fn from_int(value: Simd) -> Self { assert!(T::valid(value), "all values must be either 0 or -1",); // Safety: the validity has been checked @@ -217,6 +218,7 @@ where /// Panics if `lane` is greater than or equal to the number of lanes in the vector. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] + #[track_caller] pub fn test(&self, lane: usize) -> bool { assert!(lane < LANES, "lane index out of range"); // Safety: the lane index has been checked @@ -240,6 +242,7 @@ where /// # Panics /// Panics if `lane` is greater than or equal to the number of lanes in the vector. #[inline] + #[track_caller] pub fn set(&mut self, lane: usize, value: bool) { assert!(lane < LANES, "lane index out of range"); // Safety: the lane index has been checked @@ -327,6 +330,7 @@ where T: MaskElement + fmt::Debug, LaneCount: SupportedLaneCount, { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list() .entries((0..LANES).map(|lane| self.test(lane))) diff --git a/library/portable-simd/crates/core_simd/src/mod.rs b/library/portable-simd/crates/core_simd/src/mod.rs index 35c659b7a..f9891a3b7 100644 --- a/library/portable-simd/crates/core_simd/src/mod.rs +++ b/library/portable-simd/crates/core_simd/src/mod.rs @@ -23,6 +23,8 @@ mod vendor; #[doc = include_str!("core_simd_docs.md")] pub mod simd { + pub mod prelude; + pub(crate) use crate::core_simd::intrinsics; pub use crate::core_simd::alias::*; diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index fc1e0bc42..b007456cf 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -15,6 +15,7 @@ where I: core::slice::SliceIndex<[T]>, { type Output = I::Output; + #[inline] fn index(&self, index: I) -> &Self::Output { &self.as_array()[index] } @@ -26,6 +27,7 @@ where LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { + #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { &mut self.as_mut_array()[index] } @@ -118,10 +120,14 @@ macro_rules! for_base_types { #[inline] #[must_use = "operator returns a new vector without mutating the inputs"] + // TODO: only useful for int Div::div, but we hope that this + // will essentially always always get inlined anyway. + #[track_caller] fn $call(self, rhs: Self) -> Self::Output { $macro_impl!(self, rhs, $inner, $scalar) } - })* + } + )* } } diff --git a/library/portable-simd/crates/core_simd/src/ord.rs b/library/portable-simd/crates/core_simd/src/ord.rs index 1ae9cd061..b2455190e 100644 --- a/library/portable-simd/crates/core_simd/src/ord.rs +++ b/library/portable-simd/crates/core_simd/src/ord.rs @@ -94,6 +94,7 @@ macro_rules! impl_integer { } #[inline] + #[track_caller] fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), @@ -200,6 +201,7 @@ macro_rules! impl_mask { } #[inline] + #[track_caller] fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), @@ -254,6 +256,7 @@ where } #[inline] + #[track_caller] fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), @@ -303,6 +306,7 @@ where } #[inline] + #[track_caller] fn simd_clamp(self, min: Self, max: Self) -> Self { assert!( min.simd_le(max).all(), diff --git a/library/portable-simd/crates/core_simd/src/simd/prelude.rs b/library/portable-simd/crates/core_simd/src/simd/prelude.rs new file mode 100644 index 000000000..e8fdc932d --- /dev/null +++ b/library/portable-simd/crates/core_simd/src/simd/prelude.rs @@ -0,0 +1,80 @@ +//! The portable SIMD prelude. +//! +//! Includes important traits and types to be imported with a glob: +//! ```ignore +//! use std::simd::prelude::*; +//! ``` + +#[doc(no_inline)] +pub use super::{ + simd_swizzle, Mask, Simd, SimdConstPtr, SimdFloat, SimdInt, SimdMutPtr, SimdOrd, SimdPartialEq, + SimdPartialOrd, SimdUint, +}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{f32x1, f32x2, f32x4, f32x8, f32x16, f32x32, f32x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{f64x1, f64x2, f64x4, f64x8, f64x16, f64x32, f64x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{i8x1, i8x2, i8x4, i8x8, i8x16, i8x32, i8x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{i16x1, i16x2, i16x4, i16x8, i16x16, i16x32, i16x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{i32x1, i32x2, i32x4, i32x8, i32x16, i32x32, i32x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{i64x1, i64x2, i64x4, i64x8, i64x16, i64x32, i64x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{isizex1, isizex2, isizex4, isizex8, isizex16, isizex32, isizex64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{u8x1, u8x2, u8x4, u8x8, u8x16, u8x32, u8x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{u16x1, u16x2, u16x4, u16x8, u16x16, u16x32, u16x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{u32x1, u32x2, u32x4, u32x8, u32x16, u32x32, u32x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{u64x1, u64x2, u64x4, u64x8, u64x16, u64x32, u64x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{usizex1, usizex2, usizex4, usizex8, usizex16, usizex32, usizex64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{mask8x1, mask8x2, mask8x4, mask8x8, mask8x16, mask8x32, mask8x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{mask16x1, mask16x2, mask16x4, mask16x8, mask16x16, mask16x32, mask16x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{mask32x1, mask32x2, mask32x4, mask32x8, mask32x16, mask32x32, mask32x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{mask64x1, mask64x2, mask64x4, mask64x8, mask64x16, mask64x32, mask64x64}; + +#[rustfmt::skip] +#[doc(no_inline)] +pub use super::{masksizex1, masksizex2, masksizex4, masksizex8, masksizex16, masksizex32, masksizex64}; diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index 6065d6459..ce6217925 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -16,9 +16,14 @@ where #[inline] pub fn swizzle_dyn(self, idxs: Simd) -> Self { #![allow(unused_imports, unused_unsafe)] - #[cfg(target_arch = "aarch64")] + #[cfg(all(target_arch = "aarch64", target_endian = "little"))] use core::arch::aarch64::{uint8x8_t, vqtbl1q_u8, vtbl1_u8}; - #[cfg(all(target_arch = "arm", target_feature = "v7", target_feature = "neon"))] + #[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" + ))] use core::arch::arm::{uint8x8_t, vtbl1_u8}; #[cfg(target_arch = "wasm32")] use core::arch::wasm32 as wasm; @@ -29,13 +34,24 @@ where // SAFETY: Intrinsics covered by cfg unsafe { match N { - #[cfg(target_feature = "neon")] + #[cfg(all( + any( + target_arch = "aarch64", + all(target_arch = "arm", target_feature = "v7") + ), + target_feature = "neon", + target_endian = "little" + ))] 8 => transize(vtbl1_u8, self, idxs), #[cfg(target_feature = "ssse3")] 16 => transize(x86::_mm_shuffle_epi8, self, idxs), #[cfg(target_feature = "simd128")] 16 => transize(wasm::i8x16_swizzle, self, idxs), - #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] + #[cfg(all( + target_arch = "aarch64", + target_feature = "neon", + target_endian = "little" + ))] 16 => transize(vqtbl1q_u8, self, idxs), #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] 32 => transize_raw(avx2_pshufb, self, idxs), diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 3809cc961..9aa7bacfc 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -1,6 +1,6 @@ use crate::simd::{ - intrinsics, LaneCount, Mask, MaskElement, SimdCast, SimdCastPtr, SimdConstPtr, SimdMutPtr, - SimdPartialOrd, SupportedLaneCount, Swizzle, + intrinsics, LaneCount, Mask, MaskElement, SimdConstPtr, SimdMutPtr, SimdPartialOrd, + SupportedLaneCount, Swizzle, }; use core::convert::{TryFrom, TryInto}; @@ -122,6 +122,7 @@ where /// let v = u32x4::splat(0); /// assert_eq!(v.lanes(), 4); /// ``` + #[inline] pub const fn lanes(&self) -> usize { Self::LANES } @@ -136,6 +137,7 @@ where /// let v = u32x4::splat(8); /// assert_eq!(v.as_array(), &[8, 8, 8, 8]); /// ``` + #[inline] pub fn splat(value: T) -> Self { // This is preferred over `[value; N]`, since it's explicitly a splat: // https://github.com/rust-lang/rust/issues/97804 @@ -156,6 +158,7 @@ where /// let v: u64x4 = Simd::from_array([0, 1, 2, 3]); /// assert_eq!(v.as_array(), &[0, 1, 2, 3]); /// ``` + #[inline] pub const fn as_array(&self) -> &[T; N] { // SAFETY: `Simd` is just an overaligned `[T; N]` with // potential padding at the end, so pointer casting to a @@ -167,6 +170,7 @@ where } /// Returns a mutable array reference containing the entire SIMD vector. + #[inline] pub fn as_mut_array(&mut self) -> &mut [T; N] { // SAFETY: `Simd` is just an overaligned `[T; N]` with // potential padding at the end, so pointer casting to a @@ -184,6 +188,7 @@ where /// /// # Safety /// Reading `ptr` must be safe, as if by `<*const [T; N]>::read_unaligned`. + #[inline] const unsafe fn load(ptr: *const [T; N]) -> Self { // There are potentially simpler ways to write this function, but this should result in // LLVM `load ` @@ -204,6 +209,7 @@ where /// /// # Safety /// Writing to `ptr` must be safe, as if by `<*mut [T; N]>::write_unaligned`. + #[inline] const unsafe fn store(self, ptr: *mut [T; N]) { // There are potentially simpler ways to write this function, but this should result in // LLVM `store ` @@ -216,6 +222,7 @@ where } /// Converts an array to a SIMD vector. + #[inline] pub const fn from_array(array: [T; N]) -> Self { // SAFETY: `&array` is safe to read. // @@ -228,6 +235,7 @@ where } /// Converts a SIMD vector to an array. + #[inline] pub const fn to_array(self) -> [T; N] { let mut tmp = core::mem::MaybeUninit::uninit(); // SAFETY: writing to `tmp` is safe and initializes it. @@ -259,6 +267,8 @@ where /// assert_eq!(v.as_array(), &[1, 2, 3, 4]); /// ``` #[must_use] + #[inline] + #[track_caller] pub const fn from_slice(slice: &[T]) -> Self { assert!( slice.len() >= Self::LANES, @@ -287,6 +297,8 @@ where /// v.copy_to_slice(&mut dest); /// assert_eq!(&dest, &[1, 2, 3, 4, 0, 0]); /// ``` + #[inline] + #[track_caller] pub fn copy_to_slice(self, slice: &mut [T]) { assert!( slice.len() >= Self::LANES, @@ -297,76 +309,6 @@ where unsafe { self.store(slice.as_mut_ptr().cast()) } } - /// Performs elementwise conversion of a SIMD vector's elements to another SIMD-valid type. - /// - /// This follows the semantics of Rust's `as` conversion for casting integers between - /// signed and unsigned (interpreting integers as 2s complement, so `-1` to `U::MAX` and - /// `1 << (U::BITS -1)` becoming `I::MIN` ), and from floats to integers (truncating, - /// or saturating at the limits) for each element. - /// - /// # Examples - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::Simd; - /// let floats: Simd = Simd::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]); - /// let ints = floats.cast::(); - /// assert_eq!(ints, Simd::from_array([1, -4, i32::MAX, 0])); - /// - /// // Formally equivalent, but `Simd::cast` can optimize better. - /// assert_eq!(ints, Simd::from_array(floats.to_array().map(|x| x as i32))); - /// - /// // The float conversion does not round-trip. - /// let floats_again = ints.cast(); - /// assert_ne!(floats, floats_again); - /// assert_eq!(floats_again, Simd::from_array([1.0, -4.0, 2147483647.0, 0.0])); - /// ``` - #[must_use] - #[inline] - pub fn cast(self) -> Simd - where - T: SimdCast, - { - // Safety: supported types are guaranteed by SimdCast - unsafe { intrinsics::simd_as(self) } - } - - /// Casts a vector of pointers to another pointer type. - #[must_use] - #[inline] - pub fn cast_ptr(self) -> Simd - where - T: SimdCastPtr, - U: SimdElement, - { - // Safety: supported types are guaranteed by SimdCastPtr - unsafe { intrinsics::simd_cast_ptr(self) } - } - - /// Rounds toward zero and converts to the same-width integer type, assuming that - /// the value is finite and fits in that type. - /// - /// # Safety - /// The value must: - /// - /// * Not be NaN - /// * Not be infinite - /// * Be representable in the return type, after truncating off its fractional part - /// - /// If these requirements are infeasible or costly, consider using the safe function [cast], - /// which saturates on conversion. - /// - /// [cast]: Simd::cast - #[inline] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn to_int_unchecked(self) -> Simd - where - T: core::convert::FloatToInt + SimdCast, - I: SimdCast, - { - // Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants - unsafe { intrinsics::simd_cast(self) } - } - /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. /// If an index is out-of-bounds, the element is instead selected from the `or` vector. /// @@ -717,6 +659,7 @@ where LaneCount: SupportedLaneCount, T: SimdElement, { + #[inline] fn clone(&self) -> Self { *self } @@ -861,6 +804,7 @@ where LaneCount: SupportedLaneCount, T: SimdElement, { + #[inline] fn from(array: [T; N]) -> Self { Self::from_array(array) } @@ -871,6 +815,7 @@ where LaneCount: SupportedLaneCount, T: SimdElement, { + #[inline] fn from(vector: Simd) -> Self { vector.to_array() } @@ -883,6 +828,7 @@ where { type Error = core::array::TryFromSliceError; + #[inline] fn try_from(slice: &[T]) -> Result { Ok(Self::from_array(slice.try_into()?)) } @@ -895,6 +841,7 @@ where { type Error = core::array::TryFromSliceError; + #[inline] fn try_from(slice: &mut [T]) -> Result { Ok(Self::from_array(slice.try_into()?)) } diff --git a/library/portable-simd/crates/core_simd/tests/cast.rs b/library/portable-simd/crates/core_simd/tests/cast.rs index ab5650f07..00545936e 100644 --- a/library/portable-simd/crates/core_simd/tests/cast.rs +++ b/library/portable-simd/crates/core_simd/tests/cast.rs @@ -2,7 +2,8 @@ macro_rules! cast_types { ($start:ident, $($target:ident),*) => { mod $start { - use core_simd::simd::Simd; + #[allow(unused)] + use core_simd::simd::{Simd, SimdInt, SimdUint, SimdFloat}; type Vector = Simd<$start, N>; $( mod $target { diff --git a/library/portable-simd/crates/core_simd/tests/round.rs b/library/portable-simd/crates/core_simd/tests/round.rs index 8b9638ad4..aacf7bd3b 100644 --- a/library/portable-simd/crates/core_simd/tests/round.rs +++ b/library/portable-simd/crates/core_simd/tests/round.rs @@ -53,6 +53,7 @@ macro_rules! float_rounding_test { test_helpers::test_lanes! { fn to_int_unchecked() { + use core_simd::simd::SimdFloat; // The maximum integer that can be represented by the equivalently sized float has // all of the mantissa digits set to 1, pushed up to the MSB. const ALL_MANTISSA_BITS: IntScalar = ((1 << ::MANTISSA_DIGITS) - 1); diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index caecda1bc..86ce5d9c6 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -8,7 +8,7 @@ #![deny(unsafe_code)] -use crate::{Delimiter, Level, LineColumn, Spacing}; +use crate::{Delimiter, Level, Spacing}; use std::fmt; use std::hash::Hash; use std::marker; @@ -95,10 +95,10 @@ macro_rules! with_api { fn parent($self: $S::Span) -> Option<$S::Span>; fn source($self: $S::Span) -> $S::Span; fn byte_range($self: $S::Span) -> Range; - fn start($self: $S::Span) -> LineColumn; - fn end($self: $S::Span) -> LineColumn; - fn before($self: $S::Span) -> $S::Span; - fn after($self: $S::Span) -> $S::Span; + fn start($self: $S::Span) -> $S::Span; + fn end($self: $S::Span) -> $S::Span; + fn line($self: $S::Span) -> usize; + fn column($self: $S::Span) -> usize; fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; fn subspan($self: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; @@ -299,7 +299,6 @@ mark_noop! { Delimiter, LitKind, Level, - LineColumn, Spacing, } @@ -319,7 +318,6 @@ rpc_encode_decode!( Help, } ); -rpc_encode_decode!(struct LineColumn { line, column }); rpc_encode_decode!( enum Spacing { Alone, diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index c64665b6a..7fb0d989c 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -43,7 +43,6 @@ mod diagnostic; #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] pub use diagnostic::{Diagnostic, Level, MultiSpan}; -use std::cmp::Ordering; use std::ops::{Range, RangeBounds}; use std::path::PathBuf; use std::str::FromStr; @@ -494,28 +493,32 @@ impl Span { self.0.byte_range() } - /// Gets the starting line/column in the source file for this span. + /// Creates an empty span pointing to directly before this span. #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn start(&self) -> LineColumn { - self.0.start().add_1_to_column() + pub fn start(&self) -> Span { + Span(self.0.start()) } - /// Gets the ending line/column in the source file for this span. + /// Creates an empty span pointing to directly after this span. #[unstable(feature = "proc_macro_span", issue = "54725")] - pub fn end(&self) -> LineColumn { - self.0.end().add_1_to_column() + pub fn end(&self) -> Span { + Span(self.0.end()) } - /// Creates an empty span pointing to directly before this span. - #[unstable(feature = "proc_macro_span_shrink", issue = "87552")] - pub fn before(&self) -> Span { - Span(self.0.before()) + /// The one-indexed line of the source file where the span starts. + /// + /// To obtain the line of the span's end, use `span.end().line()`. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn line(&self) -> usize { + self.0.line() } - /// Creates an empty span pointing to directly after this span. - #[unstable(feature = "proc_macro_span_shrink", issue = "87552")] - pub fn after(&self) -> Span { - Span(self.0.after()) + /// The one-indexed column of the source file where the span starts. + /// + /// To obtain the column of the span's end, use `span.end().column()`. + #[unstable(feature = "proc_macro_span", issue = "54725")] + pub fn column(&self) -> usize { + self.0.column() } /// Creates a new span encompassing `self` and `other`. @@ -586,44 +589,6 @@ impl fmt::Debug for Span { } } -/// A line-column pair representing the start or end of a `Span`. -#[unstable(feature = "proc_macro_span", issue = "54725")] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct LineColumn { - /// The 1-indexed line in the source file on which the span starts or ends (inclusive). - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub line: usize, - /// The 1-indexed column (number of bytes in UTF-8 encoding) in the source - /// file on which the span starts or ends (inclusive). - #[unstable(feature = "proc_macro_span", issue = "54725")] - pub column: usize, -} - -impl LineColumn { - fn add_1_to_column(self) -> Self { - LineColumn { line: self.line, column: self.column + 1 } - } -} - -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl !Send for LineColumn {} -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl !Sync for LineColumn {} - -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl Ord for LineColumn { - fn cmp(&self, other: &Self) -> Ordering { - self.line.cmp(&other.line).then(self.column.cmp(&other.column)) - } -} - -#[unstable(feature = "proc_macro_span", issue = "54725")] -impl PartialOrd for LineColumn { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - /// The source file of a given `Span`. #[unstable(feature = "proc_macro_span", issue = "54725")] #[derive(Clone)] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 09afc696c..664a47615 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -15,19 +15,20 @@ 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" } -libc = { version = "0.2.143", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.92" } +libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.95" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.13", default-features = false, features = ['rustc-dep-of-std'] } +hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] } std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate -addr2line = { version = "0.18.0", optional = true, default-features = false } + +addr2line = { version = "0.21.0", optional = true, default-features = false } rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] } -miniz_oxide = { version = "0.6.0", optional = true, default-features = false } +miniz_oxide = { version = "0.7.0", optional = true, default-features = false } [dependencies.object] -version = "0.30.0" +version = "0.32.0" optional = true default-features = false features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] @@ -36,7 +37,7 @@ features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } rand_xorshift = "0.3.0" -[target.'cfg(any(all(target_family = "wasm", not(target_os = "emscripten")), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] +[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] dlmalloc = { version = "0.2.3", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] @@ -63,6 +64,7 @@ compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] compiler-builtins-no-asm = ["alloc/compiler-builtins-no-asm"] compiler-builtins-mangled-names = ["alloc/compiler-builtins-mangled-names"] +compiler-builtins-weak-intrinsics = ["alloc/compiler-builtins-weak-intrinsics"] llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] diff --git a/library/std/build.rs b/library/std/build.rs index 0fb03c8e8..ddf6e84d8 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -18,6 +18,7 @@ fn main() { || target.contains("illumos") || target.contains("apple-darwin") || target.contains("apple-ios") + || target.contains("apple-tvos") || target.contains("apple-watchos") || target.contains("uwp") || target.contains("windows") @@ -36,6 +37,8 @@ fn main() { || target.contains("nintendo-3ds") || target.contains("vita") || target.contains("nto") + // See src/bootstrap/synthetic_targets.rs + || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() { // These platforms don't have any special requirements. } else { @@ -46,7 +49,6 @@ fn main() { // - mipsel-sony-psp // - nvptx64-nvidia-cuda // - arch=avr - // - tvos (aarch64-apple-tvos, x86_64-apple-tvos) // - uefi (x86_64-unknown-uefi, i686-unknown-uefi) // - JSON targets // - Any new targets that have not been explicitly added above. diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index c722bad2e..a083b6560 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -623,28 +623,27 @@ impl HashMap { /// If the closure returns false, or panics, the element remains in the map and will not be /// yielded. /// - /// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of + /// Note that `extract_if` lets you mutate every value in the filter closure, regardless of /// whether you choose to keep or remove it. /// - /// If the iterator is only partially consumed or not consumed at all, each of the remaining - /// elements will still be subjected to the closure and removed and dropped if it returns true. + /// 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. /// - /// It is unspecified how many more elements will be subjected to the closure - /// if a panic occurs in the closure, or a panic occurs while dropping an element, - /// or if the `DrainFilter` value is leaked. + /// [`retain`]: HashMap::retain /// /// # Examples /// /// Splitting a map into even and odd keys, reusing the original map: /// /// ``` - /// #![feature(hash_drain_filter)] + /// #![feature(hash_extract_if)] /// use std::collections::HashMap; /// /// let mut map: HashMap = (0..8).map(|x| (x, x)).collect(); - /// let drained: HashMap = map.drain_filter(|k, _v| k % 2 == 0).collect(); + /// let extracted: HashMap = map.extract_if(|k, _v| k % 2 == 0).collect(); /// - /// let mut evens = drained.keys().copied().collect::>(); + /// let mut evens = extracted.keys().copied().collect::>(); /// let mut odds = map.keys().copied().collect::>(); /// evens.sort(); /// odds.sort(); @@ -654,12 +653,12 @@ impl HashMap { /// ``` #[inline] #[rustc_lint_query_instability] - #[unstable(feature = "hash_drain_filter", issue = "59618")] - pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F> + #[unstable(feature = "hash_extract_if", issue = "59618")] + pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool, { - DrainFilter { base: self.base.drain_filter(pred) } + ExtractIf { base: self.base.extract_if(pred) } } /// Retains only the elements specified by the predicate. @@ -1578,28 +1577,29 @@ impl<'a, K, V> Drain<'a, K, V> { /// A draining, filtering iterator over the entries of a `HashMap`. /// -/// This `struct` is created by the [`drain_filter`] method on [`HashMap`]. +/// This `struct` is created by the [`extract_if`] method on [`HashMap`]. /// -/// [`drain_filter`]: HashMap::drain_filter +/// [`extract_if`]: HashMap::extract_if /// /// # Example /// /// ``` -/// #![feature(hash_drain_filter)] +/// #![feature(hash_extract_if)] /// /// use std::collections::HashMap; /// /// let mut map = HashMap::from([ /// ("a", 1), /// ]); -/// let iter = map.drain_filter(|_k, v| *v % 2 == 0); +/// let iter = map.extract_if(|_k, v| *v % 2 == 0); /// ``` -#[unstable(feature = "hash_drain_filter", issue = "59618")] -pub struct DrainFilter<'a, K, V, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct ExtractIf<'a, K, V, F> where F: FnMut(&K, &mut V) -> bool, { - base: base::DrainFilter<'a, K, V, F>, + base: base::ExtractIf<'a, K, V, F>, } /// A mutable iterator over the values of a `HashMap`. @@ -2479,8 +2479,8 @@ where } } -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl Iterator for DrainFilter<'_, K, V, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl Iterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool, { @@ -2496,16 +2496,16 @@ where } } -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl<'a, K, V, F> fmt::Debug for DrainFilter<'a, K, V, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl<'a, K, V, F> fmt::Debug for ExtractIf<'a, K, V, F> where F: FnMut(&K, &mut V) -> bool, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DrainFilter").finish_non_exhaustive() + f.debug_struct("ExtractIf").finish_non_exhaustive() } } @@ -2543,12 +2543,12 @@ impl<'a, K, V> Entry<'a, K, V> { /// ``` /// use std::collections::HashMap; /// - /// let mut map: HashMap<&str, String> = HashMap::new(); - /// let s = "hoho".to_string(); + /// let mut map = HashMap::new(); + /// let value = "hoho"; /// - /// map.entry("poneyland").or_insert_with(|| s); + /// map.entry("poneyland").or_insert_with(|| value); /// - /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// assert_eq!(map["poneyland"], "hoho"); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 6b89518e2..91a3776e7 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -944,7 +944,7 @@ fn test_raw_entry() { } } -mod test_drain_filter { +mod test_extract_if { use super::*; use crate::panic::{catch_unwind, AssertUnwindSafe}; @@ -968,7 +968,7 @@ mod test_drain_filter { #[test] fn empty() { let mut map: HashMap = HashMap::new(); - map.drain_filter(|_, _| unreachable!("there's nothing to decide on")); + map.extract_if(|_, _| unreachable!("there's nothing to decide on")).for_each(drop); assert!(map.is_empty()); } @@ -976,7 +976,7 @@ mod test_drain_filter { fn consuming_nothing() { let pairs = (0..3).map(|i| (i, i)); let mut map: HashMap<_, _> = pairs.collect(); - assert!(map.drain_filter(|_, _| false).eq_sorted(crate::iter::empty())); + assert!(map.extract_if(|_, _| false).eq_sorted(crate::iter::empty())); assert_eq!(map.len(), 3); } @@ -984,7 +984,7 @@ mod test_drain_filter { fn consuming_all() { let pairs = (0..3).map(|i| (i, i)); let mut map: HashMap<_, _> = pairs.clone().collect(); - assert!(map.drain_filter(|_, _| true).eq_sorted(pairs)); + assert!(map.extract_if(|_, _| true).eq_sorted(pairs)); assert!(map.is_empty()); } @@ -993,7 +993,7 @@ mod test_drain_filter { let pairs = (0..3).map(|i| (i, i)); let mut map: HashMap<_, _> = pairs.collect(); assert!( - map.drain_filter(|_, v| { + map.extract_if(|_, v| { *v += 6; false }) @@ -1008,7 +1008,7 @@ mod test_drain_filter { let pairs = (0..3).map(|i| (i, i)); let mut map: HashMap<_, _> = pairs.collect(); assert!( - map.drain_filter(|_, v| { + map.extract_if(|_, v| { *v += 6; true }) @@ -1034,14 +1034,15 @@ mod test_drain_filter { let mut map = (0..3).map(|i| (i, D)).collect::>(); catch_unwind(move || { - drop(map.drain_filter(|_, _| { + map.extract_if(|_, _| { PREDS.fetch_add(1, Ordering::SeqCst); true - })) + }) + .for_each(drop) }) .unwrap_err(); - assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(PREDS.load(Ordering::SeqCst), 2); assert_eq!(DROPS.load(Ordering::SeqCst), 3); } @@ -1060,10 +1061,11 @@ mod test_drain_filter { let mut map = (0..3).map(|i| (i, D)).collect::>(); catch_unwind(AssertUnwindSafe(|| { - drop(map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { + map.extract_if(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { 0 => true, _ => panic!(), - })) + }) + .for_each(drop) })) .unwrap_err(); @@ -1088,7 +1090,7 @@ mod test_drain_filter { let mut map = (0..3).map(|i| (i, D)).collect::>(); { - let mut it = map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { + let mut it = map.extract_if(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { 0 => true, _ => panic!(), }); diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index ac906e682..ec59634df 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -262,25 +262,24 @@ impl HashSet { /// If the closure returns false, the value will remain in the list and will not be yielded /// by the iterator. /// - /// If the iterator is only partially consumed or not consumed at all, each of the remaining - /// values will still be subjected to the closure and removed and dropped if it returns true. + /// 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. /// - /// It is unspecified how many more values will be subjected to the closure - /// if a panic occurs in the closure, or if a panic occurs while dropping a value, or if the - /// `DrainFilter` itself is leaked. + /// [`retain`]: HashSet::retain /// /// # Examples /// /// Splitting a set into even and odd values, reusing the original set: /// /// ``` - /// #![feature(hash_drain_filter)] + /// #![feature(hash_extract_if)] /// use std::collections::HashSet; /// /// let mut set: HashSet = (0..8).collect(); - /// let drained: HashSet = set.drain_filter(|v| v % 2 == 0).collect(); + /// let extracted: HashSet = set.extract_if(|v| v % 2 == 0).collect(); /// - /// let mut evens = drained.into_iter().collect::>(); + /// let mut evens = extracted.into_iter().collect::>(); /// let mut odds = set.into_iter().collect::>(); /// evens.sort(); /// odds.sort(); @@ -290,12 +289,12 @@ impl HashSet { /// ``` #[inline] #[rustc_lint_query_instability] - #[unstable(feature = "hash_drain_filter", issue = "59618")] - pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, T, F> + #[unstable(feature = "hash_extract_if", issue = "59618")] + pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, T, F> where F: FnMut(&T) -> bool, { - DrainFilter { base: self.base.drain_filter(pred) } + ExtractIf { base: self.base.extract_if(pred) } } /// Retains only the elements specified by the predicate. @@ -868,7 +867,9 @@ where /// Returns whether the value was newly inserted. That is: /// /// - If the set did not previously contain this value, `true` is returned. - /// - If the set already contained this value, `false` is returned. + /// - If the set already contained this value, `false` is returned, + /// and the set is not modified: original value is not replaced, + /// and the value passed as argument is dropped. /// /// # Examples /// @@ -1310,27 +1311,27 @@ pub struct Drain<'a, K: 'a> { /// A draining, filtering iterator over the items of a `HashSet`. /// -/// This `struct` is created by the [`drain_filter`] method on [`HashSet`]. +/// This `struct` is created by the [`extract_if`] method on [`HashSet`]. /// -/// [`drain_filter`]: HashSet::drain_filter +/// [`extract_if`]: HashSet::extract_if /// /// # Examples /// /// ``` -/// #![feature(hash_drain_filter)] +/// #![feature(hash_extract_if)] /// /// use std::collections::HashSet; /// /// let mut a = HashSet::from([1, 2, 3]); /// -/// let mut drain_filtered = a.drain_filter(|v| v % 2 == 0); +/// let mut extract_ifed = a.extract_if(|v| v % 2 == 0); /// ``` -#[unstable(feature = "hash_drain_filter", issue = "59618")] -pub struct DrainFilter<'a, K, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +pub struct ExtractIf<'a, K, F> where F: FnMut(&K) -> bool, { - base: base::DrainFilter<'a, K, F>, + base: base::ExtractIf<'a, K, F>, } /// A lazy iterator producing elements in the intersection of `HashSet`s. @@ -1576,8 +1577,8 @@ impl fmt::Debug for Drain<'_, K> { } } -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl Iterator for DrainFilter<'_, K, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl Iterator for ExtractIf<'_, K, F> where F: FnMut(&K) -> bool, { @@ -1593,16 +1594,16 @@ where } } -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl FusedIterator for DrainFilter<'_, K, F> where F: FnMut(&K) -> bool {} +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl FusedIterator for ExtractIf<'_, K, F> where F: FnMut(&K) -> bool {} -#[unstable(feature = "hash_drain_filter", issue = "59618")] -impl<'a, K, F> fmt::Debug for DrainFilter<'a, K, F> +#[unstable(feature = "hash_extract_if", issue = "59618")] +impl<'a, K, F> fmt::Debug for ExtractIf<'a, K, F> where F: FnMut(&K) -> bool, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("DrainFilter").finish_non_exhaustive() + f.debug_struct("ExtractIf").finish_non_exhaustive() } } diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs index 941a0450c..e0cd80b44 100644 --- a/library/std/src/collections/hash/set/tests.rs +++ b/library/std/src/collections/hash/set/tests.rs @@ -3,6 +3,7 @@ use super::HashSet; use crate::panic::{catch_unwind, AssertUnwindSafe}; use crate::sync::atomic::{AtomicU32, Ordering}; +use crate::sync::Arc; #[test] fn test_zero_capacities() { @@ -418,18 +419,18 @@ fn test_retain() { } #[test] -fn test_drain_filter() { +fn test_extract_if() { let mut x: HashSet<_> = [1].iter().copied().collect(); let mut y: HashSet<_> = [1].iter().copied().collect(); - x.drain_filter(|_| true); - y.drain_filter(|_| false); + x.extract_if(|_| true).for_each(drop); + y.extract_if(|_| false).for_each(drop); assert_eq!(x.len(), 0); assert_eq!(y.len(), 1); } #[test] -fn test_drain_filter_drop_panic_leak() { +fn test_extract_if_drop_panic_leak() { static PREDS: AtomicU32 = AtomicU32::new(0); static DROPS: AtomicU32 = AtomicU32::new(0); @@ -446,19 +447,20 @@ fn test_drain_filter_drop_panic_leak() { let mut set = (0..3).map(|i| D(i)).collect::>(); catch_unwind(move || { - drop(set.drain_filter(|_| { + set.extract_if(|_| { PREDS.fetch_add(1, Ordering::SeqCst); true - })) + }) + .for_each(drop) }) .ok(); - assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(PREDS.load(Ordering::SeqCst), 2); assert_eq!(DROPS.load(Ordering::SeqCst), 3); } #[test] -fn test_drain_filter_pred_panic_leak() { +fn test_extract_if_pred_panic_leak() { static PREDS: AtomicU32 = AtomicU32::new(0); static DROPS: AtomicU32 = AtomicU32::new(0); @@ -473,10 +475,11 @@ fn test_drain_filter_pred_panic_leak() { let mut set: HashSet<_> = (0..3).map(|_| D).collect(); catch_unwind(AssertUnwindSafe(|| { - drop(set.drain_filter(|_| match PREDS.fetch_add(1, Ordering::SeqCst) { + set.extract_if(|_| match PREDS.fetch_add(1, Ordering::SeqCst) { 0 => true, _ => panic!(), - })) + }) + .for_each(drop) })) .ok(); @@ -502,3 +505,22 @@ fn const_with_hasher() { const X: HashSet<(), ()> = HashSet::with_hasher(()); assert_eq!(X.len(), 0); } + +#[test] +fn test_insert_does_not_overwrite_the_value() { + let first_value = Arc::new(17); + let second_value = Arc::new(17); + + let mut set = HashSet::new(); + let inserted = set.insert(first_value.clone()); + assert!(inserted); + + let inserted = set.insert(second_value); + assert!(!inserted); + + assert!( + Arc::ptr_eq(set.iter().next().unwrap(), &first_value), + "Insert must not overwrite the value, so the contained value pointer \ + must be the same as first value pointer we inserted" + ); +} diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 408244b2c..bed90418b 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -528,7 +528,7 @@ impl f32 { /// The positive difference of two numbers. /// - /// * If `self <= other`: `0:0` + /// * If `self <= other`: `0.0` /// * Else: `self - other` /// /// # Examples diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 6782b861f..e72de05ca 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -530,7 +530,7 @@ impl f64 { /// The positive difference of two numbers. /// - /// * If `self <= other`: `0:0` + /// * If `self <= other`: `0.0` /// * Else: `self - other` /// /// # Examples diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index d987bf69b..3ddb87487 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -127,6 +127,14 @@ //! trait, which provides a [`from_wide`] method to convert a native Windows //! string (without the terminating nul character) to an [`OsString`]. //! +//! ## On all platforms +//! +//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of +//! UTF-8; see [`OsString`] for more details on its encoding on different platforms. +//! +//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_os_str_bytes`] and +//! [`OsStr::from_os_str_bytes_unchecked`]. +//! //! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value //! [Unicode code point]: https://www.unicode.org/glossary/#code_point //! [`env::set_var()`]: crate::env::set_var "env::set_var" diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 5c0541d3c..e7bad9d54 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -667,6 +667,51 @@ impl OsStr { s.as_ref() } + /// Converts a slice of bytes to an OS string slice without checking that the string contains + /// valid `OsStr`-encoded data. + /// + /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. + /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit + /// ASCII. + /// + /// See the [module's toplevel documentation about conversions][conversions] for safe, + /// cross-platform [conversions] from/to native representations. + /// + /// # Safety + /// + /// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of + /// validated UTF-8 and bytes from [`OsStr::as_os_str_bytes`] from within the same rust version + /// built for the same target platform. For example, reconstructing an `OsStr` from bytes sent + /// over the network or stored in a file will likely violate these safety rules. + /// + /// Due to the encoding being self-synchronizing, the bytes from [`OsStr::as_os_str_bytes`] can be + /// split either immediately before or immediately after any valid non-empty UTF-8 substring. + /// + /// # Example + /// + /// ``` + /// #![feature(os_str_bytes)] + /// + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("Mary had a little lamb"); + /// let bytes = os_str.as_os_str_bytes(); + /// let words = bytes.split(|b| *b == b' '); + /// let words: Vec<&OsStr> = words.map(|word| { + /// // SAFETY: + /// // - Each `word` only contains content that originated from `OsStr::as_os_str_bytes` + /// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring + /// unsafe { OsStr::from_os_str_bytes_unchecked(word) } + /// }).collect(); + /// ``` + /// + /// [conversions]: super#conversions + #[inline] + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub unsafe fn from_os_str_bytes_unchecked(bytes: &[u8]) -> &Self { + Self::from_inner(Slice::from_os_str_bytes_unchecked(bytes)) + } + #[inline] fn from_inner(inner: &Slice) -> &OsStr { // SAFETY: OsStr is just a wrapper of Slice, @@ -700,7 +745,7 @@ impl OsStr { without modifying the original"] #[inline] pub fn to_str(&self) -> Option<&str> { - self.inner.to_str() + self.inner.to_str().ok() } /// Converts an `OsStr` to a [Cow]<[str]>. @@ -837,13 +882,24 @@ impl OsStr { OsString { inner: Buf::from_box(boxed) } } - /// Gets the underlying byte representation. + /// Converts an OS string slice to a byte slice. To convert the byte slice back into an OS + /// string slice, use the [`OsStr::from_os_str_bytes_unchecked`] function. + /// + /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. + /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit + /// ASCII. + /// + /// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should + /// be treated as opaque and only comparable within the same rust version built for the same + /// target platform. For example, sending the slice over the network or storing it in a file + /// will likely result in incompatible byte slices. See [`OsString`] for more encoding details + /// and [`std::ffi`] for platform-specific, specified conversions. /// - /// Note: it is *crucial* that this API is not externally public, to avoid - /// revealing the internal, platform-specific encodings. + /// [`std::ffi`]: crate::ffi #[inline] - pub(crate) fn bytes(&self) -> &[u8] { - unsafe { &*(&self.inner as *const _ as *const [u8]) } + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub fn as_os_str_bytes(&self) -> &[u8] { + self.inner.as_os_str_bytes() } /// Converts this string to its ASCII lower case equivalent in-place. @@ -1109,6 +1165,24 @@ impl<'a> From> for OsString { } } +#[stable(feature = "str_tryfrom_osstr_impl", since = "1.72.0")] +impl<'a> TryFrom<&'a OsStr> for &'a str { + type Error = crate::str::Utf8Error; + + /// Tries to convert an `&OsStr` to a `&str`. + /// + /// ``` + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("foo"); + /// let as_str = <&str>::try_from(os_str).unwrap(); + /// assert_eq!(as_str, "foo"); + /// ``` + fn try_from(value: &'a OsStr) -> Result { + value.inner.to_str() + } +} + #[stable(feature = "box_default_extra", since = "1.17.0")] impl Default for Box { #[inline] @@ -1131,7 +1205,7 @@ impl Default for &OsStr { impl PartialEq for OsStr { #[inline] fn eq(&self, other: &OsStr) -> bool { - self.bytes().eq(other.bytes()) + self.as_os_str_bytes().eq(other.as_os_str_bytes()) } } @@ -1158,23 +1232,23 @@ impl Eq for OsStr {} impl PartialOrd for OsStr { #[inline] fn partial_cmp(&self, other: &OsStr) -> Option { - self.bytes().partial_cmp(other.bytes()) + self.as_os_str_bytes().partial_cmp(other.as_os_str_bytes()) } #[inline] fn lt(&self, other: &OsStr) -> bool { - self.bytes().lt(other.bytes()) + self.as_os_str_bytes().lt(other.as_os_str_bytes()) } #[inline] fn le(&self, other: &OsStr) -> bool { - self.bytes().le(other.bytes()) + self.as_os_str_bytes().le(other.as_os_str_bytes()) } #[inline] fn gt(&self, other: &OsStr) -> bool { - self.bytes().gt(other.bytes()) + self.as_os_str_bytes().gt(other.as_os_str_bytes()) } #[inline] fn ge(&self, other: &OsStr) -> bool { - self.bytes().ge(other.bytes()) + self.as_os_str_bytes().ge(other.as_os_str_bytes()) } } @@ -1193,7 +1267,7 @@ impl PartialOrd for OsStr { impl Ord for OsStr { #[inline] fn cmp(&self, other: &OsStr) -> cmp::Ordering { - self.bytes().cmp(other.bytes()) + self.as_os_str_bytes().cmp(other.as_os_str_bytes()) } } @@ -1243,7 +1317,7 @@ impl_cmp!(Cow<'a, OsStr>, OsString); impl Hash for OsStr { #[inline] fn hash(&self, state: &mut H) { - self.bytes().hash(state) + self.as_os_str_bytes().hash(state) } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 89dfdfafd..c2d82169d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1755,8 +1755,14 @@ impl DirEntry { self.0.file_type().map(FileType) } - /// Returns the bare file name of this directory entry without any other - /// leading path component. + /// Returns the file name of this directory entry without any + /// leading path component(s). + /// + /// As an example, + /// the output of the function will result in "foo" for all the following paths: + /// - "./foo" + /// - "/the/foo" + /// - "../../foo" /// /// # Examples /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index e2480bcbb..9ff01b9c3 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1640,6 +1640,10 @@ fn test_file_times() { use crate::os::ios::fs::FileTimesExt; #[cfg(target_os = "macos")] use crate::os::macos::fs::FileTimesExt; + #[cfg(target_os = "tvos")] + use crate::os::tvos::fs::FileTimesExt; + #[cfg(target_os = "tvos")] + use crate::os::tvos::fs::FileTimesExt; #[cfg(target_os = "watchos")] use crate::os::watchos::fs::FileTimesExt; #[cfg(windows)] @@ -1651,9 +1655,21 @@ fn test_file_times() { let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); times = times.set_accessed(accessed).set_modified(modified); - #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + windows, + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + ))] let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); - #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + windows, + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + ))] { times = times.set_created(created); } @@ -1678,7 +1694,13 @@ fn test_file_times() { let metadata = file.metadata().unwrap(); assert_eq!(metadata.accessed().unwrap(), accessed); assert_eq!(metadata.modified().unwrap(), modified); - #[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + windows, + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "tvos", + ))] { assert_eq!(metadata.created().unwrap(), created); } diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 4f339a18a..7097dfef8 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -47,13 +47,13 @@ use buffer::Buffer; /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct BufReader { - inner: R, +pub struct BufReader { buf: Buffer, + inner: R, } impl BufReader { - /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KB, + /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KiB, /// but may change in the future. /// /// # Examples @@ -95,7 +95,7 @@ impl BufReader { } } -impl BufReader { +impl BufReader { /// Gets a reference to the underlying reader. /// /// It is inadvisable to directly read from the underlying reader. @@ -213,26 +213,29 @@ impl BufReader { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> R { + pub fn into_inner(self) -> R + where + R: Sized, + { self.inner } /// Invalidates all data in the internal buffer. #[inline] - fn discard_buffer(&mut self) { + pub(in crate::io) fn discard_buffer(&mut self) { self.buf.discard_buffer() } } // This is only used by a test which asserts that the initialization-tracking is correct. #[cfg(test)] -impl BufReader { +impl BufReader { pub fn initialized(&self) -> usize { self.buf.initialized() } } -impl BufReader { +impl BufReader { /// Seeks relative to the current position. If the new position lies within the buffer, /// the buffer will not be flushed, allowing for more efficient seeks. /// This method does not return the location of the underlying reader, so the caller @@ -257,7 +260,7 @@ impl BufReader { } #[stable(feature = "rust1", since = "1.0.0")] -impl Read for BufReader { +impl Read for BufReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { // If we don't have any buffered data and we're doing a massive read // (larger than our internal buffer), bypass our internal buffer @@ -371,7 +374,7 @@ impl Read for BufReader { } #[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for BufReader { +impl BufRead for BufReader { fn fill_buf(&mut self) -> io::Result<&[u8]> { self.buf.fill_buf(&mut self.inner) } @@ -384,11 +387,11 @@ impl BufRead for BufReader { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for BufReader where - R: fmt::Debug, + R: ?Sized + fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("BufReader") - .field("reader", &self.inner) + .field("reader", &&self.inner) .field( "buffer", &format_args!("{}/{}", self.buf.filled() - self.buf.pos(), self.capacity()), @@ -398,7 +401,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Seek for BufReader { +impl Seek for BufReader { /// Seek to an offset, in bytes, in the underlying reader. /// /// The position used for seeking with [SeekFrom::Current]\(_) is the @@ -491,7 +494,7 @@ impl Seek for BufReader { } } -impl SizeHint for BufReader { +impl SizeHint for BufReader { #[inline] fn lower_bound(&self) -> usize { SizeHint::lower_bound(self.get_ref()) + self.buffer().len() diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 14c455d4f..0f04f2911 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -67,8 +67,7 @@ use crate::ptr; /// [`TcpStream`]: crate::net::TcpStream /// [`flush`]: BufWriter::flush #[stable(feature = "rust1", since = "1.0.0")] -pub struct BufWriter { - inner: W, +pub struct BufWriter { // The buffer. Avoid using this like a normal `Vec` in common code paths. // That is, don't use `buf.push`, `buf.extend_from_slice`, or any other // methods that require bounds checking or the like. This makes an enormous @@ -78,10 +77,11 @@ pub struct BufWriter { // write the buffered data a second time in BufWriter's destructor. This // flag tells the Drop impl if it should skip the flush. panicked: bool, + inner: W, } impl BufWriter { - /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, + /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KiB, /// but may change in the future. /// /// # Examples @@ -115,6 +115,69 @@ impl BufWriter { BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false } } + /// Unwraps this `BufWriter`, returning the underlying writer. + /// + /// The buffer is written out before returning the writer. + /// + /// # Errors + /// + /// An [`Err`] will be returned if an error occurs while flushing the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // unwrap the TcpStream and flush the buffer + /// let stream = buffer.into_inner().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(mut self) -> Result>> { + match self.flush_buf() { + Err(e) => Err(IntoInnerError::new(self, e)), + Ok(()) => Ok(self.into_parts().0), + } + } + + /// Disassembles this `BufWriter`, returning the underlying writer, and any buffered but + /// unwritten data. + /// + /// If the underlying writer panicked, it is not known what portion of the data was written. + /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer + /// contents can still be recovered). + /// + /// `into_parts` makes no attempt to flush data and cannot fail. + /// + /// # Examples + /// + /// ``` + /// use std::io::{BufWriter, Write}; + /// + /// let mut buffer = [0u8; 10]; + /// let mut stream = BufWriter::new(buffer.as_mut()); + /// write!(stream, "too much data").unwrap(); + /// stream.flush().expect_err("it doesn't fit"); + /// let (recovered_writer, buffered_data) = stream.into_parts(); + /// assert_eq!(recovered_writer.len(), 0); + /// assert_eq!(&buffered_data.unwrap(), b"ata"); + /// ``` + #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] + pub fn into_parts(mut self) -> (W, Result, WriterPanicked>) { + let buf = mem::take(&mut self.buf); + let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; + + // SAFETY: forget(self) prevents double dropping inner + let inner = unsafe { ptr::read(&self.inner) }; + mem::forget(self); + + (inner, buf) + } +} + +impl BufWriter { /// Send data in our local buffer into the inner writer, looping as /// necessary until either it's all been sent or an error occurs. /// @@ -284,67 +347,6 @@ impl BufWriter { self.buf.capacity() } - /// Unwraps this `BufWriter`, returning the underlying writer. - /// - /// The buffer is written out before returning the writer. - /// - /// # Errors - /// - /// An [`Err`] will be returned if an error occurs while flushing the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // unwrap the TcpStream and flush the buffer - /// let stream = buffer.into_inner().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(mut self) -> Result>> { - match self.flush_buf() { - Err(e) => Err(IntoInnerError::new(self, e)), - Ok(()) => Ok(self.into_parts().0), - } - } - - /// Disassembles this `BufWriter`, returning the underlying writer, and any buffered but - /// unwritten data. - /// - /// If the underlying writer panicked, it is not known what portion of the data was written. - /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer - /// contents can still be recovered). - /// - /// `into_parts` makes no attempt to flush data and cannot fail. - /// - /// # Examples - /// - /// ``` - /// use std::io::{BufWriter, Write}; - /// - /// let mut buffer = [0u8; 10]; - /// let mut stream = BufWriter::new(buffer.as_mut()); - /// write!(stream, "too much data").unwrap(); - /// stream.flush().expect_err("it doesn't fit"); - /// let (recovered_writer, buffered_data) = stream.into_parts(); - /// assert_eq!(recovered_writer.len(), 0); - /// assert_eq!(&buffered_data.unwrap(), b"ata"); - /// ``` - #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] - pub fn into_parts(mut self) -> (W, Result, WriterPanicked>) { - let buf = mem::take(&mut self.buf); - let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; - - // SAFETY: forget(self) prevents double dropping inner - let inner = unsafe { ptr::read(&self.inner) }; - mem::forget(self); - - (inner, buf) - } - // Ensure this function does not get inlined into `write`, so that it // remains inlineable and its common path remains as short as possible. // If this function ends up being called frequently relative to `write`, @@ -511,7 +513,7 @@ impl fmt::Debug for WriterPanicked { } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for BufWriter { +impl Write for BufWriter { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { // Use < instead of <= to avoid a needless trip through the buffer in some cases. @@ -640,20 +642,20 @@ impl Write for BufWriter { } #[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for BufWriter +impl fmt::Debug for BufWriter where W: fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("BufWriter") - .field("writer", &self.inner) + .field("writer", &&self.inner) .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) .finish() } } #[stable(feature = "rust1", since = "1.0.0")] -impl Seek for BufWriter { +impl Seek for BufWriter { /// Seek to the offset, in bytes, in the underlying writer. /// /// Seeking always writes out the internal buffer before seeking. @@ -664,7 +666,7 @@ impl Seek for BufWriter { } #[stable(feature = "rust1", since = "1.0.0")] -impl Drop for BufWriter { +impl Drop for BufWriter { fn drop(&mut self) { if !self.panicked { // dtors should not panic, so we ignore a failed flush diff --git a/library/std/src/io/buffered/linewriter.rs b/library/std/src/io/buffered/linewriter.rs index a26a4ab33..3d4ae7041 100644 --- a/library/std/src/io/buffered/linewriter.rs +++ b/library/std/src/io/buffered/linewriter.rs @@ -64,7 +64,7 @@ use crate::io::{self, buffered::LineWriterShim, BufWriter, IntoInnerError, IoSli /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct LineWriter { +pub struct LineWriter { inner: BufWriter, } @@ -109,27 +109,6 @@ impl LineWriter { LineWriter { inner: BufWriter::with_capacity(capacity, inner) } } - /// Gets a reference to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::new(file); - /// - /// let reference = file.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &W { - self.inner.get_ref() - } - /// Gets a mutable reference to the underlying writer. /// /// Caution must be taken when calling methods on the mutable reference @@ -184,8 +163,31 @@ impl LineWriter { } } +impl LineWriter { + /// Gets a reference to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::new(file); + /// + /// let reference = file.get_ref(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_ref(&self) -> &W { + self.inner.get_ref() + } +} + #[stable(feature = "rust1", since = "1.0.0")] -impl Write for LineWriter { +impl Write for LineWriter { fn write(&mut self, buf: &[u8]) -> io::Result { LineWriterShim::new(&mut self.inner).write(buf) } @@ -216,7 +218,7 @@ impl Write for LineWriter { } #[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for LineWriter +impl fmt::Debug for LineWriter where W: fmt::Debug, { diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/std/src/io/buffered/linewritershim.rs index 0175d2693..f2a55da05 100644 --- a/library/std/src/io/buffered/linewritershim.rs +++ b/library/std/src/io/buffered/linewritershim.rs @@ -11,11 +11,11 @@ use crate::sys_common::memchr; /// `BufWriters` to be temporarily given line-buffering logic; this is what /// enables Stdout to be alternately in line-buffered or block-buffered mode. #[derive(Debug)] -pub struct LineWriterShim<'a, W: Write> { +pub struct LineWriterShim<'a, W: ?Sized + Write> { buffer: &'a mut BufWriter, } -impl<'a, W: Write> LineWriterShim<'a, W> { +impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { pub fn new(buffer: &'a mut BufWriter) -> Self { Self { buffer } } @@ -49,7 +49,7 @@ impl<'a, W: Write> LineWriterShim<'a, W> { } } -impl<'a, W: Write> Write for LineWriterShim<'a, W> { +impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { /// Write some data into this BufReader with line buffering. This means /// that, if any newlines are present in the data, the data up to the last /// newline is sent directly to the underlying writer, and data after it diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 56927429d..48e43a9ed 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,6 +1,9 @@ -use super::{BorrowedBuf, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; +use super::{BorrowedBuf, BufReader, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; use crate::mem::MaybeUninit; +#[cfg(test)] +mod tests; + /// Copies the entire contents of a reader into a writer. /// /// This function will continuously read data from `reader` and then @@ -65,32 +68,113 @@ where R: Read, W: Write, { - BufferedCopySpec::copy_to(reader, writer) + let read_buf = BufferedReaderSpec::buffer_size(reader); + let write_buf = BufferedWriterSpec::buffer_size(writer); + + if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf { + return BufferedReaderSpec::copy_to(reader, writer); + } + + BufferedWriterSpec::copy_from(writer, reader) +} + +/// Specialization of the read-write loop that reuses the internal +/// buffer of a BufReader. If there's no buffer then the writer side +/// should be used intead. +trait BufferedReaderSpec { + fn buffer_size(&self) -> usize; + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result; +} + +impl BufferedReaderSpec for T +where + Self: Read, + T: ?Sized, +{ + #[inline] + default fn buffer_size(&self) -> usize { + 0 + } + + default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result { + unimplemented!("only called from specializations"); + } +} + +impl BufferedReaderSpec for BufReader +where + Self: Read, + I: ?Sized, +{ + fn buffer_size(&self) -> usize { + self.capacity() + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result { + let mut len = 0; + + loop { + // Hack: this relies on `impl Read for BufReader` always calling fill_buf + // if the buffer is empty, even for empty slices. + // It can't be called directly here since specialization prevents us + // from adding I: Read + match self.read(&mut []) { + Ok(_) => {} + Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + } + let buf = self.buffer(); + if self.buffer().len() == 0 { + return Ok(len); + } + + // In case the writer side is a BufWriter then its write_all + // implements an optimization that passes through large + // buffers to the underlying writer. That code path is #[cold] + // but we're still avoiding redundant memcopies when doing + // a copy between buffered inputs and outputs. + to.write_all(buf)?; + len += buf.len() as u64; + self.discard_buffer(); + } + } } /// Specialization of the read-write loop that either uses a stack buffer /// or reuses the internal buffer of a BufWriter -trait BufferedCopySpec: Write { - fn copy_to(reader: &mut R, writer: &mut Self) -> Result; +trait BufferedWriterSpec: Write { + fn buffer_size(&self) -> usize; + + fn copy_from(&mut self, reader: &mut R) -> Result; } -impl BufferedCopySpec for W { - default fn copy_to(reader: &mut R, writer: &mut Self) -> Result { - stack_buffer_copy(reader, writer) +impl BufferedWriterSpec for W { + #[inline] + default fn buffer_size(&self) -> usize { + 0 + } + + default fn copy_from(&mut self, reader: &mut R) -> Result { + stack_buffer_copy(reader, self) } } -impl BufferedCopySpec for BufWriter { - fn copy_to(reader: &mut R, writer: &mut Self) -> Result { - if writer.capacity() < DEFAULT_BUF_SIZE { - return stack_buffer_copy(reader, writer); +impl BufferedWriterSpec for BufWriter { + fn buffer_size(&self) -> usize { + self.capacity() + } + + fn copy_from(&mut self, reader: &mut R) -> Result { + if self.capacity() < DEFAULT_BUF_SIZE { + return stack_buffer_copy(reader, self); } let mut len = 0; let mut init = 0; loop { - let buf = writer.buffer_mut(); + let buf = self.buffer_mut(); let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into(); unsafe { @@ -121,7 +205,7 @@ impl BufferedCopySpec for BufWriter { Err(e) => return Err(e), } } else { - writer.flush_buf()?; + self.flush_buf()?; init = 0; } } diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs new file mode 100644 index 000000000..8c816af15 --- /dev/null +++ b/library/std/src/io/copy/tests.rs @@ -0,0 +1,108 @@ +use crate::cmp::{max, min}; +use crate::io::*; + +#[test] +fn copy_copies() { + let mut r = repeat(0).take(4); + let mut w = sink(); + assert_eq!(copy(&mut r, &mut w).unwrap(), 4); + + let mut r = repeat(0).take(1 << 17); + assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17); +} + +struct ShortReader { + cap: usize, + read_size: usize, + observed_buffer: usize, +} + +impl Read for ShortReader { + fn read(&mut self, buf: &mut [u8]) -> Result { + let bytes = min(self.cap, self.read_size); + self.cap -= bytes; + self.observed_buffer = max(self.observed_buffer, buf.len()); + Ok(bytes) + } +} + +struct WriteObserver { + observed_buffer: usize, +} + +impl Write for WriteObserver { + fn write(&mut self, buf: &[u8]) -> Result { + self.observed_buffer = max(self.observed_buffer, buf.len()); + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +#[test] +fn copy_specializes_bufwriter() { + let cap = 117 * 1024; + let buf_sz = 16 * 1024; + let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; + let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 }); + assert_eq!( + copy(&mut r, &mut w).unwrap(), + cap as u64, + "expected the whole capacity to be copied" + ); + assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader"); + assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes"); +} + +#[test] +fn copy_specializes_bufreader() { + let mut source = vec![0; 768 * 1024]; + source[1] = 42; + let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source)); + + let mut sink = Vec::new(); + assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); + assert_eq!(source.as_slice(), sink.as_slice()); + + let buf_sz = 71 * 1024; + assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition"); + + let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source)); + let mut sink = WriteObserver { observed_buffer: 0 }; + assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64); + assert_eq!( + sink.observed_buffer, buf_sz, + "expected a large buffer to be provided to the writer" + ); +} + +#[cfg(unix)] +mod io_benches { + use crate::fs::File; + use crate::fs::OpenOptions; + use crate::io::prelude::*; + use crate::io::BufReader; + + use test::Bencher; + + #[bench] + 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 + let dyn_in = &mut file_in as &mut dyn Read; + let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0)); + let mut writer = + OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed"); + + const BYTES: u64 = 1024 * 1024; + + b.bytes = BYTES; + + b.iter(|| { + reader.get_mut().set_limit(BYTES); + crate::io::copy(&mut reader, &mut writer).unwrap() + }); + } +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 8a007d095..71d91f213 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1416,17 +1416,18 @@ pub trait Write { /// /// This function will attempt to write the entire contents of `buf`, but /// the entire write might not succeed, or the write may also generate an - /// error. A call to `write` represents *at most one* attempt to write to + /// error. Typically, a call to `write` represents one attempt to write to /// any wrapped object. /// /// Calls to `write` are not guaranteed to block waiting for data to be /// written, and a write which would otherwise block can be indicated through /// an [`Err`] variant. /// - /// If the return value is [`Ok(n)`] then it must be guaranteed that - /// `n <= buf.len()`. A return value of `0` typically means that the - /// underlying object is no longer able to accept bytes and will likely not - /// be able to in the future as well, or that the buffer provided is empty. + /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. + /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. + /// A return value of `Ok(0)` typically means that the underlying object is + /// no longer able to accept bytes and will likely not be able to in the + /// future as well, or that the buffer provided is empty. /// /// # Errors /// @@ -2754,7 +2755,7 @@ trait SizeHint { } } -impl SizeHint for T { +impl SizeHint for T { #[inline] default fn lower_bound(&self) -> usize { 0 diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index ce5e2c9da..1baa94e64 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,67 +1,8 @@ -use crate::cmp::{max, min}; use crate::io::prelude::*; -use crate::io::{ - copy, empty, repeat, sink, BorrowedBuf, BufWriter, Empty, Repeat, Result, SeekFrom, Sink, - DEFAULT_BUF_SIZE, -}; +use crate::io::{empty, repeat, sink, BorrowedBuf, Empty, Repeat, SeekFrom, Sink}; use crate::mem::MaybeUninit; -#[test] -fn copy_copies() { - let mut r = repeat(0).take(4); - let mut w = sink(); - assert_eq!(copy(&mut r, &mut w).unwrap(), 4); - - let mut r = repeat(0).take(1 << 17); - assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17); -} - -struct ShortReader { - cap: usize, - read_size: usize, - observed_buffer: usize, -} - -impl Read for ShortReader { - fn read(&mut self, buf: &mut [u8]) -> Result { - let bytes = min(self.cap, self.read_size); - self.cap -= bytes; - self.observed_buffer = max(self.observed_buffer, buf.len()); - Ok(bytes) - } -} - -struct WriteObserver { - observed_buffer: usize, -} - -impl Write for WriteObserver { - fn write(&mut self, buf: &[u8]) -> Result { - self.observed_buffer = max(self.observed_buffer, buf.len()); - Ok(buf.len()) - } - - fn flush(&mut self) -> Result<()> { - Ok(()) - } -} - -#[test] -fn copy_specializes_bufwriter() { - let cap = 117 * 1024; - let buf_sz = 16 * 1024; - let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; - let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 }); - assert_eq!( - copy(&mut r, &mut w).unwrap(), - cap as u64, - "expected the whole capacity to be copied" - ); - assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader"); - assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes"); -} - #[test] fn sink_sinks() { let mut s = sink(); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 03866ae4c..1af344254 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -188,6 +188,13 @@ //! [array]: prim@array //! [slice]: prim@slice +// To run std tests without x.py without ending up with two copies of std, Miri needs to be +// able to "empty" this crate. See . +// rustc itself never sets the feature, so this line has no affect there. +#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] +// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies. +#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))] +// #![cfg_attr(not(feature = "restricted-std"), stable(feature = "rust1", since = "1.0.0"))] #![cfg_attr(feature = "restricted-std", unstable(feature = "restricted_std", issue = "none"))] #![doc( @@ -202,12 +209,6 @@ no_global_oom_handling, not(no_global_oom_handling) ))] -// To run std tests without x.py without ending up with two copies of std, Miri needs to be -// able to "empty" this crate. See . -// rustc itself never sets the feature, so this line has no affect there. -#![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] -// miri-test-libstd also prefers to make std use the sysroot versions of the dependencies. -#![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))] // Don't link to std. We are std. #![no_std] // Tell the compiler to link to either panic_abort or panic_unwind diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index fcc5cfafd..ba1b8cbfa 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -154,7 +154,7 @@ macro_rules! println { /// /// Panics if writing to `io::stderr` fails. /// -/// Writing to non-blocking stdout can cause an error, which will lead +/// Writing to non-blocking stderr can cause an error, which will lead /// this macro to panic. /// /// # Examples @@ -189,7 +189,7 @@ macro_rules! eprint { /// /// Panics if writing to `io::stderr` fails. /// -/// Writing to non-blocking stdout can cause an error, which will lead +/// Writing to non-blocking stderr can cause an error, which will lead /// this macro to panic. /// /// # Examples diff --git a/library/std/src/net/socket_addr/tests.rs b/library/std/src/net/socket_addr/tests.rs index dfc6dabbe..6a065cfba 100644 --- a/library/std/src/net/socket_addr/tests.rs +++ b/library/std/src/net/socket_addr/tests.rs @@ -85,7 +85,7 @@ fn ipv6_socket_addr_to_string() { // IPv4-compatible address. assert_eq!( SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(), - "[::192.0.2.128]:8080" + "[::c000:280]:8080" ); // IPv6 address with no zero segments. diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 7a3c66e45..db367cfa0 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -46,6 +46,17 @@ fn connect_error() { } } +#[test] +#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 +fn connect_timeout_error() { + let socket_addr = next_test_ip4(); + let result = TcpStream::connect_timeout(&socket_addr, Duration::MAX); + assert!(!matches!(result, Err(e) if e.kind() == ErrorKind::TimedOut)); + + let _listener = TcpListener::bind(&socket_addr).unwrap(); + assert!(TcpStream::connect_timeout(&socket_addr, Duration::MAX).is_ok()); +} + #[test] fn listen_localhost() { let socket_addr = next_test_ip4(); diff --git a/library/std/src/os/ios/fs.rs b/library/std/src/os/ios/fs.rs index 6d4d54b7c..b319527a5 100644 --- a/library/std/src/os/ios/fs.rs +++ b/library/std/src/os/ios/fs.rs @@ -6,7 +6,7 @@ use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; #[allow(deprecated)] -use crate::os::ios::raw; +use super::raw; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 5b54cc5f2..634c3cc4a 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -137,6 +137,9 @@ pub mod redox; pub mod solaris; #[cfg(target_os = "solid_asp3")] pub mod solid; +#[cfg(target_os = "tvos")] +#[path = "ios/mod.rs"] +pub(crate) mod tvos; #[cfg(target_os = "vita")] pub mod vita; #[cfg(target_os = "vxworks")] diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index ebe94935c..48c6df31f 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -87,6 +87,8 @@ mod platform { pub use crate::os::redox::*; #[cfg(target_os = "solaris")] pub use crate::os::solaris::*; + #[cfg(target_os = "tvos")] + pub use crate::os::tvos::*; #[cfg(target_os = "vita")] pub use crate::os::vita::*; #[cfg(target_os = "vxworks")] @@ -111,6 +113,7 @@ pub mod thread; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "macos", target_os = "netbsd", diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index f9cca7cb4..b8ea0c66c 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -11,12 +11,19 @@ use crate::slice::from_raw_parts; use crate::sys::net::Socket; // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? -#[cfg(all(doc, not(target_os = "linux"), not(target_os = "android"), not(target_os = "netbsd")))] +#[cfg(all( + doc, + not(target_os = "linux"), + not(target_os = "android"), + not(target_os = "netbsd"), + not(target_os = "freebsd") +))] #[allow(non_camel_case_types)] mod libc { pub use libc::c_int; pub struct ucred; pub struct cmsghdr; + pub struct sockcred2; pub type pid_t = i32; pub type gid_t = u32; pub type uid_t = u32; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 0ee010bf6..549c4e914 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -11,6 +11,7 @@ use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, Owned target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "netbsd", @@ -30,6 +31,7 @@ use crate::time::Duration; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "netbsd", @@ -244,6 +246,7 @@ impl UnixStream { target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "netbsd", diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 729c63d18..2b40b672d 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -15,7 +15,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use cfg_if::cfg_if; cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] { + if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { type UserId = u16; type GroupId = u16; } else if #[cfg(target_os = "nto")] { diff --git a/library/std/src/os/unix/ucred.rs b/library/std/src/os/unix/ucred.rs index a4a410c12..20f4f01b5 100644 --- a/library/std/src/os/unix/ucred.rs +++ b/library/std/src/os/unix/ucred.rs @@ -55,7 +55,7 @@ pub use self::impl_linux::peer_cred; #[cfg(not(target_family = "postgres"))] pub use self::impl_bsd::peer_cred; -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] #[cfg(not(target_family = "postgres"))] pub use self::impl_mac::peer_cred; @@ -124,7 +124,7 @@ pub mod impl_bsd { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] #[cfg(not(target_family = "postgres"))] pub mod impl_mac { use super::UCred; diff --git a/library/std/src/os/unix/ucred/tests.rs b/library/std/src/os/unix/ucred/tests.rs index e63a2fc24..dd99ecdd8 100644 --- a/library/std/src/os/unix/ucred/tests.rs +++ b/library/std/src/os/unix/ucred/tests.rs @@ -8,6 +8,7 @@ use libc::{getegid, geteuid, getpid}; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "openbsd" @@ -26,7 +27,13 @@ fn test_socket_pair() { } #[test] -#[cfg(any(target_os = "linux", target_os = "ios", target_os = "macos", target_os = "watchos"))] +#[cfg(any( + target_os = "linux", + target_os = "ios", + target_os = "macos", + target_os = "watchos", + target_os = "tvos", +))] fn test_socket_pair_pids(arg: Type) -> RetType { // Create two connected sockets and get their peer credentials. let (sock_a, sock_b) = UnixStream::pair().unwrap(); diff --git a/library/std/src/path.rs b/library/std/src/path.rs index febdeb514..28cd3c4e4 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -193,7 +193,7 @@ impl<'a> Prefix<'a> { fn len(&self) -> usize { use self::Prefix::*; fn os_str_len(s: &OsStr) -> usize { - s.bytes().len() + s.as_os_str_bytes().len() } match *self { Verbatim(x) => 4 + os_str_len(x), @@ -299,20 +299,6 @@ where } } -unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { - // SAFETY: See note at the top of this module to understand why this and - // `OsStr::bytes` are used: - // - // This casts are safe as OsStr is internally a wrapper around [u8] on all - // platforms. - // - // Note that currently this relies on the special knowledge that std has; - // these types are single-element structs but are not marked - // repr(transparent) or repr(C) which would make these casts not allowable - // outside std. - unsafe { &*(s as *const [u8] as *const OsStr) } -} - // Detect scheme on Redox fn has_redox_scheme(s: &[u8]) -> bool { cfg!(target_os = "redox") && s.contains(&b':') @@ -330,7 +316,7 @@ fn has_physical_root(s: &[u8], prefix: Option>) -> bool { // basic workhorse for splitting stem and extension fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { - if file.bytes() == b".." { + if file.as_os_str_bytes() == b".." { return (Some(file), None); } @@ -338,18 +324,23 @@ fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { // and back. This is safe to do because (1) we only look at ASCII // contents of the encoding and (2) new &OsStr values are produced // only from ASCII-bounded slices of existing &OsStr values. - let mut iter = file.bytes().rsplitn(2, |b| *b == b'.'); + let mut iter = file.as_os_str_bytes().rsplitn(2, |b| *b == b'.'); let after = iter.next(); let before = iter.next(); if before == Some(b"") { (Some(file), None) } else { - unsafe { (before.map(|s| u8_slice_as_os_str(s)), after.map(|s| u8_slice_as_os_str(s))) } + unsafe { + ( + before.map(|s| OsStr::from_os_str_bytes_unchecked(s)), + after.map(|s| OsStr::from_os_str_bytes_unchecked(s)), + ) + } } } fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { - let slice = file.bytes(); + let slice = file.as_os_str_bytes(); if slice == b".." { return (file, None); } @@ -364,7 +355,12 @@ fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) { }; let before = &slice[..i]; let after = &slice[i + 1..]; - unsafe { (u8_slice_as_os_str(before), Some(u8_slice_as_os_str(after))) } + unsafe { + ( + OsStr::from_os_str_bytes_unchecked(before), + Some(OsStr::from_os_str_bytes_unchecked(after)), + ) + } } //////////////////////////////////////////////////////////////////////////////// @@ -743,7 +739,7 @@ impl<'a> Components<'a> { // separately via `include_cur_dir` b".." => Some(Component::ParentDir), b"" => None, - _ => Some(Component::Normal(unsafe { u8_slice_as_os_str(comp) })), + _ => Some(Component::Normal(unsafe { OsStr::from_os_str_bytes_unchecked(comp) })), } } @@ -900,7 +896,7 @@ impl<'a> Iterator for Components<'a> { let raw = &self.path[..self.prefix_len()]; self.path = &self.path[self.prefix_len()..]; return Some(Component::Prefix(PrefixComponent { - raw: unsafe { u8_slice_as_os_str(raw) }, + raw: unsafe { OsStr::from_os_str_bytes_unchecked(raw) }, parsed: self.prefix.unwrap(), })); } @@ -972,7 +968,7 @@ impl<'a> DoubleEndedIterator for Components<'a> { State::Prefix if self.prefix_len() > 0 => { self.back = State::Done; return Some(Component::Prefix(PrefixComponent { - raw: unsafe { u8_slice_as_os_str(self.path) }, + raw: unsafe { OsStr::from_os_str_bytes_unchecked(self.path) }, parsed: self.prefix.unwrap(), })); } @@ -1481,17 +1477,17 @@ impl PathBuf { fn _set_extension(&mut self, extension: &OsStr) -> bool { let file_stem = match self.file_stem() { None => return false, - Some(f) => f.bytes(), + Some(f) => f.as_os_str_bytes(), }; // truncate until right after the file stem let end_file_stem = file_stem[file_stem.len()..].as_ptr().addr(); - let start = self.inner.bytes().as_ptr().addr(); + let start = self.inner.as_os_str_bytes().as_ptr().addr(); let v = self.as_mut_vec(); v.truncate(end_file_stem.wrapping_sub(start)); // add the new extension, if any - let new = extension.bytes(); + let new = extension.as_os_str_bytes(); if !new.is_empty() { v.reserve_exact(new.len() + 1); v.push(b'.'); @@ -2011,11 +2007,11 @@ impl Path { // The following (private!) function allows construction of a path from a u8 // slice, which is only safe when it is known to follow the OsStr encoding. unsafe fn from_u8_slice(s: &[u8]) -> &Path { - unsafe { Path::new(u8_slice_as_os_str(s)) } + unsafe { Path::new(OsStr::from_os_str_bytes_unchecked(s)) } } // The following (private!) function reveals the byte encoding used for OsStr. fn as_u8_slice(&self) -> &[u8] { - self.inner.bytes() + self.inner.as_os_str_bytes() } /// Directly wraps a string slice as a `Path` slice. diff --git a/library/std/src/personality.rs b/library/std/src/personality.rs index 63f0ad4f1..386a399f5 100644 --- a/library/std/src/personality.rs +++ b/library/std/src/personality.rs @@ -29,7 +29,7 @@ cfg_if::cfg_if! { all(target_family = "windows", target_env = "gnu"), target_os = "psp", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs index 82edb11cb..6552d96ca 100644 --- a/library/std/src/personality/gcc.rs +++ b/library/std/src/personality/gcc.rs @@ -85,7 +85,7 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c cfg_if::cfg_if! { - if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "watchos"), not(target_os = "netbsd")))] { + if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "netbsd")))] { // ARM EHABI personality routine. // https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf // diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 8266e8990..80289ca08 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -308,7 +308,7 @@ mod prim_never {} /// /// ```no_run /// // Undefined behaviour -/// unsafe { char::from_u32_unchecked(0x110000) }; +/// let _ = unsafe { char::from_u32_unchecked(0x110000) }; /// ``` /// /// USVs are also the exact set of values that may be encoded in UTF-8. Because diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 9da74a5dd..8f3201b00 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1904,8 +1904,8 @@ impl FromInner for ExitCode { } impl Child { - /// Forces the child process to exit. If the child has already exited, an [`InvalidInput`] - /// error is returned. + /// Forces the child process to exit. If the child has already exited, `Ok(())` + /// is returned. /// /// The mapping to [`ErrorKind`]s is not part of the compatibility contract of the function. /// @@ -1920,7 +1920,7 @@ impl Child { /// /// let mut command = Command::new("yes"); /// if let Ok(mut child) = command.spawn() { - /// child.kill().expect("command wasn't running"); + /// child.kill().expect("command couldn't be killed"); /// } else { /// println!("yes command didn't start"); /// } diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index d7f4d335d..366b59146 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -582,3 +582,18 @@ fn run_canonical_bat_script() { assert!(output.status.success()); assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!"); } + +#[test] +fn terminate_exited_process() { + let mut cmd = if cfg!(target_os = "android") { + let mut p = shell_cmd(); + p.args(&["-c", "true"]); + p + } else { + known_command() + }; + let mut p = cmd.stdout(Stdio::null()).spawn().unwrap(); + p.wait().unwrap(); + assert!(p.kill().is_ok()); + assert!(p.kill().is_ok()); +} diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 11836b7b6..e39254aa4 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -13,9 +13,10 @@ use crate::sync::{Condvar, Mutex}; /// use std::sync::{Arc, Barrier}; /// use std::thread; /// -/// let mut handles = Vec::with_capacity(10); -/// let barrier = Arc::new(Barrier::new(10)); -/// for _ in 0..10 { +/// let n = 10; +/// 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. @@ -105,9 +106,10 @@ impl Barrier { /// use std::sync::{Arc, Barrier}; /// use std::thread; /// - /// let mut handles = Vec::with_capacity(10); - /// let barrier = Arc::new(Barrier::new(10)); - /// for _ in 0..10 { + /// let n = 10; + /// 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. diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs index 4912ca4f8..9aab1b941 100644 --- a/library/std/src/sync/mpmc/waker.rs +++ b/library/std/src/sync/mpmc/waker.rs @@ -66,26 +66,32 @@ impl Waker { /// Attempts to find another thread's entry, select the operation, and wake it up. #[inline] pub(crate) fn try_select(&mut self) -> Option { - self.selectors - .iter() - .position(|selector| { - // Does the entry belong to a different thread? - selector.cx.thread_id() != current_thread_id() - && selector // Try selecting this operation. - .cx - .try_select(Selected::Operation(selector.oper)) - .is_ok() - && { - // Provide the packet. - selector.cx.store_packet(selector.packet); - // Wake the thread up. - selector.cx.unpark(); - true - } - }) - // Remove the entry from the queue to keep it clean and improve - // performance. - .map(|pos| self.selectors.remove(pos)) + if self.selectors.is_empty() { + None + } else { + let thread_id = current_thread_id(); + + self.selectors + .iter() + .position(|selector| { + // Does the entry belong to a different thread? + selector.cx.thread_id() != thread_id + && selector // Try selecting this operation. + .cx + .try_select(Selected::Operation(selector.oper)) + .is_ok() + && { + // Provide the packet. + selector.cx.store_packet(selector.packet); + // Wake the thread up. + selector.cx.unpark(); + true + } + }) + // Remove the entry from the queue to keep it clean and improve + // performance. + .map(|pos| self.selectors.remove(pos)) + } } /// Notifies all operations waiting to be ready. diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index 0e0c87d1c..c00134c8b 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -347,8 +347,8 @@ pub struct Sender { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for Sender {} -#[stable(feature = "rust1", since = "1.0.0")] -impl !Sync for Sender {} +#[stable(feature = "mpsc_sender_sync", since = "1.72.0")] +unsafe impl Sync for Sender {} /// The sending-half of Rust's synchronous [`sync_channel`] type. /// diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 1b17c3108..8c46080e4 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -91,7 +91,7 @@ impl Once { /// return). /// /// If the given closure recursively invokes `call_once` on the same [`Once`] - /// instance the exact behavior is not specified, allowed outcomes are + /// instance, the exact behavior is not specified: allowed outcomes are /// a panic or a deadlock. /// /// # Examples diff --git a/library/std/src/sys/common/small_c_string.rs b/library/std/src/sys/common/small_c_string.rs index 01acd5191..963d17a47 100644 --- a/library/std/src/sys/common/small_c_string.rs +++ b/library/std/src/sys/common/small_c_string.rs @@ -19,7 +19,7 @@ pub fn run_path_with_cstr(path: &Path, f: F) -> io::Result where F: FnOnce(&CStr) -> io::Result, { - run_with_cstr(path.as_os_str().bytes(), f) + run_with_cstr(path.as_os_str().as_os_str_bytes(), f) } #[inline] diff --git a/library/std/src/sys/common/tests.rs b/library/std/src/sys/common/tests.rs index fb6f5d6af..0a1cbcbe8 100644 --- a/library/std/src/sys/common/tests.rs +++ b/library/std/src/sys/common/tests.rs @@ -8,7 +8,7 @@ use core::iter::repeat; fn stack_allocation_works() { let path = Path::new("abc"); let result = run_path_with_cstr(path, |p| { - assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap()); + assert_eq!(p, &*CString::new(path.as_os_str().as_os_str_bytes()).unwrap()); Ok(42) }); assert_eq!(result.unwrap(), 42); @@ -25,7 +25,7 @@ fn heap_allocation_works() { let path = repeat("a").take(384).collect::(); let path = Path::new(&path); let result = run_path_with_cstr(path, |p| { - assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap()); + assert_eq!(p, &*CString::new(path.as_os_str().as_os_str_bytes()).unwrap()); Ok(42) }); assert_eq!(result.unwrap(), 42); diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs index 447044a79..bc5da1a18 100644 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -33,20 +33,21 @@ pub macro thread_local_inner { // 1 == dtor registered, dtor not run // 2 == dtor registered and is running or has run #[thread_local] - static mut STATE: $crate::primitive::u8 = 0; + static STATE: $crate::cell::Cell<$crate::primitive::u8> = $crate::cell::Cell::new(0); + // Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires + // all that comes with it. unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) { - let ptr = ptr as *mut $t; - - unsafe { - $crate::debug_assert_eq!(STATE, 1); - STATE = 2; - $crate::ptr::drop_in_place(ptr); - } + $crate::thread::local_impl::abort_on_dtor_unwind(|| { + let old_state = STATE.replace(2); + $crate::debug_assert_eq!(old_state, 1); + // Safety: safety requirement is passed on to caller. + unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); } + }); } unsafe { - match STATE { + match STATE.get() { // 0 == we haven't registered a destructor, so do // so now. 0 => { @@ -54,7 +55,7 @@ pub macro thread_local_inner { $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8, destroy, ); - STATE = 1; + STATE.set(1); $crate::option::Option::Some(&VAL) } // 1 == the destructor is registered and the value @@ -148,7 +149,6 @@ impl fmt::Debug for Key { f.debug_struct("Key").finish_non_exhaustive() } } - impl Key { pub const fn new() -> Key { Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs index 77f645883..975509bd4 100644 --- a/library/std/src/sys/common/thread_local/mod.rs +++ b/library/std/src/sys/common/thread_local/mod.rs @@ -101,3 +101,24 @@ mod lazy { } } } + +/// Run a callback in a scenario which must not unwind (such as a `extern "C" +/// fn` declared in a user crate). If the callback unwinds anyway, then +/// `rtabort` with a message about thread local panicking on drop. +#[inline] +pub fn abort_on_dtor_unwind(f: impl FnOnce()) { + // Using a guard like this is lower cost. + let guard = DtorUnwindGuard; + f(); + core::mem::forget(guard); + + struct DtorUnwindGuard; + impl Drop for DtorUnwindGuard { + #[inline] + fn drop(&mut self) { + // This is not terribly descriptive, but it doesn't need to be as we'll + // already have printed a panic message at this point. + rtabort!("thread local panicked on drop"); + } + } +} diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 9ed4d9c1e..eafd6821f 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -168,7 +168,7 @@ mod imp { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] mod imp { use super::Args; use crate::ffi::CStr; @@ -209,7 +209,7 @@ mod imp { // for i in (0..[args count]) // res.push([args objectAtIndex:i]) // res - #[cfg(any(target_os = "ios", target_os = "watchos"))] + #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub fn args() -> Args { use crate::ffi::OsString; use crate::mem; diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index 8c3ef88d8..929e9dae7 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -31,6 +31,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "tvos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "tvos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "watchos")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index cb630eede..85e020ae4 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -44,6 +44,7 @@ const READ_LIMIT: usize = libc::ssize_t::MAX as usize; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "macos", target_os = "netbsd", target_os = "openbsd", @@ -69,6 +70,7 @@ const fn max_iov() -> usize { target_os = "emscripten", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "netbsd", @@ -181,6 +183,7 @@ impl FileDesc { target_os = "fuchsia", target_os = "illumos", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "netbsd", @@ -222,6 +225,7 @@ impl FileDesc { #[cfg(any( all(target_os = "android", target_pointer_width = "32"), target_os = "ios", + target_os = "tvos", target_os = "macos", ))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { @@ -320,6 +324,7 @@ impl FileDesc { target_os = "fuchsia", target_os = "illumos", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "netbsd", @@ -361,6 +366,7 @@ impl FileDesc { #[cfg(any( all(target_os = "android", target_pointer_width = "32"), target_os = "ios", + target_os = "tvos", target_os = "macos", ))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { @@ -402,7 +408,10 @@ impl FileDesc { } } #[cfg(any( - all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))), + all( + target_env = "newlib", + not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")) + ), target_os = "solaris", target_os = "illumos", target_os = "emscripten", @@ -424,10 +433,10 @@ impl FileDesc { Ok(()) } } - #[cfg(any(target_os = "espidf", target_os = "horizon"))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] pub fn set_cloexec(&self) -> io::Result<()> { - // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to, - // because neither supports spawning processes. + // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to, + // because none of them supports spawning processes. Ok(()) } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 09e9ae272..fbc7f04ce 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -15,6 +15,7 @@ use crate::mem; target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] use crate::mem::MaybeUninit; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; @@ -31,6 +32,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; all(target_os = "linux", target_env = "gnu"), target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", ))] use crate::sys::weak::syscall; @@ -42,6 +44,7 @@ use libc::{c_int, mode_t}; #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "solaris", all(target_os = "linux", target_env = "gnu") @@ -58,6 +61,7 @@ use libc::fstatat64; target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] use libc::readdir as readdir64; #[cfg(target_os = "linux")] @@ -74,6 +78,7 @@ use libc::readdir64_r; target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] use libc::readdir_r as readdir64_r; #[cfg(target_os = "android")] @@ -283,6 +288,7 @@ unsafe impl Sync for Dir {} target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita" ))] pub struct DirEntry { dir: Arc, @@ -304,10 +310,16 @@ pub struct DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", ))] struct dirent64_min { d_ino: u64, - #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "nto")))] + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "nto", + target_os = "vita" + )))] d_type: u8, } @@ -319,6 +331,7 @@ struct dirent64_min { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] pub struct DirEntry { dir: Arc, @@ -349,7 +362,7 @@ pub struct FilePermissions { pub struct FileTimes { accessed: Option, modified: Option, - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] created: Option, } @@ -508,6 +521,7 @@ impl FileAttr { target_os = "openbsd", target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", ))] pub fn created(&self) -> io::Result { @@ -519,7 +533,9 @@ impl FileAttr { target_os = "openbsd", target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", + target_os = "vita", )))] pub fn created(&self) -> io::Result { cfg_has_statx! { @@ -541,6 +557,11 @@ impl FileAttr { currently", )) } + + #[cfg(target_os = "vita")] + pub fn created(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_ctime as i64, 0)) + } } #[cfg(target_os = "nto")] @@ -594,7 +615,7 @@ impl FileTimes { self.modified = Some(t); } - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))] pub fn set_created(&mut self, t: SystemTime) { self.created = Some(t); } @@ -645,6 +666,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -725,6 +747,7 @@ impl Iterator for ReadDir { continue; } + #[cfg(not(target_os = "vita"))] let entry = dirent64_min { d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, #[cfg(not(any( @@ -735,6 +758,9 @@ impl Iterator for ReadDir { d_type: *offset_ptr!(entry_ptr, d_type) as u8, }; + #[cfg(target_os = "vita")] + let entry = dirent64_min { d_ino: 0u64 }; + return Some(Ok(DirEntry { entry, name: name.to_owned(), @@ -752,6 +778,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", )))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -842,6 +869,7 @@ impl DirEntry { target_os = "haiku", target_os = "vxworks", target_os = "nto", + target_os = "vita", ))] pub fn file_type(&self) -> io::Result { self.metadata().map(|m| m.file_type()) @@ -853,6 +881,7 @@ impl DirEntry { target_os = "haiku", target_os = "vxworks", target_os = "nto", + target_os = "vita", )))] pub fn file_type(&self) -> io::Result { match self.entry.d_type { @@ -870,6 +899,7 @@ impl DirEntry { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "linux", target_os = "emscripten", @@ -903,6 +933,7 @@ impl DirEntry { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "netbsd", target_os = "openbsd", @@ -921,6 +952,7 @@ impl DirEntry { #[cfg(not(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "netbsd", target_os = "openbsd", @@ -939,6 +971,7 @@ impl DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } @@ -951,6 +984,7 @@ impl DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", ))] fn name_cstr(&self) -> &CStr { &self.name @@ -1080,11 +1114,21 @@ impl File { cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?; return Ok(()); - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + ))] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) } - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "watchos")))] + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + )))] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fsync(fd) } @@ -1094,7 +1138,12 @@ impl File { cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; return Ok(()); - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + ))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) } @@ -1113,6 +1162,7 @@ impl File { target_os = "android", target_os = "freebsd", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "netbsd", @@ -1222,7 +1272,7 @@ impl File { io::ErrorKind::Unsupported, "setting file times not supported", )) - } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] { + } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] { let mut buf = [mem::MaybeUninit::::uninit(); 3]; let mut num_times = 0; let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; @@ -1543,7 +1593,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { run_path_with_cstr(original, |original| { run_path_with_cstr(link, |link| { cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves // it implementation-defined whether `link` follows symlinks, so rely on the // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. @@ -1666,6 +1716,8 @@ fn open_to_and_set_permissions( .truncate(true) .open(to)?; let writer_metadata = writer.metadata()?; + // fchmod is broken on vita + #[cfg(not(target_os = "vita"))] if writer_metadata.is_file() { // Set the correct file permissions, in case the file already existed. // Don't set the permissions on already existing non-files like @@ -1680,6 +1732,7 @@ fn open_to_and_set_permissions( target_os = "android", target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", )))] pub fn copy(from: &Path, to: &Path) -> io::Result { @@ -1707,7 +1760,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { } } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub fn copy(from: &Path, to: &Path) -> io::Result { use crate::sync::atomic::{AtomicBool, Ordering}; @@ -1844,11 +1897,12 @@ pub fn chroot(dir: &Path) -> io::Result<()> { pub use remove_dir_impl::remove_dir_all; -// Fallback for REDOX, ESP-ID, Horizon, and Miri +// Fallback for REDOX, ESP-ID, Horizon, Vita and Miri #[cfg(any( target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "vita", target_os = "nto", miri ))] @@ -1861,6 +1915,7 @@ mod remove_dir_impl { target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "vita", target_os = "nto", miri )))] diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 16c8e0c0e..7d49bbdcb 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -466,7 +466,7 @@ impl CopyRead for Take { } } -impl CopyRead for BufReader { +impl CopyRead for BufReader { fn drain_to(&mut self, writer: &mut W, outer_limit: u64) -> Result { let buf = self.buffer(); let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))]; @@ -495,7 +495,7 @@ impl CopyRead for BufReader { } } -impl CopyWrite for BufWriter { +impl CopyWrite for BufWriter { fn properties(&self) -> CopyParams { self.get_ref().properties() } diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs index ee016887e..fe9559f2a 100644 --- a/library/std/src/sys/unix/l4re.rs +++ b/library/std/src/sys/unix/l4re.rs @@ -10,7 +10,7 @@ macro_rules! unimpl { pub mod net { #![allow(warnings)] use crate::fmt; - use crate::io::{self, IoSlice, IoSliceMut}; + 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; @@ -218,6 +218,10 @@ pub mod net { unimpl!(); } + pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { + unimpl!(); + } + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { unimpl!(); } diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs index 192fa216d..2dc1b0c60 100644 --- a/library/std/src/sys/unix/locks/pthread_condvar.rs +++ b/library/std/src/sys/unix/locks/pthread_condvar.rs @@ -32,6 +32,7 @@ impl LazyInit for AllocatedCondvar { if #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "l4re", target_os = "android", @@ -124,6 +125,7 @@ impl Condvar { #[cfg(not(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "android", target_os = "espidf", @@ -158,6 +160,7 @@ impl Condvar { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "android", target_os = "espidf", diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index bb9e65e68..326f1481e 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -88,6 +88,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // The poll on Darwin doesn't set POLLNVAL for closed fds. target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "redox", target_os = "l4re", @@ -164,12 +165,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { - #[cfg(not(any( - target_os = "emscripten", - target_os = "fuchsia", - target_os = "horizon", - target_os = "vita" - )))] + #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] { // We don't want to add this as a public type to std, nor do we // want to `include!` a file from the compiler (which would break @@ -207,7 +203,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", - target_os = "vita" )))] static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = crate::sync::atomic::AtomicBool::new(false); @@ -217,7 +212,6 @@ static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", - target_os = "vita", )))] pub(crate) fn unix_sigpipe_attr_specified() -> bool { UNIX_SIGPIPE_ATTR_SPECIFIED.load(crate::sync::atomic::Ordering::Relaxed) @@ -395,7 +389,7 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "macos")] { #[link(name = "System")] extern "C" {} - } else if #[cfg(any(target_os = "ios", target_os = "watchos"))] { + } else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] { #[link(name = "System")] #[link(name = "objc")] #[link(name = "Security", kind = "framework")] @@ -408,6 +402,9 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_os = "linux", target_env = "uclibc"))] { #[link(name = "dl")] extern "C" {} + } else if #[cfg(target_os = "vita")] { + #[link(name = "pthread", kind = "static", modifiers = "-bundle")] + extern "C" {} } } diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 39edb136c..7258c222a 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -454,12 +454,18 @@ impl Socket { Ok(passcred != 0) } - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as libc::c_int; cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) } + #[cfg(target_os = "vita")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let option = nonblocking as libc::c_int; + setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) + } + #[cfg(any(target_os = "solaris", target_os = "illumos"))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { // FIONBIO is inadequate for sockets on illumos/Solaris, so use the diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 8edfd3313..a68c14758 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -63,7 +63,13 @@ extern "C" { #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")] #[cfg_attr( - any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "watchos"), + any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "freebsd", + target_os = "watchos" + ), link_name = "__error" )] #[cfg_attr(target_os = "haiku", link_name = "_errnop")] @@ -375,7 +381,7 @@ pub fn current_exe() -> io::Result { Ok(PathBuf::from(OsString::from_vec(e))) } -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub fn current_exe() -> io::Result { unsafe { let mut sz: u32 = 0; @@ -609,6 +615,7 @@ pub fn home_dir() -> Option { #[cfg(any( target_os = "android", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "emscripten", target_os = "redox", @@ -623,6 +630,7 @@ pub fn home_dir() -> Option { #[cfg(not(any( target_os = "android", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "emscripten", target_os = "redox", diff --git a/library/std/src/sys/unix/os_str.rs b/library/std/src/sys/unix/os_str.rs index 488217f39..f7333fd5a 100644 --- a/library/std/src/sys/unix/os_str.rs +++ b/library/std/src/sys/unix/os_str.rs @@ -193,17 +193,22 @@ impl Buf { impl Slice { #[inline] - fn from_u8_slice(s: &[u8]) -> &Slice { + pub fn as_os_str_bytes(&self) -> &[u8] { + &self.inner + } + + #[inline] + pub unsafe fn from_os_str_bytes_unchecked(s: &[u8]) -> &Slice { unsafe { mem::transmute(s) } } #[inline] pub fn from_str(s: &str) -> &Slice { - Slice::from_u8_slice(s.as_bytes()) + unsafe { Slice::from_os_str_bytes_unchecked(s.as_bytes()) } } - pub fn to_str(&self) -> Option<&str> { - str::from_utf8(&self.inner).ok() + pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { + str::from_utf8(&self.inner) } pub fn to_string_lossy(&self) -> Cow<'_, str> { diff --git a/library/std/src/sys/unix/os_str/tests.rs b/library/std/src/sys/unix/os_str/tests.rs index 22ba0c923..91bc0e61a 100644 --- a/library/std/src/sys/unix/os_str/tests.rs +++ b/library/std/src/sys/unix/os_str/tests.rs @@ -2,7 +2,7 @@ use super::*; #[test] fn slice_debug_output() { - let input = Slice::from_u8_slice(b"\xF0hello,\tworld"); + let input = unsafe { Slice::from_os_str_bytes_unchecked(b"\xF0hello,\tworld") }; let expected = r#""\xF0hello,\tworld""#; let output = format!("{input:?}"); @@ -11,8 +11,7 @@ fn slice_debug_output() { #[test] fn display() { - assert_eq!( - "Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", - Slice::from_u8_slice(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string(), - ); + assert_eq!("Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", unsafe { + Slice::from_os_str_bytes_unchecked(b"Hello\xC0\x80 There\xE6\x83 Goodbye").to_string() + },); } diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs index a98a69e2d..935245f63 100644 --- a/library/std/src/sys/unix/path.rs +++ b/library/std/src/sys/unix/path.rs @@ -30,7 +30,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { // Get the components, skipping the redundant leading "." component if it exists. let mut components = path.strip_prefix(".").unwrap_or(path).components(); - let path_os = path.as_os_str().bytes(); + let path_os = path.as_os_str().as_os_str_bytes(); let mut normalized = if path.is_absolute() { // "If a pathname begins with two successive characters, the diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index afd03d79c..640648e87 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -164,9 +164,9 @@ pub enum ProgramKind { impl ProgramKind { fn new(program: &OsStr) -> Self { - if program.bytes().starts_with(b"/") { + if program.as_os_str_bytes().starts_with(b"/") { Self::Absolute - } else if program.bytes().contains(&b'/') { + } else if program.as_os_str_bytes().contains(&b'/') { // If the program has more than one component in it, it is a relative path. Self::Relative } else { diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 612d43fe2..0ce93af66 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -15,6 +15,8 @@ use crate::sys::weak::raw_syscall; #[cfg(any( target_os = "macos", + target_os = "watchos", + target_os = "tvos", target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), @@ -28,15 +30,38 @@ use libc::RTP_ID as pid_t; #[cfg(not(target_os = "vxworks"))] use libc::{c_int, pid_t}; -#[cfg(not(any(target_os = "vxworks", target_os = "l4re")))] +#[cfg(not(any( + target_os = "vxworks", + target_os = "l4re", + target_os = "tvos", + target_os = "watchos", +)))] use libc::{gid_t, uid_t}; cfg_if::cfg_if! { if #[cfg(all(target_os = "nto", target_env = "nto71"))] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; - // arbitrary number of tries: - const MAX_FORKSPAWN_TRIES: u32 = 4; + use crate::time::Duration; + use crate::sync::LazyLock; + // Get smallest amount of time we can sleep. + // Return a common value if it cannot be determined. + fn get_clock_resolution() -> Duration { + static MIN_DELAY: LazyLock Duration> = LazyLock::new(|| { + let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 + { + Duration::from_nanos(mindelay.tv_nsec as u64) + } else { + Duration::from_millis(1) + } + }); + *MIN_DELAY + } + // Arbitrary minimum sleep duration for retrying fork/spawn + const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); + // Maximum duration of sleeping before giving up and returning an error + const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000); } } @@ -66,7 +91,6 @@ impl Command { if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? { return Ok((ret, ours)); } - let (input, output) = sys::pipe::anon_pipe()?; // Whatever happens after the fork is almost for sure going to touch or @@ -148,9 +172,31 @@ impl Command { crate::sys_common::process::wait_with_output(proc, pipes) } + // WatchOS and TVOS headers mark the `fork`/`exec*` functions with + // `__WATCHOS_PROHIBITED __TVOS_PROHIBITED`, and indicate that the + // `posix_spawn*` functions should be used instead. It isn't entirely clear + // what `PROHIBITED` means here (e.g. if calls to these functions are + // 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_io_error!( + ErrorKind::Unsupported, + "`fork`+`exec`-based process spawning is not supported on this target", + ); + + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { + return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); + } + // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(any(target_os = "linux", all(target_os = "nto", target_env = "nto71"))))] + #[cfg(not(any( + target_os = "linux", + target_os = "watchos", + target_os = "tvos", + all(target_os = "nto", target_env = "nto71"), + )))] unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { cvt(libc::fork()).map(|res| (res, -1)) } @@ -163,12 +209,25 @@ impl Command { unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { use crate::sys::os::errno; - let mut tries_left = MAX_FORKSPAWN_TRIES; + let mut delay = MIN_FORKSPAWN_SLEEP; + loop { let r = libc::fork(); - if r == -1 as libc::pid_t && tries_left > 0 && errno() as libc::c_int == libc::EBADF { - thread::yield_now(); - tries_left -= 1; + if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "forking returned EBADF too often", + )); + } + delay *= 2; + continue; } else { return cvt(r).map(|res| (res, -1)); } @@ -308,6 +367,7 @@ impl Command { // allocation). Instead we just close it manually. This will never // have the drop glue anyway because this code never returns (the // child will either exec() or invoke libc::exit) + #[cfg(not(any(target_os = "tvos", target_os = "watchos")))] unsafe fn do_exec( &mut self, stdio: ChildPipes, @@ -414,8 +474,19 @@ impl Command { Err(io::Error::last_os_error()) } + #[cfg(any(target_os = "tvos", target_os = "watchos"))] + unsafe fn do_exec( + &mut self, + _stdio: ChildPipes, + _maybe_envp: Option<&CStringArray>, + ) -> Result { + return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); + } + #[cfg(not(any( target_os = "macos", + target_os = "tvos", + target_os = "watchos", target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), @@ -433,6 +504,9 @@ impl Command { // directly. #[cfg(any( target_os = "macos", + // FIXME: `target_os = "ios"`? + target_os = "tvos", + target_os = "watchos", target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), @@ -480,17 +554,28 @@ impl Command { attrp: *const posix_spawnattr_t, argv: *const *mut c_char, envp: *const *mut c_char, - ) -> i32 { - let mut tries_left = MAX_FORKSPAWN_TRIES; + ) -> io::Result { + let mut delay = MIN_FORKSPAWN_SLEEP; loop { match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { - libc::EBADF if tries_left > 0 => { - thread::yield_now(); - tries_left -= 1; + libc::EBADF => { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "posix_spawnp returned EBADF too often", + )); + } + delay *= 2; continue; } r => { - return r; + return Ok(r); } } } @@ -508,7 +593,7 @@ impl Command { } let addchdir = match self.get_cwd() { Some(cwd) => { - if cfg!(target_os = "macos") { + if cfg!(any(target_os = "macos", target_os = "tvos", target_os = "watchos")) { // There is a bug in macOS where a relative executable // path like "../myprogram" will cause `posix_spawn` to // successfully launch the program, but erroneously return @@ -620,14 +705,20 @@ impl Command { let spawn_fn = libc::posix_spawnp; #[cfg(target_os = "nto")] let spawn_fn = retrying_libc_posix_spawnp; - cvt_nz(spawn_fn( + + let spawn_res = spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(), file_actions.0.as_ptr(), attrs.0.as_ptr(), self.get_argv().as_ptr() as *const _, envp as *const _, - ))?; + ); + + #[cfg(target_os = "nto")] + let spawn_res = spawn_res?; + + cvt_nz(spawn_res)?; Ok(Some(p)) } } @@ -671,12 +762,9 @@ impl Process { pub fn kill(&mut self) -> io::Result<()> { // If we've already waited on this process then the pid can be recycled // and used for another process, and we probably shouldn't be killing - // random processes, so just return an error. + // random processes, so return Ok because the process has exited already. if self.status.is_some() { - Err(io::const_io_error!( - ErrorKind::InvalidInput, - "invalid argument: can't kill an exited process", - )) + Ok(()) } else { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) } @@ -788,31 +876,47 @@ fn signal_string(signal: i32) -> &'static str { libc::SIGILL => " (SIGILL)", libc::SIGTRAP => " (SIGTRAP)", libc::SIGABRT => " (SIGABRT)", + #[cfg(not(target_os = "l4re"))] libc::SIGBUS => " (SIGBUS)", libc::SIGFPE => " (SIGFPE)", libc::SIGKILL => " (SIGKILL)", + #[cfg(not(target_os = "l4re"))] libc::SIGUSR1 => " (SIGUSR1)", libc::SIGSEGV => " (SIGSEGV)", + #[cfg(not(target_os = "l4re"))] libc::SIGUSR2 => " (SIGUSR2)", libc::SIGPIPE => " (SIGPIPE)", libc::SIGALRM => " (SIGALRM)", libc::SIGTERM => " (SIGTERM)", + #[cfg(not(target_os = "l4re"))] libc::SIGCHLD => " (SIGCHLD)", + #[cfg(not(target_os = "l4re"))] libc::SIGCONT => " (SIGCONT)", + #[cfg(not(target_os = "l4re"))] libc::SIGSTOP => " (SIGSTOP)", + #[cfg(not(target_os = "l4re"))] libc::SIGTSTP => " (SIGTSTP)", + #[cfg(not(target_os = "l4re"))] libc::SIGTTIN => " (SIGTTIN)", + #[cfg(not(target_os = "l4re"))] libc::SIGTTOU => " (SIGTTOU)", + #[cfg(not(target_os = "l4re"))] libc::SIGURG => " (SIGURG)", + #[cfg(not(target_os = "l4re"))] libc::SIGXCPU => " (SIGXCPU)", + #[cfg(not(target_os = "l4re"))] libc::SIGXFSZ => " (SIGXFSZ)", + #[cfg(not(target_os = "l4re"))] libc::SIGVTALRM => " (SIGVTALRM)", + #[cfg(not(target_os = "l4re"))] libc::SIGPROF => " (SIGPROF)", + #[cfg(not(target_os = "l4re"))] libc::SIGWINCH => " (SIGWINCH)", - #[cfg(not(target_os = "haiku"))] + #[cfg(not(any(target_os = "haiku", target_os = "l4re")))] libc::SIGIO => " (SIGIO)", #[cfg(target_os = "haiku")] libc::SIGPOLL => " (SIGPOLL)", + #[cfg(not(target_os = "l4re"))] libc::SIGSYS => " (SIGSYS)", // For information on Linux signals, run `man 7 signal` #[cfg(all( diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs index c40e7ada0..f70d3cb39 100644 --- a/library/std/src/sys/unix/process/process_vxworks.rs +++ b/library/std/src/sys/unix/process/process_vxworks.rs @@ -144,12 +144,9 @@ impl Process { pub fn kill(&mut self) -> io::Result<()> { // If we've already waited on this process then the pid can be recycled // and used for another process, and we probably shouldn't be killing - // random processes, so just return an error. + // random processes, so return Ok because the process has exited already. if self.status.is_some() { - Err(io::const_io_error!( - ErrorKind::InvalidInput, - "invalid argument: can't kill an exited process", - )) + Ok(()) } else { cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) } diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index d8b63546b..d471be33e 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -14,6 +14,7 @@ pub fn hashmap_random_keys() -> (u64, u64) { unix, not(target_os = "macos"), not(target_os = "ios"), + not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "openbsd"), not(target_os = "freebsd"), @@ -198,7 +199,7 @@ mod imp { // once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is // only used on iOS where direct access to `/dev/urandom` is blocked by the // sandbox. -#[cfg(any(target_os = "ios", target_os = "watchos"))] +#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] mod imp { use crate::io; use crate::ptr; diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 7307d9b2c..4f2d9cf36 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -150,7 +150,7 @@ impl Thread { } } - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] pub fn set_name(name: &CStr) { unsafe { let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); @@ -284,7 +284,13 @@ impl Drop for Thread { } } -#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))] +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", +))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) { @@ -300,6 +306,7 @@ pub fn available_parallelism() -> io::Result { target_os = "emscripten", target_os = "fuchsia", target_os = "ios", + target_os = "tvos", target_os = "linux", target_os = "macos", target_os = "solaris", @@ -345,6 +352,29 @@ pub fn available_parallelism() -> io::Result { } } + #[cfg(target_os = "netbsd")] + { + unsafe { + let set = libc::_cpuset_create(); + if !set.is_null() { + let mut count: usize = 0; + if libc::pthread_getaffinity_np(libc::pthread_self(), libc::_cpuset_size(set), set) == 0 { + for i in 0..u64::MAX { + match libc::_cpuset_isset(i, set) { + -1 => break, + 0 => continue, + _ => count = count + 1, + } + } + } + libc::_cpuset_destroy(set); + if let Some(count) = NonZeroUsize::new(count) { + return Ok(count); + } + } + } + } + let mut cpus: libc::c_uint = 0; let mut cpus_size = crate::mem::size_of_val(&cpus); diff --git a/library/std/src/sys/unix/thread_parking/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs index 43046ed07..ae805d843 100644 --- a/library/std/src/sys/unix/thread_parking/pthread.rs +++ b/library/std/src/sys/unix/thread_parking/pthread.rs @@ -46,6 +46,7 @@ unsafe fn wait_timeout( #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "espidf", target_os = "horizon", @@ -73,6 +74,7 @@ unsafe fn wait_timeout( #[cfg(not(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "espidf", target_os = "horizon", @@ -120,10 +122,12 @@ impl Parker { if #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos", target_os = "l4re", target_os = "android", - target_os = "redox" + target_os = "redox", + target_os = "vita", ))] { addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index a9fbc7ab1..17b4130c2 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -219,7 +219,8 @@ impl From<__timespec64> for Timespec { #[cfg(any( all(target_os = "macos", any(not(target_arch = "aarch64"))), target_os = "ios", - target_os = "watchos" + target_os = "watchos", + target_os = "tvos" ))] mod inner { use crate::sync::atomic::{AtomicU64, Ordering}; @@ -339,7 +340,8 @@ mod inner { #[cfg(not(any( all(target_os = "macos", any(not(target_arch = "aarch64"))), target_os = "ios", - target_os = "watchos" + target_os = "watchos", + target_os = "tvos" )))] mod inner { use crate::fmt; diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs index 62ffee70b..61088ff16 100644 --- a/library/std/src/sys/unix/weak.rs +++ b/library/std/src/sys/unix/weak.rs @@ -28,7 +28,7 @@ use crate::ptr; use crate::sync::atomic::{self, AtomicPtr, Ordering}; // We can use true weak linkage on ELF targets. -#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos")))] pub(crate) macro weak { (fn $name:ident($($t:ty),*) -> $ret:ty) => ( let ref $name: ExternWeak $ret> = { @@ -43,7 +43,7 @@ pub(crate) macro weak { } // On non-ELF targets, use the dlsym approximation of weak linkage. -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] pub(crate) use self::dlsym as weak; pub(crate) struct ExternWeak { diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs index 9a8b2a0be..1b50c2ea6 100644 --- a/library/std/src/sys/wasi/fd.rs +++ b/library/std/src/sys/wasi/fd.rs @@ -96,7 +96,7 @@ impl WasiFd { unsafe { wasi::fd_sync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } } - pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { + pub(crate) fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { unsafe { wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io) } @@ -179,7 +179,7 @@ impl WasiFd { } } - pub fn filestat_get(&self) -> io::Result { + pub(crate) fn filestat_get(&self) -> io::Result { unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::Fd).map_err(err2io) } } @@ -199,7 +199,7 @@ impl WasiFd { unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) } } - pub fn path_filestat_get( + pub(crate) fn path_filestat_get( &self, flags: wasi::Lookupflags, path: &str, diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index 8d1dbf591..437aae3ae 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -104,7 +104,7 @@ impl FileAttr { Ok(SystemTime::from_wasi_timestamp(self.meta.ctim)) } - pub fn as_wasi(&self) -> &wasi::Filestat { + pub(crate) fn as_wasi(&self) -> &wasi::Filestat { &self.meta } } @@ -142,7 +142,7 @@ impl FileType { self.bits == wasi::FILETYPE_SYMBOLIC_LINK } - pub fn bits(&self) -> wasi::Filetype { + pub(crate) fn bits(&self) -> wasi::Filetype { self.bits } } diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs index 5bfd8b52e..6b597f499 100644 --- a/library/std/src/sys/windows/args.rs +++ b/library/std/src/sys/windows/args.rs @@ -226,7 +226,7 @@ pub(crate) fn append_arg(cmd: &mut Vec, arg: &Arg, force_quotes: bool) -> i // that it actually gets passed through on the command line or otherwise // it will be dropped entirely when parsed on the other end. ensure_no_nuls(arg)?; - let arg_bytes = arg.bytes(); + let arg_bytes = arg.as_os_str_bytes(); let (quote, escape) = match quote { Quote::Always => (true, true), Quote::Auto => { @@ -297,7 +297,9 @@ pub(crate) fn make_bat_command_line( // * `|<>` pipe/redirect characters. const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>"; let force_quotes = match arg { - Arg::Regular(arg) if !force_quotes => arg.bytes().iter().any(|c| SPECIAL.contains(c)), + Arg::Regular(arg) if !force_quotes => { + arg.as_os_str_bytes().iter().any(|c| SPECIAL.contains(c)) + } _ => force_quotes, }; append_arg(&mut cmd, arg, force_quotes)?; diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 2bc40c474..d9ccba0e9 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -12,13 +12,13 @@ use crate::os::windows::io::{AsRawHandle, BorrowedHandle}; use crate::ptr; use core::ffi::NonZero_c_ulong; -#[path = "c/windows_sys.rs"] // c.rs is included from two places so we need to specify this mod windows_sys; pub use windows_sys::*; pub type DWORD = c_ulong; pub type NonZeroDWORD = NonZero_c_ulong; pub type LARGE_INTEGER = c_longlong; +#[cfg_attr(target_vendor = "uwp", allow(unused))] pub type LONG = c_long; pub type UINT = c_uint; pub type WCHAR = u16; @@ -267,6 +267,8 @@ pub unsafe fn getaddrinfo( windows_sys::getaddrinfo(node.cast::(), service.cast::(), hints, res) } +cfg_if::cfg_if! { +if #[cfg(not(target_vendor = "uwp"))] { pub unsafe fn NtReadFile( filehandle: BorrowedHandle<'_>, event: HANDLE, @@ -313,6 +315,8 @@ pub unsafe fn NtWriteFile( key.map(|k| k as *const u32).unwrap_or(ptr::null()), ) } +} +} // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. @@ -376,4 +380,98 @@ compat_fn_with_fallback! { ) -> NTSTATUS { panic!("keyed events not available") } + + // These functions are available on UWP when lazily loaded. They will fail WACK if loaded statically. + #[cfg(target_vendor = "uwp")] + pub fn NtCreateFile( + filehandle: *mut HANDLE, + desiredaccess: FILE_ACCESS_RIGHTS, + objectattributes: *const OBJECT_ATTRIBUTES, + iostatusblock: *mut IO_STATUS_BLOCK, + allocationsize: *const i64, + fileattributes: FILE_FLAGS_AND_ATTRIBUTES, + shareaccess: FILE_SHARE_MODE, + createdisposition: NTCREATEFILE_CREATE_DISPOSITION, + createoptions: NTCREATEFILE_CREATE_OPTIONS, + eabuffer: *const ::core::ffi::c_void, + ealength: u32 + ) -> NTSTATUS { + STATUS_NOT_IMPLEMENTED + } + #[cfg(target_vendor = "uwp")] + pub fn NtReadFile( + filehandle: BorrowedHandle<'_>, + event: HANDLE, + apcroutine: PIO_APC_ROUTINE, + apccontext: *mut c_void, + iostatusblock: &mut IO_STATUS_BLOCK, + buffer: *mut crate::mem::MaybeUninit, + length: ULONG, + byteoffset: Option<&LARGE_INTEGER>, + key: Option<&ULONG> + ) -> NTSTATUS { + STATUS_NOT_IMPLEMENTED + } + #[cfg(target_vendor = "uwp")] + pub fn NtWriteFile( + filehandle: BorrowedHandle<'_>, + event: HANDLE, + apcroutine: PIO_APC_ROUTINE, + apccontext: *mut c_void, + iostatusblock: &mut IO_STATUS_BLOCK, + buffer: *const u8, + length: ULONG, + byteoffset: Option<&LARGE_INTEGER>, + key: Option<&ULONG> + ) -> NTSTATUS { + STATUS_NOT_IMPLEMENTED + } + #[cfg(target_vendor = "uwp")] + pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> u32 { + Status as u32 + } +} + +// # Arm32 shim +// +// AddVectoredExceptionHandler and WSAStartup use platform-specific types. +// However, Microsoft no longer supports thumbv7a so definitions for those targets +// are not included in the win32 metadata. We work around that by defining them here. +// +// Where possible, these definitions should be kept in sync with https://docs.rs/windows-sys +cfg_if::cfg_if! { +if #[cfg(not(target_vendor = "uwp"))] { + #[link(name = "kernel32")] + extern "system" { + pub fn AddVectoredExceptionHandler( + first: u32, + handler: PVECTORED_EXCEPTION_HANDLER, + ) -> *mut c_void; + } + pub type PVECTORED_EXCEPTION_HANDLER = Option< + unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32, + >; + #[repr(C)] + pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, + } + #[cfg(target_arch = "arm")] + pub enum CONTEXT {} +}} + +#[link(name = "ws2_32")] +extern "system" { + pub fn WSAStartup(wversionrequested: u16, lpwsadata: *mut WSADATA) -> i32; +} +#[cfg(target_arch = "arm")] +#[repr(C)] +pub struct WSADATA { + pub wVersion: u16, + pub wHighVersion: u16, + pub szDescription: [u8; 257], + pub szSystemStatus: [u8; 129], + pub iMaxSockets: u16, + pub iMaxUdpDg: u16, + pub lpVendorInfo: PSTR, } diff --git a/library/std/src/sys/windows/c/windows_sys.lst b/library/std/src/sys/windows/c/windows_sys.lst index 3e454199f..631aedd26 100644 --- a/library/std/src/sys/windows/c/windows_sys.lst +++ b/library/std/src/sys/windows/c/windows_sys.lst @@ -1930,6 +1930,7 @@ Windows.Win32.Foundation.SetLastError Windows.Win32.Foundation.STATUS_DELETE_PENDING Windows.Win32.Foundation.STATUS_END_OF_FILE Windows.Win32.Foundation.STATUS_INVALID_PARAMETER +Windows.Win32.Foundation.STATUS_NOT_IMPLEMENTED Windows.Win32.Foundation.STATUS_PENDING Windows.Win32.Foundation.STATUS_SUCCESS Windows.Win32.Foundation.TRUE @@ -2170,7 +2171,6 @@ Windows.Win32.Networking.WinSock.WSARecv Windows.Win32.Networking.WinSock.WSASend Windows.Win32.Networking.WinSock.WSASERVICE_NOT_FOUND Windows.Win32.Networking.WinSock.WSASocketW -Windows.Win32.Networking.WinSock.WSAStartup Windows.Win32.Networking.WinSock.WSASYSCALLFAILURE Windows.Win32.Networking.WinSock.WSASYSNOTREADY Windows.Win32.Networking.WinSock.WSATRY_AGAIN @@ -2418,12 +2418,10 @@ Windows.Win32.System.Console.STD_HANDLE Windows.Win32.System.Console.STD_INPUT_HANDLE Windows.Win32.System.Console.STD_OUTPUT_HANDLE Windows.Win32.System.Console.WriteConsoleW -Windows.Win32.System.Diagnostics.Debug.AddVectoredExceptionHandler Windows.Win32.System.Diagnostics.Debug.ARM64_NT_NEON128 Windows.Win32.System.Diagnostics.Debug.CONTEXT Windows.Win32.System.Diagnostics.Debug.CONTEXT Windows.Win32.System.Diagnostics.Debug.CONTEXT -Windows.Win32.System.Diagnostics.Debug.EXCEPTION_POINTERS Windows.Win32.System.Diagnostics.Debug.EXCEPTION_RECORD Windows.Win32.System.Diagnostics.Debug.FACILITY_CODE Windows.Win32.System.Diagnostics.Debug.FACILITY_NT_BIT @@ -2436,7 +2434,6 @@ Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_OPTIONS Windows.Win32.System.Diagnostics.Debug.FormatMessageW Windows.Win32.System.Diagnostics.Debug.M128A -Windows.Win32.System.Diagnostics.Debug.PVECTORED_EXCEPTION_HANDLER Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT Windows.Win32.System.Environment.FreeEnvironmentStringsW diff --git a/library/std/src/sys/windows/c/windows_sys.rs b/library/std/src/sys/windows/c/windows_sys.rs index 36a30f6ba..023770871 100644 --- a/library/std/src/sys/windows/c/windows_sys.rs +++ b/library/std/src/sys/windows/c/windows_sys.rs @@ -39,13 +39,6 @@ extern "system" { pub fn AcquireSRWLockShared(srwlock: *mut RTL_SRWLOCK) -> (); } #[link(name = "kernel32")] -extern "system" { - pub fn AddVectoredExceptionHandler( - first: u32, - handler: PVECTORED_EXCEPTION_HANDLER, - ) -> *mut ::core::ffi::c_void; -} -#[link(name = "kernel32")] extern "system" { pub fn CancelIo(hfile: HANDLE) -> BOOL; } @@ -711,10 +704,6 @@ extern "system" { ) -> SOCKET; } #[link(name = "ws2_32")] -extern "system" { - pub fn WSAStartup(wversionrequested: u16, lpwsadata: *mut WSADATA) -> i32; -} -#[link(name = "ws2_32")] extern "system" { pub fn accept(s: SOCKET, addr: *mut SOCKADDR, addrlen: *mut i32) -> SOCKET; } @@ -3029,17 +3018,6 @@ pub const ERROR_XML_PARSE_ERROR: WIN32_ERROR = 1465u32; pub type EXCEPTION_DISPOSITION = i32; pub const EXCEPTION_MAXIMUM_PARAMETERS: u32 = 15u32; #[repr(C)] -pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ContextRecord: *mut CONTEXT, -} -impl ::core::marker::Copy for EXCEPTION_POINTERS {} -impl ::core::clone::Clone for EXCEPTION_POINTERS { - fn clone(&self) -> Self { - *self - } -} -#[repr(C)] pub struct EXCEPTION_RECORD { pub ExceptionCode: NTSTATUS, pub ExceptionFlags: u32, @@ -3748,9 +3726,6 @@ pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32; pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32; pub const PROGRESS_CONTINUE: u32 = 0u32; pub type PSTR = *mut u8; -pub type PVECTORED_EXCEPTION_HANDLER = ::core::option::Option< - unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32, ->; pub type PWSTR = *mut u16; pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32; pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32; @@ -3888,6 +3863,7 @@ pub type STARTUPINFOW_FLAGS = u32; pub const STATUS_DELETE_PENDING: NTSTATUS = -1073741738i32; pub const STATUS_END_OF_FILE: NTSTATUS = -1073741807i32; pub const STATUS_INVALID_PARAMETER: NTSTATUS = -1073741811i32; +pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = -1073741822i32; pub const STATUS_PENDING: NTSTATUS = 259i32; pub const STATUS_SUCCESS: NTSTATUS = 0i32; pub const STD_ERROR_HANDLE: STD_HANDLE = 4294967284u32; diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 2404bbe2b..1ae42cb7e 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -159,7 +159,7 @@ impl Socket { } let mut timeout = c::timeval { - tv_sec: timeout.as_secs() as c_long, + tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, tv_usec: (timeout.subsec_nanos() / 1000) as c_long, }; diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs index 2f2b0e56e..16c4f55c6 100644 --- a/library/std/src/sys/windows/os_str.rs +++ b/library/std/src/sys/windows/os_str.rs @@ -151,12 +151,22 @@ impl Buf { } impl Slice { + #[inline] + pub fn as_os_str_bytes(&self) -> &[u8] { + self.inner.as_bytes() + } + + #[inline] + pub unsafe fn from_os_str_bytes_unchecked(s: &[u8]) -> &Slice { + mem::transmute(Wtf8::from_bytes_unchecked(s)) + } + #[inline] pub fn from_str(s: &str) -> &Slice { unsafe { mem::transmute(Wtf8::from_str(s)) } } - pub fn to_str(&self) -> Option<&str> { + pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { self.inner.as_str() } diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs index c3573d14c..c9c2d10e6 100644 --- a/library/std/src/sys/windows/path.rs +++ b/library/std/src/sys/windows/path.rs @@ -1,7 +1,6 @@ use super::{c, fill_utf16_buf, to_u16s}; use crate::ffi::{OsStr, OsString}; use crate::io; -use crate::mem; use crate::path::{Path, PathBuf, Prefix}; use crate::ptr; @@ -11,16 +10,6 @@ mod tests; pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; -/// # Safety -/// -/// `bytes` must be a valid wtf8 encoded slice -#[inline] -unsafe fn bytes_as_os_str(bytes: &[u8]) -> &OsStr { - // &OsStr is layout compatible with &Slice, which is compatible with &Wtf8, - // which is compatible with &[u8]. - mem::transmute(bytes) -} - #[inline] pub fn is_sep_byte(b: u8) -> bool { b == b'/' || b == b'\\' @@ -33,12 +22,12 @@ pub fn is_verbatim_sep(b: u8) -> bool { /// Returns true if `path` looks like a lone filename. pub(crate) fn is_file_name(path: &OsStr) -> bool { - !path.bytes().iter().copied().any(is_sep_byte) + !path.as_os_str_bytes().iter().copied().any(is_sep_byte) } pub(crate) fn has_trailing_slash(path: &OsStr) -> bool { - let is_verbatim = path.bytes().starts_with(br"\\?\"); + let is_verbatim = path.as_os_str_bytes().starts_with(br"\\?\"); let is_separator = if is_verbatim { is_verbatim_sep } else { is_sep_byte }; - if let Some(&c) = path.bytes().last() { is_separator(c) } else { false } + if let Some(&c) = path.as_os_str_bytes().last() { is_separator(c) } else { false } } /// Appends a suffix to a path. @@ -60,7 +49,7 @@ impl<'a, const LEN: usize> PrefixParser<'a, LEN> { fn get_prefix(path: &OsStr) -> [u8; LEN] { let mut prefix = [0; LEN]; // SAFETY: Only ASCII characters are modified. - for (i, &ch) in path.bytes().iter().take(LEN).enumerate() { + for (i, &ch) in path.as_os_str_bytes().iter().take(LEN).enumerate() { prefix[i] = if ch == b'/' { b'\\' } else { ch }; } prefix @@ -93,7 +82,7 @@ impl<'a> PrefixParserSlice<'a, '_> { } fn prefix_bytes(&self) -> &'a [u8] { - &self.path.bytes()[..self.index] + &self.path.as_os_str_bytes()[..self.index] } fn finish(self) -> &'a OsStr { @@ -101,7 +90,7 @@ impl<'a> PrefixParserSlice<'a, '_> { // &[u8] and back. This is safe to do because (1) we only look at ASCII // contents of the encoding and (2) new &OsStr values are produced only // from ASCII-bounded slices of existing &OsStr values. - unsafe { bytes_as_os_str(&self.path.bytes()[self.index..]) } + unsafe { OsStr::from_os_str_bytes_unchecked(&self.path.as_os_str_bytes()[self.index..]) } } } @@ -173,7 +162,7 @@ fn parse_drive(path: &OsStr) -> Option { drive.is_ascii_alphabetic() } - match path.bytes() { + match path.as_os_str_bytes() { [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()), _ => None, } @@ -182,7 +171,7 @@ fn parse_drive(path: &OsStr) -> Option { // Parses a drive prefix exactly, e.g. "C:" fn parse_drive_exact(path: &OsStr) -> Option { // only parse two bytes: the drive letter and the drive separator - if path.bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { + if path.as_os_str_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) { parse_drive(path) } else { None @@ -196,21 +185,26 @@ fn parse_drive_exact(path: &OsStr) -> Option { fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) { let separator = if verbatim { is_verbatim_sep } else { is_sep_byte }; - match path.bytes().iter().position(|&x| separator(x)) { + match path.as_os_str_bytes().iter().position(|&x| separator(x)) { Some(separator_start) => { let separator_end = separator_start + 1; - let component = &path.bytes()[..separator_start]; + let component = &path.as_os_str_bytes()[..separator_start]; // Panic safe // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index. - let path = &path.bytes()[separator_end..]; + let path = &path.as_os_str_bytes()[separator_end..]; // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\') // is encoded in a single byte, therefore `bytes[separator_start]` and // `bytes[separator_end]` must be code point boundaries and thus // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices. - unsafe { (bytes_as_os_str(component), bytes_as_os_str(path)) } + unsafe { + ( + OsStr::from_os_str_bytes_unchecked(component), + OsStr::from_os_str_bytes_unchecked(path), + ) + } } None => (path, OsStr::new("")), } @@ -329,7 +323,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { // Verbatim paths should not be modified. if prefix.map(|x| x.is_verbatim()).unwrap_or(false) { // NULs in verbatim paths are rejected for consistency. - if path.bytes().contains(&0) { + if path.as_os_str_bytes().contains(&0) { return Err(io::const_io_error!( io::ErrorKind::InvalidInput, "strings passed to WinAPI cannot contain NULs", diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index df3667c0f..e3493cbb8 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -395,7 +395,7 @@ fn resolve_exe<'a>( // Test if the file name has the `exe` extension. // This does a case-insensitive `ends_with`. let has_exe_suffix = if exe_path.len() >= EXE_SUFFIX.len() { - exe_path.bytes()[exe_path.len() - EXE_SUFFIX.len()..] + exe_path.as_os_str_bytes()[exe_path.len() - EXE_SUFFIX.len()..] .eq_ignore_ascii_case(EXE_SUFFIX.as_bytes()) } else { false @@ -425,7 +425,7 @@ fn resolve_exe<'a>( // From the `CreateProcessW` docs: // > If the file name does not contain an extension, .exe is appended. // Note that this rule only applies when searching paths. - let has_extension = exe_path.bytes().contains(&b'.'); + let has_extension = exe_path.as_os_str_bytes().contains(&b'.'); // Search the directories given by `search_paths`. let result = search_paths(parent_paths, child_paths, |mut path| { @@ -595,7 +595,16 @@ pub struct Process { impl Process { pub fn kill(&mut self) -> io::Result<()> { - cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?; + let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) }; + if result == c::FALSE { + let error = unsafe { c::GetLastError() }; + // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been + // terminated (by us, or for any other reason). So check if the process was actually + // terminated, and if so, do not return an error. + if error != c::ERROR_ACCESS_DENIED || self.try_wait().is_err() { + return Err(crate::io::Error::from_raw_os_error(error as i32)); + } + } Ok(()) } diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs index bca4e38d9..5d8fd1378 100644 --- a/library/std/src/sys/windows/rand.rs +++ b/library/std/src/sys/windows/rand.rs @@ -1,5 +1,3 @@ -use crate::ffi::c_void; -use crate::io; use crate::mem; use crate::ptr; use crate::sys::c; @@ -25,6 +23,9 @@ pub fn hashmap_random_keys() -> (u64, u64) { #[cfg(not(target_vendor = "uwp"))] #[inline(never)] fn fallback_rng() -> (u64, u64) { + use crate::ffi::c_void; + use crate::io; + let mut v = (0, 0); let ret = unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut c_void, mem::size_of_val(&v) as c::ULONG) diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index 2e3e0859d..3fcaaa508 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -11,6 +11,9 @@ use crate::sys::cvt; use crate::sys::handle::Handle; use core::str::utf8_char_width; +#[cfg(test)] +mod tests; + // Don't cache handles but get them fresh for every read/write. This allows us to track changes to // the value over time (such as if a process calls `SetStdHandle` while it's running). See #40490. pub struct Stdin { @@ -383,6 +386,10 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { debug_assert!(utf16.len() <= c::c_int::MAX as usize); debug_assert!(utf8.len() <= c::c_int::MAX as usize); + if utf16.is_empty() { + return Ok(0); + } + let result = unsafe { c::WideCharToMultiByte( c::CP_UTF8, // CodePage diff --git a/library/std/src/sys/windows/stdio/tests.rs b/library/std/src/sys/windows/stdio/tests.rs new file mode 100644 index 000000000..1e53e0bee --- /dev/null +++ b/library/std/src/sys/windows/stdio/tests.rs @@ -0,0 +1,6 @@ +use super::utf16_to_utf8; + +#[test] +fn zero_size_read() { + assert_eq!(utf16_to_utf8(&[], &mut []).unwrap(), 0); +} diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 652c695fc..2976a9f57 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -18,7 +18,7 @@ use crate::ffi::{c_int, c_void}; cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", target_os = "macos", target_os = "watchos", + target_os = "ios", target_os = "tvos", target_os = "macos", target_os = "watchos", target_os = "openbsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "haiku", target_os = "l4re", target_os = "nto"))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index ff96c35fb..c9d3e13cf 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -570,7 +570,7 @@ impl Wtf8 { /// Since the byte slice is not checked for valid WTF-8, this functions is /// marked unsafe. #[inline] - unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 { + pub unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 { mem::transmute(value) } @@ -614,19 +614,20 @@ impl Wtf8 { Wtf8CodePoints { bytes: self.bytes.iter() } } + /// Access raw bytes of WTF-8 data + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.bytes + } + /// Tries to convert the string to UTF-8 and return a `&str` slice. /// /// Returns `None` if the string contains surrogates. /// /// This does not copy the data. #[inline] - pub fn as_str(&self) -> Option<&str> { - // Well-formed WTF-8 is also well-formed UTF-8 - // if and only if it contains no surrogate. - match self.next_surrogate(0) { - None => Some(unsafe { str::from_utf8_unchecked(&self.bytes) }), - Some(_) => None, - } + pub fn as_str(&self) -> Result<&str, str::Utf8Error> { + str::from_utf8(&self.bytes) } /// Creates an owned `Wtf8Buf` from a borrowed `Wtf8`. diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index 1a302d646..a07bbe6d7 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -521,11 +521,11 @@ fn wtf8_code_points() { #[test] fn wtf8_as_str() { - assert_eq!(Wtf8::from_str("").as_str(), Some("")); - assert_eq!(Wtf8::from_str("aé 💩").as_str(), Some("aé 💩")); + assert_eq!(Wtf8::from_str("").as_str(), Ok("")); + assert_eq!(Wtf8::from_str("aé 💩").as_str(), Ok("aé 💩")); let mut string = Wtf8Buf::new(); string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!(string.as_str(), None); + assert!(string.as_str().is_err()); } #[test] diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index f712c8727..e4581c2de 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -195,7 +195,7 @@ mod local; cfg_if::cfg_if! { if #[cfg(test)] { - // Avoid duplicating the global state assoicated with thread-locals between this crate and + // Avoid duplicating the global state associated with thread-locals between this crate and // realstd. Miri relies on this. pub use realstd::thread::{local_impl, AccessError, LocalKey}; } else { @@ -206,7 +206,7 @@ cfg_if::cfg_if! { #[doc(hidden)] #[unstable(feature = "thread_local_internals", issue = "none")] pub mod local_impl { - pub use crate::sys::common::thread_local::{thread_local_inner, Key}; + pub use crate::sys::common::thread_local::{thread_local_inner, Key, abort_on_dtor_unwind}; } } } @@ -889,7 +889,7 @@ impl Drop for PanicGuard { /// it is guaranteed that this function will not panic (it may abort the /// process if the implementation encounters some rare errors). /// -/// # park and unpark +/// # `park` and `unpark` /// /// Every thread is equipped with some basic low-level blocking support, via the /// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] @@ -910,14 +910,6 @@ impl Drop for PanicGuard { /// if it wasn't already. Because the token is initially absent, [`unpark`] /// followed by [`park`] will result in the second call returning immediately. /// -/// In other words, each [`Thread`] acts a bit like a spinlock that can be -/// locked and unlocked using `park` and `unpark`. -/// -/// Notice that being unblocked does not imply any synchronization with someone -/// that unparked this thread, it could also be spurious. -/// For example, it would be a valid, but inefficient, implementation to make both [`park`] and -/// [`unpark`] return immediately without doing anything. -/// /// The API is typically used by acquiring a handle to the current thread, /// placing that handle in a shared data structure so that other threads can /// find it, and then `park`ing in a loop. When some desired condition is met, another @@ -931,6 +923,23 @@ impl Drop for PanicGuard { /// /// * It can be implemented very efficiently on many platforms. /// +/// # Memory Ordering +/// +/// 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 `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 +/// thread form a [release sequence]. +/// +/// Note that being unblocked does not imply a call was made to `unpark`, because +/// wakeups can also be spurious. For example, a valid, but inefficient, +/// implementation could have `park` and `unpark` return immediately without doing anything, +/// making *all* wakeups spurious. +/// /// # Examples /// /// ``` @@ -944,7 +953,7 @@ impl Drop for PanicGuard { /// let parked_thread = thread::spawn(move || { /// // We want to wait until the flag is set. We *could* just spin, but using /// // park/unpark is more efficient. -/// while !flag2.load(Ordering::Acquire) { +/// while !flag2.load(Ordering::Relaxed) { /// println!("Parking thread"); /// thread::park(); /// // We *could* get here spuriously, i.e., way before the 10ms below are over! @@ -961,7 +970,7 @@ impl Drop for PanicGuard { /// // There is no race condition here, if `unpark` /// // happens first, `park` will return immediately. /// // Hence there is no risk of a deadlock. -/// flag.store(true, Ordering::Release); +/// flag.store(true, Ordering::Relaxed); /// println!("Unpark the thread"); /// parked_thread.thread().unpark(); /// @@ -970,6 +979,7 @@ impl Drop for PanicGuard { /// /// [`unpark`]: Thread::unpark /// [`thread::park_timeout`]: park_timeout +/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence #[stable(feature = "rust1", since = "1.0.0")] pub fn park() { let guard = PanicGuard; diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index b65e2572c..5d6b9e94e 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -42,6 +42,7 @@ fn test_named_thread() { all(target_os = "linux", target_env = "gnu"), target_os = "macos", target_os = "ios", + target_os = "tvos", target_os = "watchos" ))] #[test] diff --git a/library/std/tests/common/mod.rs b/library/std/tests/common/mod.rs index fce220223..358c2c3f9 100644 --- a/library/std/tests/common/mod.rs +++ b/library/std/tests/common/mod.rs @@ -20,15 +20,15 @@ pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { } // Copied from std::sys_common::io -pub struct TempDir(PathBuf); +pub(crate) struct TempDir(PathBuf); impl TempDir { - pub fn join(&self, path: &str) -> PathBuf { + pub(crate) fn join(&self, path: &str) -> PathBuf { let TempDir(ref p) = *self; p.join(path) } - pub fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Path { let TempDir(ref p) = *self; p } @@ -49,7 +49,7 @@ impl Drop for TempDir { } #[track_caller] // for `test_rng` -pub fn tmpdir() -> TempDir { +pub(crate) fn tmpdir() -> TempDir { let p = env::temp_dir(); let mut r = test_rng(); let ret = p.join(&format!("rust-{}", r.next_u32())); diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 5356ee277..6ff24a8db 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -17,6 +17,7 @@ compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"] compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"] +compiler-builtins-weak-intrinsics = ["std/compiler-builtins-weak-intrinsics"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] panic-unwind = ["std/panic_unwind"] diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 7eee4ca23..bbeb944e8 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -199,7 +199,7 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Res let TestDescAndFn { desc, testfn } = test; let fntype = match testfn { - StaticTestFn(..) | DynTestFn(..) => { + StaticTestFn(..) | DynTestFn(..) | StaticBenchAsTestFn(..) | DynBenchAsTestFn(..) => { st.tests += 1; "test" } diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index e76d6716b..b40b6009e 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -92,6 +92,7 @@ use time::TestExecTime; const ERROR_EXIT_CODE: i32 = 101; const SECONDARY_TEST_INVOKER_VAR: &str = "__RUST_TEST_INVOKE"; +const SECONDARY_TEST_BENCH_BENCHMARKS_VAR: &str = "__RUST_TEST_BENCH_BENCHMARKS"; // The default console test runner. It accepts the command line // arguments and a vector of test_descs. @@ -171,18 +172,32 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { // will then exit the process. if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) { env::remove_var(SECONDARY_TEST_INVOKER_VAR); + + // Convert benchmarks to tests if we're not benchmarking. + let mut tests = tests.iter().map(make_owned_test).collect::>(); + if env::var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR).is_ok() { + env::remove_var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR); + } else { + tests = convert_benchmarks_to_tests(tests); + }; + let test = tests - .iter() + .into_iter() .filter(|test| test.desc.name.as_slice() == name) - .map(make_owned_test) .next() .unwrap_or_else(|| panic!("couldn't find a test with the provided name '{name}'")); let TestDescAndFn { desc, testfn } = test; - let testfn = match testfn { - StaticTestFn(f) => f, - _ => panic!("only static tests are supported"), - }; - run_test_in_spawned_subprocess(desc, Box::new(testfn)); + match testfn.into_runnable() { + Runnable::Test(runnable_test) => { + if runnable_test.is_dynamic() { + panic!("only static tests are supported"); + } + run_test_in_spawned_subprocess(desc, runnable_test); + } + Runnable::Bench(_) => { + panic!("benchmarks should not be executed into child processes") + } + } } let args = env::args().collect::>(); @@ -234,16 +249,6 @@ impl FilteredTests { self.tests.push((TestId(self.next_id), test)); self.next_id += 1; } - fn add_bench_as_test( - &mut self, - desc: TestDesc, - benchfn: impl Fn(&mut Bencher) -> Result<(), String> + Send + 'static, - ) { - let testfn = DynTestFn(Box::new(move || { - bench::run_once(|b| __rust_begin_short_backtrace(|| benchfn(b))) - })); - self.add_test(desc, testfn); - } fn total_len(&self) -> usize { self.tests.len() + self.benches.len() } @@ -301,14 +306,14 @@ where if opts.bench_benchmarks { filtered.add_bench(desc, DynBenchFn(benchfn)); } else { - filtered.add_bench_as_test(desc, benchfn); + filtered.add_test(desc, DynBenchAsTestFn(benchfn)); } } StaticBenchFn(benchfn) => { if opts.bench_benchmarks { filtered.add_bench(desc, StaticBenchFn(benchfn)); } else { - filtered.add_bench_as_test(desc, benchfn); + filtered.add_test(desc, StaticBenchAsTestFn(benchfn)); } } testfn => { @@ -519,12 +524,8 @@ pub fn convert_benchmarks_to_tests(tests: Vec) -> Vec DynTestFn(Box::new(move || { - bench::run_once(|b| __rust_begin_short_backtrace(|| benchfn(b))) - })), - StaticBenchFn(benchfn) => DynTestFn(Box::new(move || { - bench::run_once(|b| __rust_begin_short_backtrace(|| benchfn(b))) - })), + DynBenchFn(benchfn) => DynBenchAsTestFn(benchfn), + StaticBenchFn(benchfn) => StaticBenchAsTestFn(benchfn), f => f, }; TestDescAndFn { desc: x.desc, testfn } @@ -553,99 +554,69 @@ pub fn run_test( return None; } - struct TestRunOpts { - pub strategy: RunStrategy, - pub nocapture: bool, - pub time: Option, - } + match testfn.into_runnable() { + Runnable::Test(runnable_test) => { + if runnable_test.is_dynamic() { + match strategy { + RunStrategy::InProcess => (), + _ => panic!("Cannot run dynamic test fn out-of-process"), + }; + } - fn run_test_inner( - id: TestId, - desc: TestDesc, - monitor_ch: Sender, - testfn: Box Result<(), String> + Send>, - opts: TestRunOpts, - ) -> Option> { - let name = desc.name.clone(); - - let runtest = move || match opts.strategy { - RunStrategy::InProcess => run_test_in_process( - id, - desc, - opts.nocapture, - opts.time.is_some(), - testfn, - monitor_ch, - opts.time, - ), - RunStrategy::SpawnPrimary => spawn_test_subprocess( - id, - desc, - opts.nocapture, - opts.time.is_some(), - monitor_ch, - opts.time, - ), - }; + let name = desc.name.clone(); + let nocapture = opts.nocapture; + let time_options = opts.time_options; + let bench_benchmarks = opts.bench_benchmarks; + + let runtest = move || match strategy { + RunStrategy::InProcess => run_test_in_process( + id, + desc, + nocapture, + time_options.is_some(), + runnable_test, + monitor_ch, + time_options, + ), + RunStrategy::SpawnPrimary => spawn_test_subprocess( + id, + desc, + nocapture, + time_options.is_some(), + monitor_ch, + time_options, + bench_benchmarks, + ), + }; - // If the platform is single-threaded we're just going to run - // the test synchronously, regardless of the concurrency - // level. - let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_family = "wasm"); - if supports_threads { - let cfg = thread::Builder::new().name(name.as_slice().to_owned()); - let mut runtest = Arc::new(Mutex::new(Some(runtest))); - let runtest2 = runtest.clone(); - match cfg.spawn(move || runtest2.lock().unwrap().take().unwrap()()) { - Ok(handle) => Some(handle), - Err(e) if e.kind() == io::ErrorKind::WouldBlock => { - // `ErrorKind::WouldBlock` means hitting the thread limit on some - // platforms, so run the test synchronously here instead. - Arc::get_mut(&mut runtest).unwrap().get_mut().unwrap().take().unwrap()(); - None + // If the platform is single-threaded we're just going to run + // the test synchronously, regardless of the concurrency + // level. + let supports_threads = !cfg!(target_os = "emscripten") && !cfg!(target_family = "wasm"); + if supports_threads { + let cfg = thread::Builder::new().name(name.as_slice().to_owned()); + let mut runtest = Arc::new(Mutex::new(Some(runtest))); + let runtest2 = runtest.clone(); + match cfg.spawn(move || runtest2.lock().unwrap().take().unwrap()()) { + Ok(handle) => Some(handle), + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { + // `ErrorKind::WouldBlock` means hitting the thread limit on some + // platforms, so run the test synchronously here instead. + Arc::get_mut(&mut runtest).unwrap().get_mut().unwrap().take().unwrap()(); + None + } + Err(e) => panic!("failed to spawn thread to run test: {e}"), } - Err(e) => panic!("failed to spawn thread to run test: {e}"), + } else { + runtest(); + None } - } else { - runtest(); - None } - } - - let test_run_opts = - TestRunOpts { strategy, nocapture: opts.nocapture, time: opts.time_options }; - - match testfn { - DynBenchFn(benchfn) => { + Runnable::Bench(runnable_bench) => { // Benchmarks aren't expected to panic, so we run them all in-process. - crate::bench::benchmark(id, desc, monitor_ch, opts.nocapture, benchfn); + runnable_bench.run(id, &desc, &monitor_ch, opts.nocapture); None } - StaticBenchFn(benchfn) => { - // Benchmarks aren't expected to panic, so we run them all in-process. - crate::bench::benchmark(id, desc, monitor_ch, opts.nocapture, benchfn); - None - } - DynTestFn(f) => { - match strategy { - RunStrategy::InProcess => (), - _ => panic!("Cannot run dynamic test fn out-of-process"), - }; - run_test_inner( - id, - desc, - monitor_ch, - Box::new(move || __rust_begin_short_backtrace(f)), - test_run_opts, - ) - } - StaticTestFn(f) => run_test_inner( - id, - desc, - monitor_ch, - Box::new(move || __rust_begin_short_backtrace(f)), - test_run_opts, - ), } } @@ -663,7 +634,7 @@ fn run_test_in_process( desc: TestDesc, nocapture: bool, report_time: bool, - testfn: Box Result<(), String> + Send>, + runnable_test: RunnableTest, monitor_ch: Sender, time_opts: Option, ) { @@ -675,7 +646,7 @@ fn run_test_in_process( } let start = report_time.then(Instant::now); - let result = fold_err(catch_unwind(AssertUnwindSafe(testfn))); + let result = fold_err(catch_unwind(AssertUnwindSafe(|| runnable_test.run()))); let exec_time = start.map(|start| { let duration = start.elapsed(); TestExecTime(duration) @@ -712,6 +683,7 @@ fn spawn_test_subprocess( report_time: bool, monitor_ch: Sender, time_opts: Option, + bench_benchmarks: bool, ) { let (result, test_output, exec_time) = (|| { let args = env::args().collect::>(); @@ -719,6 +691,9 @@ fn spawn_test_subprocess( let mut command = Command::new(current_exe); command.env(SECONDARY_TEST_INVOKER_VAR, desc.name.as_slice()); + if bench_benchmarks { + command.env(SECONDARY_TEST_BENCH_BENCHMARKS_VAR, "1"); + } if nocapture { command.stdout(process::Stdio::inherit()); command.stderr(process::Stdio::inherit()); @@ -760,10 +735,7 @@ fn spawn_test_subprocess( monitor_ch.send(message).unwrap(); } -fn run_test_in_spawned_subprocess( - desc: TestDesc, - testfn: Box Result<(), String> + Send>, -) -> ! { +fn run_test_in_spawned_subprocess(desc: TestDesc, runnable_test: RunnableTest) -> ! { let builtin_panic_hook = panic::take_hook(); let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| { let test_result = match panic_info { @@ -789,7 +761,7 @@ fn run_test_in_spawned_subprocess( }); let record_result2 = record_result.clone(); panic::set_hook(Box::new(move |info| record_result2(Some(info)))); - if let Err(message) = testfn() { + if let Err(message) = runnable_test.run() { panic!("{}", message); } record_result(None); diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index c34583e69..4ef18b14f 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -154,6 +154,7 @@ pub fn ignored_tests_result_in_ignored() { // 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> { panic!(); @@ -184,6 +185,7 @@ fn test_should_panic() { // 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> { panic!("an error message"); @@ -214,6 +216,7 @@ fn test_should_panic_good_message() { // 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; fn f() -> Result<(), String> { @@ -249,6 +252,7 @@ fn test_should_panic_bad_message() { // 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 crate::tests::TrFailedMsg; use std::any::TypeId; @@ -288,6 +292,7 @@ fn test_should_panic_non_string_message_type() { // 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")]; diff --git a/library/test/src/types.rs b/library/test/src/types.rs index e79914dbf..504ceee7f 100644 --- a/library/test/src/types.rs +++ b/library/test/src/types.rs @@ -2,8 +2,11 @@ use std::borrow::Cow; use std::fmt; +use std::sync::mpsc::Sender; +use super::__rust_begin_short_backtrace; use super::bench::Bencher; +use super::event::CompletedTest; use super::options; pub use NamePadding::*; @@ -82,8 +85,10 @@ impl fmt::Display for TestName { pub enum TestFn { StaticTestFn(fn() -> Result<(), String>), StaticBenchFn(fn(&mut Bencher) -> Result<(), String>), + StaticBenchAsTestFn(fn(&mut Bencher) -> Result<(), String>), DynTestFn(Box Result<(), String> + Send>), DynBenchFn(Box Result<(), String> + Send>), + DynBenchAsTestFn(Box Result<(), String> + Send>), } impl TestFn { @@ -91,8 +96,21 @@ impl TestFn { match *self { StaticTestFn(..) => PadNone, StaticBenchFn(..) => PadOnRight, + StaticBenchAsTestFn(..) => PadNone, DynTestFn(..) => PadNone, DynBenchFn(..) => PadOnRight, + DynBenchAsTestFn(..) => PadNone, + } + } + + pub(crate) fn into_runnable(self) -> Runnable { + match self { + StaticTestFn(f) => Runnable::Test(RunnableTest::Static(f)), + StaticBenchFn(f) => Runnable::Bench(RunnableBench::Static(f)), + StaticBenchAsTestFn(f) => Runnable::Test(RunnableTest::StaticBenchAsTest(f)), + DynTestFn(f) => Runnable::Test(RunnableTest::Dynamic(f)), + DynBenchFn(f) => Runnable::Bench(RunnableBench::Dynamic(f)), + DynBenchAsTestFn(f) => Runnable::Test(RunnableTest::DynamicBenchAsTest(f)), } } } @@ -102,12 +120,74 @@ impl fmt::Debug for TestFn { f.write_str(match *self { StaticTestFn(..) => "StaticTestFn(..)", StaticBenchFn(..) => "StaticBenchFn(..)", + StaticBenchAsTestFn(..) => "StaticBenchAsTestFn(..)", DynTestFn(..) => "DynTestFn(..)", DynBenchFn(..) => "DynBenchFn(..)", + DynBenchAsTestFn(..) => "DynBenchAsTestFn(..)", }) } } +pub(crate) enum Runnable { + Test(RunnableTest), + Bench(RunnableBench), +} + +pub(crate) enum RunnableTest { + Static(fn() -> Result<(), String>), + Dynamic(Box Result<(), String> + Send>), + StaticBenchAsTest(fn(&mut Bencher) -> Result<(), String>), + DynamicBenchAsTest(Box Result<(), String> + Send>), +} + +impl RunnableTest { + pub(crate) fn run(self) -> Result<(), String> { + match self { + RunnableTest::Static(f) => __rust_begin_short_backtrace(f), + RunnableTest::Dynamic(f) => __rust_begin_short_backtrace(f), + RunnableTest::StaticBenchAsTest(f) => { + crate::bench::run_once(|b| __rust_begin_short_backtrace(|| f(b))) + } + RunnableTest::DynamicBenchAsTest(f) => { + crate::bench::run_once(|b| __rust_begin_short_backtrace(|| f(b))) + } + } + } + + pub(crate) fn is_dynamic(&self) -> bool { + match self { + RunnableTest::Static(_) => false, + RunnableTest::StaticBenchAsTest(_) => false, + RunnableTest::Dynamic(_) => true, + RunnableTest::DynamicBenchAsTest(_) => true, + } + } +} + +pub(crate) enum RunnableBench { + Static(fn(&mut Bencher) -> Result<(), String>), + Dynamic(Box Result<(), String> + Send>), +} + +impl RunnableBench { + pub(crate) fn run( + self, + id: TestId, + desc: &TestDesc, + monitor_ch: &Sender, + nocapture: bool, + ) { + match self { + RunnableBench::Static(f) => { + crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f) + } + RunnableBench::Dynamic(f) => { + crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f) + } + } + } +} + // A unique integer associated with each test. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct TestId(pub usize); diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index f6a68073b..ec24e1375 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -117,7 +117,7 @@ extern "C" { } cfg_if::cfg_if! { -if #[cfg(any(target_os = "ios", target_os = "watchos", target_os = "netbsd", not(target_arch = "arm")))] { +if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "netbsd", not(target_arch = "arm")))] { // Not ARM EHABI #[repr(C)] #[derive(Copy, Clone, PartialEq)] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 66018a880..73b9b2ba4 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.71.1" +channel = "1.72.0" components = [ "rustfmt" ] targets = [ ] profile = "minimal"