diff options
| author | Laurențiu Nicola <lnicola@dend.ro> | 2024-10-08 14:25:39 +0300 |
|---|---|---|
| committer | Laurențiu Nicola <lnicola@dend.ro> | 2024-10-08 14:25:39 +0300 |
| commit | 4316afffd9390bdac1d3bd5e5398c65d6e9bb150 (patch) | |
| tree | 9d5b39941405bec4b7c4571606df8b88850325e9 /library/alloc/src | |
| parent | 537fb8d1bb2405b118898c663bc076815a1762c7 (diff) | |
| parent | cf24c73141a77db730f4b7fda69dcd7e8b113b51 (diff) | |
Merge from rust-lang/rust
Diffstat (limited to 'library/alloc/src')
| -rw-r--r-- | library/alloc/src/boxed.rs | 11 | ||||
| -rw-r--r-- | library/alloc/src/boxed/thin.rs | 2 | ||||
| -rw-r--r-- | library/alloc/src/collections/binary_heap/mod.rs | 5 | ||||
| -rw-r--r-- | library/alloc/src/collections/btree/node.rs | 10 | ||||
| -rw-r--r-- | library/alloc/src/ffi/c_str.rs | 1 | ||||
| -rw-r--r-- | library/alloc/src/lib.rs | 1 | ||||
| -rw-r--r-- | library/alloc/src/raw_vec.rs | 12 | ||||
| -rw-r--r-- | library/alloc/src/rc.rs | 26 | ||||
| -rw-r--r-- | library/alloc/src/slice.rs | 67 | ||||
| -rw-r--r-- | library/alloc/src/str.rs | 129 | ||||
| -rw-r--r-- | library/alloc/src/string.rs | 38 | ||||
| -rw-r--r-- | library/alloc/src/sync.rs | 23 | ||||
| -rw-r--r-- | library/alloc/src/vec/into_iter.rs | 4 | ||||
| -rw-r--r-- | library/alloc/src/vec/mod.rs | 55 |
14 files changed, 242 insertions, 142 deletions
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index f61a484499f..5f207295683 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -199,7 +199,7 @@ use core::ops::{ DerefPure, DispatchFromDyn, Receiver, }; use core::pin::{Pin, PinCoerceUnsized}; -use core::ptr::{self, NonNull, Unique, addr_of_mut}; +use core::ptr::{self, NonNull, Unique}; use core::task::{Context, Poll}; use core::{borrow, fmt, slice}; @@ -228,6 +228,7 @@ mod thin; #[lang = "owned_box"] #[fundamental] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_insignificant_dtor] // The declaration of the `Box` struct must be kept in sync with the // compiler or ICEs will happen. pub struct Box< @@ -1277,7 +1278,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> { #[inline] pub fn into_raw(b: Self) -> *mut T { // Make sure Miri realizes that we transition from a noalias pointer to a raw pointer here. - unsafe { addr_of_mut!(*&mut *Self::into_raw_with_allocator(b).0) } + unsafe { &raw mut *&mut *Self::into_raw_with_allocator(b).0 } } /// Consumes the `Box`, returning a wrapped `NonNull` pointer. @@ -1396,7 +1397,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> { // want *no* aliasing requirements here! // In case `A` *is* `Global`, this does not quite have the right behavior; `into_raw` // works around that. - let ptr = addr_of_mut!(**b); + let ptr = &raw mut **b; let alloc = unsafe { ptr::read(&b.1) }; (ptr, alloc) } @@ -1506,7 +1507,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> { pub fn as_mut_ptr(b: &mut Self) -> *mut T { // This is a primitive deref, not going through `DerefMut`, and therefore not materializing // any references. - ptr::addr_of_mut!(**b) + &raw mut **b } /// Returns a raw pointer to the `Box`'s contents. @@ -1554,7 +1555,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> { pub fn as_ptr(b: &Self) -> *const T { // This is a primitive deref, not going through `DerefMut`, and therefore not materializing // any references. - ptr::addr_of!(**b) + &raw const **b } /// Returns a reference to the underlying allocator. diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 9baded3a521..78e5aec09b1 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -186,7 +186,7 @@ impl<T: ?Sized> ThinBox<T> { fn with_header(&self) -> &WithHeader<<T as Pointee>::Metadata> { // SAFETY: both types are transparent to `NonNull<u8>` - unsafe { &*(core::ptr::addr_of!(self.ptr) as *const WithHeader<_>) } + unsafe { &*((&raw const self.ptr) as *const WithHeader<_>) } } } diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 5e59abf54ee..59f10b09c73 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -374,7 +374,10 @@ impl<'a, T: Ord, A: Allocator> PeekMut<'a, T, A> { // the caller could've mutated the element. It is removed from the // heap on the next line and pop() is not sensitive to its value. } - this.heap.pop().unwrap() + + // SAFETY: Have a `PeekMut` element proves that the associated binary heap being non-empty, + // so the `pop` operation will not fail. + unsafe { this.heap.pop().unwrap_unchecked() } } } diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 78ccb3af66d..5c513d34fc9 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -72,8 +72,8 @@ impl<K, V> LeafNode<K, V> { // be both slightly faster and easier to track in Valgrind. unsafe { // parent_idx, keys, and vals are all MaybeUninit - ptr::addr_of_mut!((*this).parent).write(None); - ptr::addr_of_mut!((*this).len).write(0); + (&raw mut (*this).parent).write(None); + (&raw mut (*this).len).write(0); } } @@ -114,7 +114,7 @@ impl<K, V> InternalNode<K, V> { unsafe { let mut node = Box::<Self, _>::new_uninit_in(alloc); // We only need to initialize the data; the edges are MaybeUninit. - LeafNode::init(ptr::addr_of_mut!((*node.as_mut_ptr()).data)); + LeafNode::init(&raw mut (*node.as_mut_ptr()).data); node.assume_init() } } @@ -525,8 +525,8 @@ impl<'a, K, V, Type> NodeRef<marker::ValMut<'a>, K, V, Type> { // to avoid aliasing with outstanding references to other elements, // in particular, those returned to the caller in earlier iterations. let leaf = Self::as_leaf_ptr(&mut self); - let keys = unsafe { ptr::addr_of!((*leaf).keys) }; - let vals = unsafe { ptr::addr_of_mut!((*leaf).vals) }; + let keys = unsafe { &raw const (*leaf).keys }; + let vals = unsafe { &raw mut (*leaf).vals }; // We must coerce to unsized array pointers because of Rust issue #74679. let keys: *const [_] = keys; let vals: *mut [_] = vals; diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index 797d591a8b5..d496899e72b 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -696,6 +696,7 @@ impl CString { // memory-unsafe code from working by accident. Inline // to prevent LLVM from optimizing it away in debug builds. #[stable(feature = "cstring_drop", since = "1.13.0")] +#[rustc_insignificant_dtor] impl Drop for CString { #[inline] fn drop(&mut self) { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index ff5ddd16e07..c60c0743c7e 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -115,6 +115,7 @@ #![feature(const_option)] #![feature(const_pin)] #![feature(const_size_of_val)] +#![feature(const_vec_string_slice)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 436e0596e3d..8fdca8c4200 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -280,7 +280,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. #[inline] - pub fn ptr(&self) -> *mut T { + pub const fn ptr(&self) -> *mut T { self.inner.ptr() } @@ -293,7 +293,7 @@ impl<T, A: Allocator> RawVec<T, A> { /// /// This will always be `usize::MAX` if `T` is zero-sized. #[inline] - pub fn capacity(&self) -> usize { + pub const fn capacity(&self) -> usize { self.inner.capacity(size_of::<T>()) } @@ -488,17 +488,17 @@ impl<A: Allocator> RawVecInner<A> { } #[inline] - fn ptr<T>(&self) -> *mut T { + const fn ptr<T>(&self) -> *mut T { self.non_null::<T>().as_ptr() } #[inline] - fn non_null<T>(&self) -> NonNull<T> { - self.ptr.cast().into() + const fn non_null<T>(&self) -> NonNull<T> { + self.ptr.cast().as_non_null_ptr() } #[inline] - fn capacity(&self, elem_size: usize) -> usize { + const fn capacity(&self, elem_size: usize) -> usize { if elem_size == 0 { usize::MAX } else { self.cap.0 } } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 54669bd310a..0e2c35845e8 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -787,7 +787,7 @@ impl<T, A: Allocator> Rc<T, A> { let strong = unsafe { let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).value), data); + ptr::write(&raw mut (*inner).value, data); let prev_value = (*inner).strong.get(); debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); @@ -1442,7 +1442,7 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> { // SAFETY: This cannot go through Deref::deref or Rc::inner because // this is required to retain raw/mut provenance such that e.g. `get_mut` can // write through the pointer after the Rc is recovered through `from_raw`. - unsafe { ptr::addr_of_mut!((*ptr).value) } + unsafe { &raw mut (*ptr).value } } /// Constructs an `Rc<T, A>` from a raw pointer in the provided allocator. @@ -2042,8 +2042,8 @@ impl<T: ?Sized> Rc<T> { unsafe { debug_assert_eq!(Layout::for_value_raw(inner), layout); - ptr::addr_of_mut!((*inner).strong).write(Cell::new(1)); - ptr::addr_of_mut!((*inner).weak).write(Cell::new(1)); + (&raw mut (*inner).strong).write(Cell::new(1)); + (&raw mut (*inner).weak).write(Cell::new(1)); } Ok(inner) @@ -2072,8 +2072,8 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> { // Copy value as bytes ptr::copy_nonoverlapping( - core::ptr::addr_of!(*src) as *const u8, - ptr::addr_of_mut!((*ptr).value) as *mut u8, + (&raw const *src) as *const u8, + (&raw mut (*ptr).value) as *mut u8, value_size, ); @@ -2107,11 +2107,7 @@ impl<T> Rc<[T]> { unsafe fn copy_from_slice(v: &[T]) -> Rc<[T]> { unsafe { let ptr = Self::allocate_for_slice(v.len()); - ptr::copy_nonoverlapping( - v.as_ptr(), - ptr::addr_of_mut!((*ptr).value) as *mut T, - v.len(), - ); + ptr::copy_nonoverlapping(v.as_ptr(), (&raw mut (*ptr).value) as *mut T, v.len()); Self::from_ptr(ptr) } } @@ -2149,7 +2145,7 @@ impl<T> Rc<[T]> { let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = ptr::addr_of_mut!((*ptr).value) as *mut T; + let elems = (&raw mut (*ptr).value) as *mut T; let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; @@ -2577,7 +2573,7 @@ impl<T: ?Sized + fmt::Debug, A: Allocator> fmt::Debug for Rc<T, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized, A: Allocator> fmt::Pointer for Rc<T, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&core::ptr::addr_of!(**self), f) + fmt::Pointer::fmt(&(&raw const **self), f) } } @@ -2718,7 +2714,7 @@ impl<T, A: Allocator> From<Vec<T, A>> for Rc<[T], A> { let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_alloc(); let rc_ptr = Self::allocate_for_slice_in(len, &alloc); - ptr::copy_nonoverlapping(vec_ptr, ptr::addr_of_mut!((*rc_ptr).value) as *mut T, len); + ptr::copy_nonoverlapping(vec_ptr, (&raw mut (*rc_ptr).value) as *mut T, len); // Create a `Vec<T, &A>` with length 0, to deallocate the buffer // without dropping its contents or the allocator @@ -3084,7 +3080,7 @@ impl<T: ?Sized, A: Allocator> Weak<T, A> { // SAFETY: if is_dangling returns false, then the pointer is dereferenceable. // The payload may be dropped at this point, and we have to maintain provenance, // so use raw pointer manipulation. - unsafe { ptr::addr_of_mut!((*ptr).value) } + unsafe { &raw mut (*ptr).value } } } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index f636f10d5c0..a92d22b1c30 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -180,10 +180,9 @@ impl<T> [T] { /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. /// - /// If the implementation of [`Ord`] for `T` does not implement a [total order] the resulting - /// order of elements in the slice is unspecified. All original elements will remain in the - /// slice and any possible modifications via interior mutability are observed in the input. Same - /// is true if the implementation of [`Ord`] for `T` panics. + /// If the implementation of [`Ord`] for `T` does not implement a [total order], the function + /// may panic; even if the function exits normally, the resulting order of elements in the slice + /// is unspecified. See also the note on panicking below. /// /// When applicable, unstable sorting is preferred because it is generally faster than stable /// sorting and it doesn't allocate auxiliary memory. See @@ -212,7 +211,15 @@ impl<T> [T] { /// /// # Panics /// - /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order], or if + /// the [`Ord`] implementation itself panics. + /// + /// All safe functions on slices preserve the invariant that even if the function panics, all + /// original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. This ensures that recovery code (for instance inside + /// of a `Drop` or following a `catch_unwind`) will still have access to all the original + /// elements. For instance, if the slice belongs to a `Vec`, the `Vec::drop` method will be able + /// to dispose of all contained elements. /// /// # Examples /// @@ -241,10 +248,9 @@ impl<T> [T] { /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. /// - /// If the comparison function `compare` does not implement a [total order] the resulting order - /// of elements in the slice is unspecified. All original elements will remain in the slice and - /// any possible modifications via interior mutability are observed in the input. Same is true - /// if `compare` panics. + /// If the comparison function `compare` does not implement a [total order], the function may + /// panic; even if the function exits normally, the resulting order of elements in the slice is + /// unspecified. See also the note on panicking below. /// /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and @@ -263,7 +269,14 @@ impl<T> [T] { /// /// # Panics /// - /// May panic if `compare` does not implement a [total order]. + /// May panic if `compare` does not implement a [total order], or if `compare` itself panics. + /// + /// All safe functions on slices preserve the invariant that even if the function panics, all + /// original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. This ensures that recovery code (for instance inside + /// of a `Drop` or following a `catch_unwind`) will still have access to all the original + /// elements. For instance, if the slice belongs to a `Vec`, the `Vec::drop` method will be able + /// to dispose of all contained elements. /// /// # Examples /// @@ -295,10 +308,9 @@ impl<T> [T] { /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*)) /// worst-case, where the key function is *O*(*m*). /// - /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting - /// order of elements in the slice is unspecified. All original elements will remain in the - /// slice and any possible modifications via interior mutability are observed in the input. Same - /// is true if the implementation of [`Ord`] for `K` panics. + /// If the implementation of [`Ord`] for `K` does not implement a [total order], the function + /// may panic; even if the function exits normally, the resulting order of elements in the slice + /// is unspecified. See also the note on panicking below. /// /// # Current implementation /// @@ -313,7 +325,15 @@ impl<T> [T] { /// /// # Panics /// - /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order], or if + /// the [`Ord`] implementation or the key-function `f` panics. + /// + /// All safe functions on slices preserve the invariant that even if the function panics, all + /// original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. This ensures that recovery code (for instance inside + /// of a `Drop` or following a `catch_unwind`) will still have access to all the original + /// elements. For instance, if the slice belongs to a `Vec`, the `Vec::drop` method will be able + /// to dispose of all contained elements. /// /// # Examples /// @@ -347,10 +367,9 @@ impl<T> [T] { /// storage to remember the results of key evaluation. The order of calls to the key function is /// unspecified and may change in future versions of the standard library. /// - /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting - /// order of elements in the slice is unspecified. All original elements will remain in the - /// slice and any possible modifications via interior mutability are observed in the input. Same - /// is true if the implementation of [`Ord`] for `K` panics. + /// If the implementation of [`Ord`] for `K` does not implement a [total order], the function + /// may panic; even if the function exits normally, the resulting order of elements in the slice + /// is unspecified. See also the note on panicking below. /// /// For simple key functions (e.g., functions that are property accesses or basic operations), /// [`sort_by_key`](slice::sort_by_key) is likely to be faster. @@ -369,7 +388,15 @@ impl<T> [T] { /// /// # Panics /// - /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order], or if + /// the [`Ord`] implementation panics. + /// + /// All safe functions on slices preserve the invariant that even if the function panics, all + /// original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. This ensures that recovery code (for instance inside + /// of a `Drop` or following a `catch_unwind`) will still have access to all the original + /// elements. For instance, if the slice belongs to a `Vec`, the `Vec::drop` method will be able + /// to dispose of all contained elements. /// /// # Examples /// diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 32212b61c6e..42501f9c315 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -9,6 +9,7 @@ use core::borrow::{Borrow, BorrowMut}; use core::iter::FusedIterator; +use core::mem::MaybeUninit; #[stable(feature = "encode_utf16", since = "1.8.0")] pub use core::str::EncodeUtf16; #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] @@ -365,14 +366,9 @@ impl str { without modifying the original"] #[stable(feature = "unicode_case_mapping", since = "1.2.0")] pub fn to_lowercase(&self) -> String { - let out = convert_while_ascii(self.as_bytes(), u8::to_ascii_lowercase); + let (mut s, rest) = convert_while_ascii(self, u8::to_ascii_lowercase); - // Safety: we know this is a valid char boundary since - // out.len() is only progressed if ascii bytes are found - let rest = unsafe { self.get_unchecked(out.len()..) }; - - // Safety: We have written only valid ASCII to our vec - let mut s = unsafe { String::from_utf8_unchecked(out) }; + let prefix_len = s.len(); for (i, c) in rest.char_indices() { if c == 'Σ' { @@ -381,8 +377,7 @@ impl str { // in `SpecialCasing.txt`, // so hard-code it rather than have a generic "condition" mechanism. // See https://github.com/rust-lang/rust/issues/26035 - let out_len = self.len() - rest.len(); - let sigma_lowercase = map_uppercase_sigma(&self, i + out_len); + let sigma_lowercase = map_uppercase_sigma(self, prefix_len + i); s.push(sigma_lowercase); } else { match conversions::to_lower(c) { @@ -458,14 +453,7 @@ impl str { without modifying the original"] #[stable(feature = "unicode_case_mapping", since = "1.2.0")] pub fn to_uppercase(&self) -> String { - let out = convert_while_ascii(self.as_bytes(), u8::to_ascii_uppercase); - - // Safety: we know this is a valid char boundary since - // out.len() is only progressed if ascii bytes are found - let rest = unsafe { self.get_unchecked(out.len()..) }; - - // Safety: We have written only valid ASCII to our vec - let mut s = unsafe { String::from_utf8_unchecked(out) }; + let (mut s, rest) = convert_while_ascii(self, u8::to_ascii_uppercase); for c in rest.chars() { match conversions::to_upper(c) { @@ -614,50 +602,87 @@ pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box<str> { unsafe { Box::from_raw(Box::into_raw(v) as *mut str) } } -/// Converts the bytes while the bytes are still ascii. +/// Converts leading ascii bytes in `s` by calling the `convert` function. +/// /// For better average performance, this happens in chunks of `2*size_of::<usize>()`. -/// Returns a vec with the converted bytes. +/// +/// Returns a tuple of the converted prefix and the remainder starting from +/// the first non-ascii character. +/// +/// This function is only public so that it can be verified in a codegen test, +/// see `issue-123712-str-to-lower-autovectorization.rs`. +#[unstable(feature = "str_internals", issue = "none")] +#[doc(hidden)] #[inline] #[cfg(not(test))] #[cfg(not(no_global_oom_handling))] -fn convert_while_ascii(b: &[u8], convert: fn(&u8) -> u8) -> Vec<u8> { - let mut out = Vec::with_capacity(b.len()); +pub fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, &str) { + // Process the input in chunks of 16 bytes to enable auto-vectorization. + // Previously the chunk size depended on the size of `usize`, + // but on 32-bit platforms with sse or neon is also the better choice. + // The only downside on other platforms would be a bit more loop-unrolling. + const N: usize = 16; + + let mut slice = s.as_bytes(); + let mut out = Vec::with_capacity(slice.len()); + let mut out_slice = out.spare_capacity_mut(); + + let mut ascii_prefix_len = 0_usize; + let mut is_ascii = [false; N]; + + while slice.len() >= N { + // SAFETY: checked in loop condition + let chunk = unsafe { slice.get_unchecked(..N) }; + // SAFETY: out_slice has at least same length as input slice and gets sliced with the same offsets + let out_chunk = unsafe { out_slice.get_unchecked_mut(..N) }; + + for j in 0..N { + is_ascii[j] = chunk[j] <= 127; + } - const USIZE_SIZE: usize = mem::size_of::<usize>(); - const MAGIC_UNROLL: usize = 2; - const N: usize = USIZE_SIZE * MAGIC_UNROLL; - const NONASCII_MASK: usize = usize::from_ne_bytes([0x80; USIZE_SIZE]); + // Auto-vectorization for this check is a bit fragile, sum and comparing against the chunk + // size gives the best result, specifically a pmovmsk instruction on x86. + // See https://github.com/llvm/llvm-project/issues/96395 for why llvm currently does not + // currently recognize other similar idioms. + if is_ascii.iter().map(|x| *x as u8).sum::<u8>() as usize != N { + break; + } - let mut i = 0; - unsafe { - while i + N <= b.len() { - // Safety: we have checks the sizes `b` and `out` to know that our - let in_chunk = b.get_unchecked(i..i + N); - let out_chunk = out.spare_capacity_mut().get_unchecked_mut(i..i + N); - - let mut bits = 0; - for j in 0..MAGIC_UNROLL { - // read the bytes 1 usize at a time (unaligned since we haven't checked the alignment) - // safety: in_chunk is valid bytes in the range - bits |= in_chunk.as_ptr().cast::<usize>().add(j).read_unaligned(); - } - // if our chunks aren't ascii, then return only the prior bytes as init - if bits & NONASCII_MASK != 0 { - break; - } + for j in 0..N { + out_chunk[j] = MaybeUninit::new(convert(&chunk[j])); + } - // perform the case conversions on N bytes (gets heavily autovec'd) - for j in 0..N { - // safety: in_chunk and out_chunk is valid bytes in the range - let out = out_chunk.get_unchecked_mut(j); - out.write(convert(in_chunk.get_unchecked(j))); - } + ascii_prefix_len += N; + slice = unsafe { slice.get_unchecked(N..) }; + out_slice = unsafe { out_slice.get_unchecked_mut(N..) }; + } - // mark these bytes as initialised - i += N; + // handle the remainder as individual bytes + while slice.len() > 0 { + let byte = slice[0]; + if byte > 127 { + break; + } + // SAFETY: out_slice has at least same length as input slice + unsafe { + *out_slice.get_unchecked_mut(0) = MaybeUninit::new(convert(&byte)); } - out.set_len(i); + ascii_prefix_len += 1; + slice = unsafe { slice.get_unchecked(1..) }; + out_slice = unsafe { out_slice.get_unchecked_mut(1..) }; } - out + unsafe { + // SAFETY: ascii_prefix_len bytes have been initialized above + out.set_len(ascii_prefix_len); + + // SAFETY: We have written only valid ascii to the output vec + let ascii_string = String::from_utf8_unchecked(out); + + // SAFETY: we know this is a valid char boundary + // since we only skipped over leading ascii bytes + let rest = core::str::from_utf8_unchecked(slice); + + (ascii_string, rest) + } } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index ee878e879e9..82dbf030608 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1059,7 +1059,8 @@ impl String { #[inline] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_bytes(self) -> Vec<u8> { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn into_bytes(self) -> Vec<u8> { self.vec } @@ -1076,8 +1077,11 @@ impl String { #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_str")] - pub fn as_str(&self) -> &str { - self + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_str(&self) -> &str { + // SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error + // at construction. + unsafe { str::from_utf8_unchecked(self.vec.as_slice()) } } /// Converts a `String` into a mutable string slice. @@ -1096,8 +1100,11 @@ impl String { #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_mut_str")] - pub fn as_mut_str(&mut self) -> &mut str { - self + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_mut_str(&mut self) -> &mut str { + // SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error + // at construction. + unsafe { str::from_utf8_unchecked_mut(self.vec.as_mut_slice()) } } /// Appends a given string slice onto the end of this `String`. @@ -1168,7 +1175,8 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn capacity(&self) -> usize { self.vec.capacity() } @@ -1431,8 +1439,9 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - pub fn as_bytes(&self) -> &[u8] { - &self.vec + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_bytes(&self) -> &[u8] { + self.vec.as_slice() } /// Shortens this `String` to the specified length. @@ -1784,7 +1793,8 @@ impl String { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> { &mut self.vec } @@ -1805,8 +1815,9 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_confusables("length", "size")] - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.vec.len() } @@ -1824,7 +1835,8 @@ impl String { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -2589,7 +2601,7 @@ impl ops::Deref for String { #[inline] fn deref(&self) -> &str { - unsafe { str::from_utf8_unchecked(&self.vec) } + self.as_str() } } @@ -2600,7 +2612,7 @@ unsafe impl ops::DerefPure for String {} impl ops::DerefMut for String { #[inline] fn deref_mut(&mut self) -> &mut str { - unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } + self.as_mut_str() } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 50886244d58..5d099a49854 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -237,6 +237,7 @@ macro_rules! acquire { /// [rc_examples]: crate::rc#examples #[cfg_attr(not(test), rustc_diagnostic_item = "Arc")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_insignificant_dtor] pub struct Arc< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, @@ -797,7 +798,7 @@ impl<T, A: Allocator> Arc<T, A> { // reference into a strong reference. let strong = unsafe { let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).data), data); + ptr::write(&raw mut (*inner).data, data); // The above write to the data field must be visible to any threads which // observe a non-zero strong count. Therefore we need at least "Release" ordering @@ -1583,7 +1584,7 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> { // SAFETY: This cannot go through Deref::deref or RcBoxPtr::inner because // this is required to retain raw/mut provenance such that e.g. `get_mut` can // write through the pointer after the Rc is recovered through `from_raw`. - unsafe { ptr::addr_of_mut!((*ptr).data) } + unsafe { &raw mut (*ptr).data } } /// Constructs an `Arc<T, A>` from a raw pointer. @@ -1955,8 +1956,8 @@ impl<T: ?Sized> Arc<T> { debug_assert_eq!(unsafe { Layout::for_value_raw(inner) }, layout); unsafe { - ptr::addr_of_mut!((*inner).strong).write(atomic::AtomicUsize::new(1)); - ptr::addr_of_mut!((*inner).weak).write(atomic::AtomicUsize::new(1)); + (&raw mut (*inner).strong).write(atomic::AtomicUsize::new(1)); + (&raw mut (*inner).weak).write(atomic::AtomicUsize::new(1)); } inner @@ -1986,8 +1987,8 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> { // Copy value as bytes ptr::copy_nonoverlapping( - core::ptr::addr_of!(*src) as *const u8, - ptr::addr_of_mut!((*ptr).data) as *mut u8, + (&raw const *src) as *const u8, + (&raw mut (*ptr).data) as *mut u8, value_size, ); @@ -2022,7 +2023,7 @@ impl<T> Arc<[T]> { unsafe { let ptr = Self::allocate_for_slice(v.len()); - ptr::copy_nonoverlapping(v.as_ptr(), ptr::addr_of_mut!((*ptr).data) as *mut T, v.len()); + ptr::copy_nonoverlapping(v.as_ptr(), (&raw mut (*ptr).data) as *mut T, v.len()); Self::from_ptr(ptr) } @@ -2061,7 +2062,7 @@ impl<T> Arc<[T]> { let layout = Layout::for_value_raw(ptr); // Pointer to first element - let elems = ptr::addr_of_mut!((*ptr).data) as *mut T; + let elems = (&raw mut (*ptr).data) as *mut T; let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; @@ -2805,7 +2806,7 @@ impl<T: ?Sized, A: Allocator> Weak<T, A> { // SAFETY: if is_dangling returns false, then the pointer is dereferenceable. // The payload may be dropped at this point, and we have to maintain provenance, // so use raw pointer manipulation. - unsafe { ptr::addr_of_mut!((*ptr).data) } + unsafe { &raw mut (*ptr).data } } } @@ -3428,7 +3429,7 @@ impl<T: ?Sized + fmt::Debug, A: Allocator> fmt::Debug for Arc<T, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized, A: Allocator> fmt::Pointer for Arc<T, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&core::ptr::addr_of!(**self), f) + fmt::Pointer::fmt(&(&raw const **self), f) } } @@ -3678,7 +3679,7 @@ impl<T, A: Allocator + Clone> From<Vec<T, A>> for Arc<[T], A> { let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_alloc(); let rc_ptr = Self::allocate_for_slice_in(len, &alloc); - ptr::copy_nonoverlapping(vec_ptr, ptr::addr_of_mut!((*rc_ptr).data) as *mut T, len); + ptr::copy_nonoverlapping(vec_ptr, (&raw mut (*rc_ptr).data) as *mut T, len); // Create a `Vec<T, &A>` with length 0, to deallocate the buffer // without dropping its contents or the allocator diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index f7aad56695d..9a6745fdbc0 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -21,11 +21,11 @@ use crate::raw_vec::RawVec; macro non_null { (mut $place:expr, $t:ident) => {{ #![allow(unused_unsafe)] // we're sometimes used within an unsafe block - unsafe { &mut *(ptr::addr_of_mut!($place) as *mut NonNull<$t>) } + unsafe { &mut *((&raw mut $place) as *mut NonNull<$t>) } }}, ($place:expr, $t:ident) => {{ #![allow(unused_unsafe)] // we're sometimes used within an unsafe block - unsafe { *(ptr::addr_of!($place) as *const NonNull<$t>) } + unsafe { *((&raw const $place) as *const NonNull<$t>) } }}, } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 1984cfeefc1..830512ceee8 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1240,7 +1240,8 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn capacity(&self) -> usize { self.buf.capacity() } @@ -1548,8 +1549,22 @@ impl<T, A: Allocator> Vec<T, A> { #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_slice")] - pub fn as_slice(&self) -> &[T] { - self + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_slice(&self) -> &[T] { + // SAFETY: `slice::from_raw_parts` requires pointee is a contiguous, aligned buffer of size + // `len` containing properly-initialized `T`s. Data must not be mutated for the returned + // lifetime. Further, `len * mem::size_of::<T>` <= `ISIZE::MAX`, and allocation does not + // "wrap" through overflowing memory addresses. + // + // * Vec API guarantees that self.buf: + // * contains only properly-initialized items within 0..len + // * is aligned, contiguous, and valid for `len` reads + // * obeys size and address-wrapping constraints + // + // * We only construct `&mut` references to `self.buf` through `&mut self` methods; borrow- + // check ensures that it is not possible to mutably alias `self.buf` within the + // returned lifetime. + unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } } /// Extracts a mutable slice of the entire vector. @@ -1566,8 +1581,22 @@ impl<T, A: Allocator> Vec<T, A> { #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_mut_slice")] - pub fn as_mut_slice(&mut self) -> &mut [T] { - self + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn as_mut_slice(&mut self) -> &mut [T] { + // SAFETY: `slice::from_raw_parts_mut` requires pointee is a contiguous, aligned buffer of + // size `len` containing properly-initialized `T`s. Data must not be accessed through any + // other pointer for the returned lifetime. Further, `len * mem::size_of::<T>` <= + // `ISIZE::MAX` and allocation does not "wrap" through overflowing memory addresses. + // + // * Vec API guarantees that self.buf: + // * contains only properly-initialized items within 0..len + // * is aligned, contiguous, and valid for `len` reads + // * obeys size and address-wrapping constraints + // + // * We only construct references to `self.buf` through `&self` and `&mut self` methods; + // borrow-check ensures that it is not possible to construct a reference to `self.buf` + // within the returned lifetime. + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } } /// Returns a raw pointer to the vector's buffer, or a dangling raw pointer @@ -1624,9 +1653,10 @@ impl<T, A: Allocator> Vec<T, A> { /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_never_returns_null_ptr] #[inline] - pub fn as_ptr(&self) -> *const T { + pub const fn as_ptr(&self) -> *const T { // We shadow the slice method of the same name to avoid going through // `deref`, which creates an intermediate reference. self.buf.ptr() @@ -1685,9 +1715,10 @@ impl<T, A: Allocator> Vec<T, A> { /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null #[stable(feature = "vec_as_ptr", since = "1.37.0")] + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_never_returns_null_ptr] #[inline] - pub fn as_mut_ptr(&mut self) -> *mut T { + pub const fn as_mut_ptr(&mut self) -> *mut T { // We shadow the slice method of the same name to avoid going through // `deref_mut`, which creates an intermediate reference. self.buf.ptr() @@ -2628,8 +2659,9 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] #[rustc_confusables("length", "size")] - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.len } @@ -2646,7 +2678,8 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "vec_is_empty")] - pub fn is_empty(&self) -> bool { + #[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")] + pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -3197,7 +3230,7 @@ impl<T, A: Allocator> ops::Deref for Vec<T, A> { #[inline] fn deref(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } + self.as_slice() } } @@ -3205,7 +3238,7 @@ impl<T, A: Allocator> ops::Deref for Vec<T, A> { impl<T, A: Allocator> ops::DerefMut for Vec<T, A> { #[inline] fn deref_mut(&mut self) -> &mut [T] { - unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } + self.as_mut_slice() } } |
