From ae080ebed66f667b7ff68476bdb1976da99beffe Mon Sep 17 00:00:00 2001 From: vinDelphini Date: Sun, 25 Jan 2026 19:15:33 +0530 Subject: [PATCH 01/15] Optimize ArrayChunks by adding next_chunk_back to DoubleEndedIterator --- library/core/src/array/mod.rs | 100 ++++++++++++++++++ .../core/src/iter/adapters/array_chunks.rs | 12 +-- library/core/src/iter/traits/double_ended.rs | 15 +++ 3 files changed, 118 insertions(+), 9 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 5784dbed03634..13f798b56490c 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -969,6 +969,53 @@ const impl Drop for Guard<'_, T> { } } +/// Panic guard for incremental initialization of arrays from the back. +/// +/// Elements of the array are populated starting from the end towards the beginning. +/// Disarm the guard with `mem::forget` once the array has been fully initialized. +/// +/// # Safety +/// +/// All write accesses to this structure are unsafe and must maintain a correct +/// count of `initialized` elements. +struct GuardBack<'a, T> { + /// The array to be initialized. + pub array_mut: &'a mut [MaybeUninit], + /// The number of items that have been initialized so far. + pub initialized: usize, +} + +impl GuardBack<'_, T> { + /// Adds an item to the array and updates the initialized item counter. + /// + /// # Safety + /// + /// No more than N elements must be initialized. + #[inline] + pub(crate) unsafe fn push_unchecked(&mut self, item: T) { + // SAFETY: If `initialized` was correct before and the caller does not + // invoke this method more than N times, then writes will be in-bounds + // and slots will not be initialized more than once. + unsafe { + self.initialized = self.initialized.unchecked_add(1); + let index = self.array_mut.len().unchecked_sub(self.initialized); + self.array_mut.get_unchecked_mut(index).write(item); + } + } +} + +impl Drop for GuardBack<'_, T> { + #[inline] + fn drop(&mut self) { + debug_assert!(self.initialized <= self.array_mut.len()); + let len = self.array_mut.len(); + // SAFETY: this slice will contain only initialized objects. + unsafe { + self.array_mut.get_unchecked_mut(len - self.initialized..len).assume_init_drop(); + } + } +} + /// Pulls `N` items from `iter` and returns them as an array. If the iterator /// yields fewer than `N` items, `Err` is returned containing an iterator over /// the already yielded items. @@ -1076,3 +1123,56 @@ const fn iter_next_chunk_erased( mem::forget(guard); Ok(()) } + +/// Pulls `N` items from the back of `iter` and returns them as an array. +/// If the iterator yields fewer than `N` items, `Err` is returned containing +/// an iterator over the already yielded items. +/// +/// Since the iterator is passed as a mutable reference and this function calls +/// `next_back` at most `N` times, the iterator can still be used afterwards to +/// retrieve the remaining items. +/// +/// If `iter.next_back()` panics, all items already yielded by the iterator are +/// dropped. +/// +/// Used for [`DoubleEndedIterator::next_chunk_back`]. +#[inline] +pub(crate) fn iter_next_chunk_back( + iter: &mut impl DoubleEndedIterator, +) -> Result<[T; N], IntoIter> { + let mut array = [const { MaybeUninit::uninit() }; N]; + let r = iter_next_chunk_back_erased(&mut array, iter); + match r { + Ok(()) => { + // SAFETY: All elements of `array` were populated. + Ok(unsafe { MaybeUninit::array_assume_init(array) }) + } + Err(initialized) => { + // SAFETY: Only the last `initialized` elements were populated + Err(unsafe { IntoIter::new_unchecked(array, N - initialized..N) }) + } + } +} + +/// Version of [`iter_next_chunk_back`] using a passed-in slice. +#[inline] +fn iter_next_chunk_back_erased( + buffer: &mut [MaybeUninit], + iter: &mut impl DoubleEndedIterator, +) -> Result<(), usize> { + // if `Iterator::next_back` panics, this guard will drop already initialized items + let mut guard = GuardBack { array_mut: buffer, initialized: 0 }; + while guard.initialized < guard.array_mut.len() { + let Some(item) = iter.next_back() else { + let initialized = guard.initialized; + mem::forget(guard); + return Err(initialized); + }; + + // SAFETY: The loop condition ensures we have space to push the item + unsafe { guard.push_unchecked(item) }; + } + + mem::forget(guard); + Ok(()) +} diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index a3e5ae5e7698b..6c502ee75e17f 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -1,8 +1,6 @@ use crate::array; use crate::iter::adapters::SourceIter; -use crate::iter::{ - ByRefSized, FusedIterator, InPlaceIterable, TrustedFused, TrustedRandomAccessNoCoerce, -}; +use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedRandomAccessNoCoerce}; use crate::num::NonZero; use crate::ops::{ControlFlow, NeverShortCircuit, Try}; @@ -128,15 +126,11 @@ where self.next_back_remainder(); let mut acc = init; - let mut iter = ByRefSized(&mut self.iter).rev(); // NB remainder is handled by `next_back_remainder`, so - // `next_chunk` can't return `Err` with non-empty remainder + // `next_chunk_back` can't return `Err` with non-empty remainder // (assuming correct `I as ExactSizeIterator` impl). - while let Ok(mut chunk) = iter.next_chunk() { - // FIXME: do not do double reverse - // (we could instead add `next_chunk_back` for example) - chunk.reverse(); + while let Ok(chunk) = self.iter.next_chunk_back() { acc = f(acc, chunk)? } diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index ce80874a23897..84e22959a0728 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -1,4 +1,5 @@ use crate::marker::Destruct; +use crate::array; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; @@ -95,6 +96,20 @@ pub const trait DoubleEndedIterator: [const] Iterator { #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option; + /// Pulls `N` items from the back of the iterator and returns them as an array. + /// + /// See [`Iterator::next_chunk`] for more details. + #[inline] + #[unstable(feature = "iter_next_chunk", issue = "98326")] + fn next_chunk_back( + &mut self, + ) -> Result<[Self::Item; N], array::IntoIter> + where + Self: Sized, + { + crate::array::iter_next_chunk_back(self) + } + /// Advances the iterator from the back by `n` elements. /// /// `advance_back_by` is the reverse version of [`advance_by`]. This method will From f72b3025234113b85e25dc44bdb989f8702c7042 Mon Sep 17 00:00:00 2001 From: vinDelphini Date: Wed, 28 Jan 2026 07:04:38 +0530 Subject: [PATCH 02/15] library: Improve docs for GuardBack and iter_next_chunk_erased Clarify that GuardBack initializes arrays from the end. Restore performance note for iter_next_chunk_erased regarding optimization issues. --- library/core/src/array/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 13f798b56490c..f3ba9f298acbf 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -979,7 +979,7 @@ const impl Drop for Guard<'_, T> { /// All write accesses to this structure are unsafe and must maintain a correct /// count of `initialized` elements. struct GuardBack<'a, T> { - /// The array to be initialized. + /// The array to be initialized (will be filled from the end). pub array_mut: &'a mut [MaybeUninit], /// The number of items that have been initialized so far. pub initialized: usize, From 91e0e59b57042d6a007458c5e86893ff4bee92a1 Mon Sep 17 00:00:00 2001 From: Vin <109163559+vinDelphini@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:43:02 +0530 Subject: [PATCH 03/15] Update library/core/src/array/mod.rs I copied the impl block from Guard without noticing that Destruct was only there for const contexts. Consumed the suggestion, thanks. Co-authored-by: Chayim Refael Friedman --- library/core/src/array/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index f3ba9f298acbf..cc99dae1d04f5 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -1004,7 +1004,7 @@ impl GuardBack<'_, T> { } } -impl Drop for GuardBack<'_, T> { +impl Drop for GuardBack<'_, T> { #[inline] fn drop(&mut self) { debug_assert!(self.initialized <= self.array_mut.len()); From bfd1ba63cc1753ee4ebd0ad14267ac2a0503ae4e Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Mon, 18 May 2026 22:59:08 -0400 Subject: [PATCH 04/15] Introduced specialization to DoubleEndedIterator::next_chunk_back, introduced next_chunk_back implementation in IntoIter, updated documentation, and added test cases for DoubleEndedIterator::next_chunk_back --- library/alloc/src/lib.rs | 1 + library/alloc/src/vec/into_iter.rs | 41 ++++++++ library/alloctests/tests/vec.rs | 17 ++++ library/core/src/array/mod.rs | 99 +++++++++++++++---- .../core/src/iter/adapters/array_chunks.rs | 12 ++- library/core/src/iter/traits/double_ended.rs | 38 ++++++- .../coretests/tests/iter/adapters/filter.rs | 12 +++ .../coretests/tests/iter/traits/iterator.rs | 34 +++++++ 8 files changed, 230 insertions(+), 24 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index f66dc648f809b..95dc40aa04a83 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -136,6 +136,7 @@ #![feature(legacy_receiver_trait)] #![feature(likely_unlikely)] #![feature(local_waker)] +#![feature(maybe_uninit_array_assume_init)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(panic_internals)] #![feature(pattern)] diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 97e4912596b94..b7fcecf11e059 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -445,6 +445,47 @@ impl DoubleEndedIterator for IntoIter { } } + #[inline] + fn next_chunk_back(&mut self) -> Result<[T; N], core::array::IntoIter> { + let mut raw_ary = [const { MaybeUninit::uninit() }; N]; + + let len = self.len(); + + if T::IS_ZST { + if len < N { + self.forget_remaining_elements(); + // Safety: ZSTs can be conjured ex nihilo, only the amount has to be correct + return Err(unsafe { array::IntoIter::new_unchecked(raw_ary, N - len..N) }); + } + + self.end = self.end.wrapping_byte_sub(N); + // Safety: ditto + return Ok(unsafe { MaybeUninit::array_assume_init(raw_ary) }); + } + + if len < N { + // Safety: `len` indicates that this many elements are available and we just checked that + // it fits into the array. + unsafe { + ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, len); + self.forget_remaining_elements(); + return Err(array::IntoIter::new_unchecked(raw_ary, 0..len)); + } + } + + // Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize + // the array. + unsafe { + ptr::copy_nonoverlapping( + self.ptr.add(len - N).as_ptr(), + raw_ary.as_mut_ptr() as *mut T, + N, + ); + self.end = self.end.sub(N); + Ok(MaybeUninit::array_assume_init(raw_ary)) + } + } + #[inline] fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { let step_size = self.len().min(n); diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index ccf9b8095b290..fc58e4364fe66 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -1027,6 +1027,15 @@ fn test_into_iter_next_chunk() { assert_eq!(iter.next_chunk::<4>().unwrap_err().as_slice(), &[]); // N is explicitly 4 } +#[test] +fn test_into_iter_next_chunk_back() { + let mut iter = b"lorem".to_vec().into_iter(); + + assert_eq!(iter.next_chunk_back().unwrap(), [b'e', b'm']); // N is inferred as 2 + assert_eq!(iter.next_chunk_back().unwrap(), [b'l', b'o', b'r']); // N is inferred as 3 + assert_eq!(iter.next_chunk_back::<4>().unwrap_err().as_slice(), &[]); // N is explicitly 4 +} + #[test] fn test_into_iter_clone() { fn iter_equal>(it: I, slice: &[i32]) { @@ -1131,6 +1140,14 @@ fn test_into_iter_zst() { let mut it = vec![C, C].into_iter(); it.next_chunk::<4>().unwrap_err(); drop(it); + + let mut it = vec![C, C].into_iter(); + it.next_chunk_back::<1>().unwrap(); + drop(it); + + let mut it = vec![C, C].into_iter(); + it.next_chunk_back::<4>().unwrap_err(); + drop(it); } #[test] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index cc99dae1d04f5..07d5591bbe9b5 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -991,20 +991,23 @@ impl GuardBack<'_, T> { /// # Safety /// /// No more than N elements must be initialized. + #[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] #[inline] - pub(crate) unsafe fn push_unchecked(&mut self, item: T) { + pub(crate) const unsafe fn push_unchecked(&mut self, item: T) { // SAFETY: If `initialized` was correct before and the caller does not // invoke this method more than N times, then writes will be in-bounds // and slots will not be initialized more than once. unsafe { - self.initialized = self.initialized.unchecked_add(1); - let index = self.array_mut.len().unchecked_sub(self.initialized); + let offset = self.initialized.unchecked_add(1); + let index = self.array_mut.len().unchecked_sub(offset); self.array_mut.get_unchecked_mut(index).write(item); + self.initialized = offset; } } } -impl Drop for GuardBack<'_, T> { +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +const impl Drop for GuardBack<'_, T> { #[inline] fn drop(&mut self) { debug_assert!(self.initialized <= self.array_mut.len()); @@ -1136,29 +1139,89 @@ const fn iter_next_chunk_erased( /// dropped. /// /// Used for [`DoubleEndedIterator::next_chunk_back`]. +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] #[inline] -pub(crate) fn iter_next_chunk_back( - iter: &mut impl DoubleEndedIterator, +pub(crate) const fn iter_next_chunk_back( + iter: &mut impl [const] DoubleEndedIterator, ) -> Result<[T; N], IntoIter> { - let mut array = [const { MaybeUninit::uninit() }; N]; - let r = iter_next_chunk_back_erased(&mut array, iter); - match r { - Ok(()) => { - // SAFETY: All elements of `array` were populated. - Ok(unsafe { MaybeUninit::array_assume_init(array) }) + iter.spec_next_chunk_back() +} + +pub(crate) const trait SpecNextChunkBack: + DoubleEndedIterator +{ + fn spec_next_chunk_back(&mut self) -> Result<[T; N], IntoIter>; +} + +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +const impl, T, const N: usize> SpecNextChunkBack + for I +{ + #[inline] + default fn spec_next_chunk_back(&mut self) -> Result<[T; N], IntoIter> { + let mut array = [const { MaybeUninit::uninit() }; N]; + let r = iter_next_chunk_back_erased(&mut array, self); + match r { + Ok(()) => { + // SAFETY: All elements of `array` were populated. + Ok(unsafe { MaybeUninit::array_assume_init(array) }) + } + Err(initialized) => { + // SAFETY: Only the last `initialized` elements were populated + Err(unsafe { IntoIter::new_unchecked(array, N - initialized..N) }) + } } - Err(initialized) => { - // SAFETY: Only the last `initialized` elements were populated - Err(unsafe { IntoIter::new_unchecked(array, N - initialized..N) }) + } +} + +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +const impl + TrustedLen, T, const N: usize> + SpecNextChunkBack for I +{ + fn spec_next_chunk_back(&mut self) -> Result<[T; N], IntoIter> { + let len = (*self).size_hint().0; + let mut array = [const { MaybeUninit::uninit() }; N]; + if len < N { + // SAFETY: `TrustedLen`, an unsafe trait, requires that i can get len items out of it. + unsafe { write_back(&mut array, self, len) }; + // SAFETY: Only the last `len` elements were populated + Err(unsafe { IntoIter::new_unchecked(array, N - len..N) }) + } else { + // SAFETY: `TrustedLen`, an unsafe trait, requires that i can get N items out of it. + unsafe { write_back(&mut array, self, N) }; + // SAFETY: All N items were populated + Ok(unsafe { MaybeUninit::array_assume_init(array) }) } } } -/// Version of [`iter_next_chunk_back`] using a passed-in slice. +// SAFETY: `from` must have len items, and len items must be < N. +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +const unsafe fn write_back( + to: &mut [MaybeUninit; N], + from: &mut impl [const] DoubleEndedIterator, + len: usize, +) { + let mut guard = GuardBack { array_mut: to, initialized: 0 }; + while guard.initialized < len { + // SAFETY: caller has guaranteed, from has len items. + let item = unsafe { from.next_back().unwrap_unchecked() }; + // SAFETY: guard.initialized < len < N + unsafe { guard.push_unchecked(item) }; + } + crate::mem::forget(guard); +} + +/// Version of [`iter_next_chunk_back`] using a passed-in slice +/// in order to avoid needing to monomorphize for every array length. +/// +/// Unfortunately this loop has two exit conditions, the buffer filling up +/// or the iterator running out of items, making it tend to optimize poorly. +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] #[inline] -fn iter_next_chunk_back_erased( +const fn iter_next_chunk_back_erased( buffer: &mut [MaybeUninit], - iter: &mut impl DoubleEndedIterator, + iter: &mut impl [const] DoubleEndedIterator, ) -> Result<(), usize> { // if `Iterator::next_back` panics, this guard will drop already initialized items let mut guard = GuardBack { array_mut: buffer, initialized: 0 }; diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 6c502ee75e17f..a3e5ae5e7698b 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -1,6 +1,8 @@ use crate::array; use crate::iter::adapters::SourceIter; -use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedRandomAccessNoCoerce}; +use crate::iter::{ + ByRefSized, FusedIterator, InPlaceIterable, TrustedFused, TrustedRandomAccessNoCoerce, +}; use crate::num::NonZero; use crate::ops::{ControlFlow, NeverShortCircuit, Try}; @@ -126,11 +128,15 @@ where self.next_back_remainder(); let mut acc = init; + let mut iter = ByRefSized(&mut self.iter).rev(); // NB remainder is handled by `next_back_remainder`, so - // `next_chunk_back` can't return `Err` with non-empty remainder + // `next_chunk` can't return `Err` with non-empty remainder // (assuming correct `I as ExactSizeIterator` impl). - while let Ok(chunk) = self.iter.next_chunk_back() { + while let Ok(mut chunk) = iter.next_chunk() { + // FIXME: do not do double reverse + // (we could instead add `next_chunk_back` for example) + chunk.reverse(); acc = f(acc, chunk)? } diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 84e22959a0728..a5a27e2ff4ead 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -1,5 +1,5 @@ -use crate::marker::Destruct; use crate::array; +use crate::marker::Destruct; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; @@ -96,9 +96,41 @@ pub const trait DoubleEndedIterator: [const] Iterator { #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option; - /// Pulls `N` items from the back of the iterator and returns them as an array. + /// Advances from the back of the iterator and returns an array containing the next + /// `N` values in sequence. + /// + /// If there are not enough elements to fill the array then `Err` is returned + /// containing an iterator over the remaining elements. /// - /// See [`Iterator::next_chunk`] for more details. + /// Note: This is not equivalent to doing `iter.rev().next_chunk()` as this method + /// takes elements from the back of the iterator and preserves the order that the + /// elements were seen in the original iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_next_chunk)] + /// + /// let mut iter = "lorem".chars(); + /// + /// assert_eq!(iter.next_chunk_back().unwrap(), ['e', 'm']); // N is inferred as 2 + /// assert_eq!(iter.next_chunk_back().unwrap(), ['l', 'o', 'r']); // N is inferred as 3 + /// assert_eq!(iter.next_chunk_back::<4>().unwrap_err().as_slice(), &[]); // N is explicitly 4 + /// ``` + /// + /// Split a string and get the last three items in sequence. + /// + /// ``` + /// #![feature(iter_next_chunk)] + /// + /// let quote = "not all those who wander are lost"; + /// let [first, second, third] = quote.split_whitespace().next_chunk_back().unwrap(); + /// assert_eq!(first, "wander"); + /// assert_eq!(second, "are"); + /// assert_eq!(third, "lost"); + /// ``` #[inline] #[unstable(feature = "iter_next_chunk", issue = "98326")] fn next_chunk_back( diff --git a/library/coretests/tests/iter/adapters/filter.rs b/library/coretests/tests/iter/adapters/filter.rs index 167851e33336e..14cd3cbd58f10 100644 --- a/library/coretests/tests/iter/adapters/filter.rs +++ b/library/coretests/tests/iter/adapters/filter.rs @@ -63,3 +63,15 @@ fn test_next_chunk_does_not_leak() { assert_eq!(Rc::strong_count(w), 1); } } + +#[test] +fn test_next_chunk_back_does_not_leak() { + let drop_witness: [_; 5] = std::array::from_fn(|_| Rc::new(())); + + let v = (0..5).map(|i| drop_witness[i].clone()).collect::>(); + let _ = v.into_iter().filter(|_| false).next_chunk_back::<1>(); + + for ref w in drop_witness { + assert_eq!(Rc::strong_count(w), 1); + } +} diff --git a/library/coretests/tests/iter/traits/iterator.rs b/library/coretests/tests/iter/traits/iterator.rs index 386946461e9fe..0850b1e4edc26 100644 --- a/library/coretests/tests/iter/traits/iterator.rs +++ b/library/coretests/tests/iter/traits/iterator.rs @@ -619,6 +619,18 @@ fn test_next_chunk() { assert_eq!(it.next_chunk::<0>().unwrap(), []); } +#[test] +fn test_next_chunk_back() { + let mut it = 0..12; + assert_eq!(it.next_chunk_back().unwrap(), [8, 9, 10, 11]); + assert_eq!(it.next_chunk_back().unwrap(), []); + assert_eq!(it.next_chunk_back().unwrap(), [2, 3, 4, 5, 6, 7]); + assert_eq!(it.next_chunk_back::<4>().unwrap_err().as_slice(), &[0, 1]); + + let mut it = std::iter::once_with(|| panic!()); + assert_eq!(it.next_chunk_back::<0>().unwrap(), []); +} + #[test] fn test_next_chunk_untrusted() { struct Untrusted(I); @@ -636,6 +648,28 @@ fn test_next_chunk_untrusted() { assert_eq!(it.next_chunk::<4>().unwrap_err().as_slice(), &[10, 11]); } +#[test] +fn test_next_chunk_back_untrusted() { + struct Untrusted(I); + impl Iterator for Untrusted { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.0.next() + } + } + impl DoubleEndedIterator for Untrusted { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + let mut it = Untrusted(0..12); + assert_eq!(it.next_chunk_back().unwrap(), [8, 9, 10, 11]); + assert_eq!(it.next_chunk_back().unwrap(), []); + assert_eq!(it.next_chunk_back().unwrap(), [2, 3, 4, 5, 6, 7]); + assert_eq!(it.next_chunk::<4>().unwrap_err().as_slice(), &[0, 1]); +} + #[test] fn test_collect_into_tuples() { let a = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; From ae8f56da130391968a569f7dee81d302c45196a7 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Wed, 24 Jun 2026 13:28:57 -0400 Subject: [PATCH 05/15] Non-constify DoubleEndedIterator::next_chunk_back --- library/core/src/array/mod.rs | 33 +++++++------------- library/core/src/iter/traits/double_ended.rs | 1 + 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 07d5591bbe9b5..63a99349d7c9a 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -991,9 +991,8 @@ impl GuardBack<'_, T> { /// # Safety /// /// No more than N elements must be initialized. - #[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] #[inline] - pub(crate) const unsafe fn push_unchecked(&mut self, item: T) { + pub(crate) unsafe fn push_unchecked(&mut self, item: T) { // SAFETY: If `initialized` was correct before and the caller does not // invoke this method more than N times, then writes will be in-bounds // and slots will not be initialized more than once. @@ -1006,8 +1005,7 @@ impl GuardBack<'_, T> { } } -#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] -const impl Drop for GuardBack<'_, T> { +impl Drop for GuardBack<'_, T> { #[inline] fn drop(&mut self) { debug_assert!(self.initialized <= self.array_mut.len()); @@ -1139,24 +1137,20 @@ const fn iter_next_chunk_erased( /// dropped. /// /// Used for [`DoubleEndedIterator::next_chunk_back`]. -#[rustc_const_unstable(feature = "const_iter", issue = "92476")] #[inline] -pub(crate) const fn iter_next_chunk_back( - iter: &mut impl [const] DoubleEndedIterator, +pub(crate) fn iter_next_chunk_back( + iter: &mut impl DoubleEndedIterator, ) -> Result<[T; N], IntoIter> { iter.spec_next_chunk_back() } -pub(crate) const trait SpecNextChunkBack: +pub(crate) trait SpecNextChunkBack: DoubleEndedIterator { fn spec_next_chunk_back(&mut self) -> Result<[T; N], IntoIter>; } -#[rustc_const_unstable(feature = "const_iter", issue = "92476")] -const impl, T, const N: usize> SpecNextChunkBack - for I -{ +impl, T, const N: usize> SpecNextChunkBack for I { #[inline] default fn spec_next_chunk_back(&mut self) -> Result<[T; N], IntoIter> { let mut array = [const { MaybeUninit::uninit() }; N]; @@ -1174,9 +1168,8 @@ const impl, T, const N: usize> SpecNext } } -#[rustc_const_unstable(feature = "const_iter", issue = "92476")] -const impl + TrustedLen, T, const N: usize> - SpecNextChunkBack for I +impl + TrustedLen, T, const N: usize> SpecNextChunkBack + for I { fn spec_next_chunk_back(&mut self) -> Result<[T; N], IntoIter> { let len = (*self).size_hint().0; @@ -1196,10 +1189,9 @@ const impl + TrustedLen, T, const N: us } // SAFETY: `from` must have len items, and len items must be < N. -#[rustc_const_unstable(feature = "const_iter", issue = "92476")] -const unsafe fn write_back( +unsafe fn write_back( to: &mut [MaybeUninit; N], - from: &mut impl [const] DoubleEndedIterator, + from: &mut impl DoubleEndedIterator, len: usize, ) { let mut guard = GuardBack { array_mut: to, initialized: 0 }; @@ -1217,11 +1209,10 @@ const unsafe fn write_back( /// /// Unfortunately this loop has two exit conditions, the buffer filling up /// or the iterator running out of items, making it tend to optimize poorly. -#[rustc_const_unstable(feature = "const_iter", issue = "92476")] #[inline] -const fn iter_next_chunk_back_erased( +fn iter_next_chunk_back_erased( buffer: &mut [MaybeUninit], - iter: &mut impl [const] DoubleEndedIterator, + iter: &mut impl DoubleEndedIterator, ) -> Result<(), usize> { // if `Iterator::next_back` panics, this guard will drop already initialized items let mut guard = GuardBack { array_mut: buffer, initialized: 0 }; diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index a5a27e2ff4ead..a16732e1d8c74 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -133,6 +133,7 @@ pub const trait DoubleEndedIterator: [const] Iterator { /// ``` #[inline] #[unstable(feature = "iter_next_chunk", issue = "98326")] + #[rustc_non_const_trait_method] fn next_chunk_back( &mut self, ) -> Result<[Self::Item; N], array::IntoIter> From 0fc6688ab5e54bdb970881a8997d86f650445988 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 20 Jun 2026 19:15:31 -0700 Subject: [PATCH 06/15] Add a test for current read/write unaligned behaviour --- tests/codegen-llvm/read_write_unaligned.rs | 71 +++++++++++++ tests/mir-opt/pre-codegen/unaligned.rs | 37 +++++++ ...d_copy_generic.runtime-optimized.after.mir | 99 +++++++++++++++++++ ...ed_copy_manual.runtime-optimized.after.mir | 34 +++++++ 4 files changed, 241 insertions(+) create mode 100644 tests/codegen-llvm/read_write_unaligned.rs create mode 100644 tests/mir-opt/pre-codegen/unaligned.rs create mode 100644 tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir create mode 100644 tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir diff --git a/tests/codegen-llvm/read_write_unaligned.rs b/tests/codegen-llvm/read_write_unaligned.rs new file mode 100644 index 0000000000000..beba4400dc54a --- /dev/null +++ b/tests/codegen-llvm/read_write_unaligned.rs @@ -0,0 +1,71 @@ +//@ compile-flags: -Copt-level=3 +//@ only-64bit + +#![crate_type = "lib"] + +use std::num::NonZero; +use std::ptr::NonNull; + +// CHECK-LABEL: nonnull ptr @read_unaligned_ptr(ptr{{.+}}%ptr) +#[no_mangle] +unsafe fn read_unaligned_ptr(ptr: *const NonNull) -> NonNull { + // CHECK: start: + // CHECK-NEXT: [[TEMP:%.+]] = load ptr, ptr %ptr, align 1 + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK-NEXT: ret ptr [[TEMP]] + ptr.read_unaligned() +} + +// CHECK-LABEL: range(i16 1, 0) i16 @read_unaligned_i16(ptr{{.+}}%ptr) +#[no_mangle] +unsafe fn read_unaligned_i16(ptr: *const NonZero) -> NonZero { + // CHECK: start: + // CHECK-NEXT: [[TEMP:%.+]] = load i16, ptr %ptr, align 1 + // CHECK-NOT: !noundef + // CHECK-NOT: !range + // CHECK-NEXT: ret i16 [[TEMP]] + ptr.read_unaligned() +} + +// CHECK-LABEL: void @typed_copy_unaligned_i32(ptr{{.+}}%src, ptr{{.+}}%dst) +#[no_mangle] +unsafe fn typed_copy_unaligned_i32(src: *const NonZero, dst: *mut NonZero) { + // CHECK: start: + // CHECK-NEXT: [[TEMP:%.+]] = load i32, ptr %src, align 1 + // CHECK-NOT: !noundef + // CHECK-NOT: !range + // CHECK-NEXT: store i32 [[TEMP]], ptr %dst, align 1 + // CHECK-NEXT: ret void + dst.write_unaligned(src.read_unaligned()) +} + +// CHECK-LABEL: void @write_unaligned_i64(ptr{{.+}}%ptr, i64{{.+}}%val) +#[no_mangle] +unsafe fn write_unaligned_i64(ptr: *mut NonZero, val: NonZero) { + // CHECK: start: + // CHECK-NEXT: store i64 %val, ptr %ptr, align 1 + // CHECK-NEXT: ret void + ptr.write_unaligned(val) +} + +#[repr(align(128))] +struct HugeBuffer([u64; 1 << 10]); + +// CHECK-LABEL: void @read_unaligned_huge(ptr{{.+}}%_0, ptr{{.+}}%ptr) +#[no_mangle] +unsafe fn read_unaligned_huge(ptr: *const HugeBuffer) -> HugeBuffer { + // CHECK: start: + // CHECK-NEXT: call void @llvm.memcpy{{.+}} align 128 dereferenceable(8192) %_0, {{.+}} align 1 dereferenceable(8192) %ptr, i64 8192, + // CHECK-NEXT: ret void + ptr.read_unaligned() +} + +// CHECK-LABEL: void @write_unaligned_huge(ptr{{.+}}%ptr, ptr{{.+}}%val) +#[no_mangle] +unsafe fn write_unaligned_huge(ptr: *mut HugeBuffer, val: HugeBuffer) { + // CHECK: start: + // CHECK-NEXT: call void @llvm.memcpy{{.+}} align 1 dereferenceable(8192) %ptr, {{.+}} align 128 dereferenceable(8192) %val, i64 8192, + // CHECK-NEXT: ret void + ptr.write_unaligned(val) +} diff --git a/tests/mir-opt/pre-codegen/unaligned.rs b/tests/mir-opt/pre-codegen/unaligned.rs new file mode 100644 index 0000000000000..70fbc373ca499 --- /dev/null +++ b/tests/mir-opt/pre-codegen/unaligned.rs @@ -0,0 +1,37 @@ +//@ compile-flags: -O -Zmir-opt-level=2 +//@ ignore-std-debug-assertions (there's one in `ptr::read` via `MaybeUninit::assume_init`) + +#![crate_type = "lib"] + +// EMIT_MIR unaligned.unaligned_copy_manual.runtime-optimized.after.mir +pub unsafe fn unaligned_copy_manual(src: *const u128, dst: *mut u128) { + #[repr(packed)] + struct Packed(T); + + // CHECK-LABEL: fn unaligned_copy_manual(_1: *const u128, _2: *mut u128) -> () + // CHECK: [[SRCU:_.+]] = copy _1 as *const unaligned_copy_manual::Packed (PtrToPtr); + // CHECK: [[DSTU:_.+]] = copy _2 as *mut unaligned_copy_manual::Packed (PtrToPtr); + // CHECK: [[TEMP:_.+]] = copy ((*[[SRCU]]).0: u128); + // ((*[[DSTU]]).0: u128) = move [[TEMP]]; + let src = src.cast::>(); + let dst = dst.cast::>(); + unsafe { (*dst).0 = (*src).0 }; +} + +// EMIT_MIR unaligned.unaligned_copy_generic.runtime-optimized.after.mir +pub unsafe fn unaligned_copy_generic(src: *const T, dst: *mut T) { + // CHECK-LABEL: fn unaligned_copy_generic( + // CHECK: debug src => _1; + // CHECK: debug dst => _2; + // CHECK: debug val => [[VAL:_.+]]; + // CHECK: copy _1 as *const u8 (PtrToPtr); + // CHECK: &raw mut + // CHECK: copy_nonoverlapping{{.+}}count = const ::SIZE + // CHECK: &raw const + // CHECK: copy _2 as *mut u8 (PtrToPtr); + // CHECK: copy_nonoverlapping{{.+}}count = const ::SIZE + unsafe { + let val = std::ptr::read_unaligned(src); + std::ptr::write_unaligned(dst, val); + } +} diff --git a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir new file mode 100644 index 0000000000000..ad474ba1edb73 --- /dev/null +++ b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir @@ -0,0 +1,99 @@ +// MIR for `unaligned_copy_generic` after runtime-optimized + +fn unaligned_copy_generic(_1: *const T, _2: *mut T) -> () { + debug src => _1; + debug dst => _2; + let mut _0: (); + let _9: T; + let mut _10: T; + scope 1 { + debug val => _9; + scope 14 (inlined #[track_caller] write_unaligned::) { + let mut _11: *const T; + let mut _12: *const u8; + let mut _13: *mut u8; + scope 15 (inlined std::mem::size_of::) { + } + scope 16 (inlined std::ptr::copy_nonoverlapping::) { + scope 17 (inlined core::ub_checks::check_language_ub) { + scope 18 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + scope 19 (inlined std::mem::size_of::) { + } + scope 20 (inlined std::mem::align_of::) { + } + } + } + } + scope 2 (inlined #[track_caller] read_unaligned::) { + let mut _3: std::mem::MaybeUninit; + let mut _4: *const u8; + let mut _6: *mut u8; + let mut _7: std::mem::MaybeUninit; + scope 3 { + scope 5 (inlined MaybeUninit::::as_mut_ptr) { + let mut _5: *mut std::mem::MaybeUninit; + } + scope 6 (inlined std::mem::size_of::) { + } + scope 7 (inlined std::ptr::copy_nonoverlapping::) { + scope 8 (inlined core::ub_checks::check_language_ub) { + scope 9 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + scope 10 (inlined std::mem::size_of::) { + } + scope 11 (inlined std::mem::align_of::) { + } + } + scope 12 (inlined #[track_caller] MaybeUninit::::assume_init) { + let _8: (); + scope 13 (inlined transmute_neo::, T>) { + } + } + } + scope 4 (inlined MaybeUninit::::uninit) { + } + } + + bb0: { + StorageLive(_9); + StorageLive(_5); + StorageLive(_3); + _3 = MaybeUninit:: { uninit: const () }; + StorageLive(_4); + _4 = copy _1 as *const u8 (PtrToPtr); + StorageLive(_6); + _5 = &raw mut _3; + _6 = copy _5 as *mut u8 (PtrToPtr); + copy_nonoverlapping(dst = copy _6, src = copy _4, count = const ::SIZE); + StorageDead(_6); + StorageDead(_4); + StorageLive(_7); + _7 = move _3; + _8 = assert_inhabited::() -> [return: bb1, unwind unreachable]; + } + + bb1: { + _9 = copy _7 as T (Transmute); + StorageDead(_7); + StorageDead(_3); + StorageDead(_5); + StorageLive(_10); + _10 = move _9; + StorageLive(_11); + StorageLive(_12); + _11 = &raw const _10; + _12 = copy _11 as *const u8 (PtrToPtr); + StorageLive(_13); + _13 = copy _2 as *mut u8 (PtrToPtr); + copy_nonoverlapping(dst = copy _13, src = copy _12, count = const ::SIZE); + StorageDead(_13); + StorageDead(_12); + StorageDead(_11); + StorageDead(_10); + StorageDead(_9); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir new file mode 100644 index 0000000000000..450df935a0fb3 --- /dev/null +++ b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_manual.runtime-optimized.after.mir @@ -0,0 +1,34 @@ +// MIR for `unaligned_copy_manual` after runtime-optimized + +fn unaligned_copy_manual(_1: *const u128, _2: *mut u128) -> () { + debug src => _1; + debug dst => _2; + let mut _0: (); + let _3: *const unaligned_copy_manual::Packed; + let mut _5: u128; + scope 1 { + debug src => _3; + let _4: *mut unaligned_copy_manual::Packed; + scope 2 { + debug dst => _4; + } + scope 4 (inlined std::ptr::mut_ptr::::cast::>) { + } + } + scope 3 (inlined std::ptr::const_ptr::::cast::>) { + } + + bb0: { + StorageLive(_3); + _3 = copy _1 as *const unaligned_copy_manual::Packed (PtrToPtr); + StorageLive(_4); + _4 = copy _2 as *mut unaligned_copy_manual::Packed (PtrToPtr); + StorageLive(_5); + _5 = copy ((*_3).0: u128); + ((*_4).0: u128) = move _5; + StorageDead(_5); + StorageDead(_4); + StorageDead(_3); + return; + } +} From 3a9c74d36180c2d93618ecc2e976ed59e510946f Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 23 Jun 2026 21:44:03 -0700 Subject: [PATCH 07/15] Implement `ptr::{read,write}_aligned` via `repr(packed)` --- library/core/src/ptr/mod.rs | 43 ++++++--- tests/mir-opt/pre-codegen/unaligned.rs | 16 ++-- ...d_copy_generic.runtime-optimized.after.mir | 92 +++++-------------- 3 files changed, 63 insertions(+), 88 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 593011edecf27..b4d47205f2fea 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1810,16 +1810,24 @@ pub const unsafe fn read(src: *const T) -> T { #[track_caller] #[rustc_diagnostic_item = "ptr_read_unaligned"] pub const unsafe fn read_unaligned(src: *const T) -> T { - let mut tmp = MaybeUninit::::uninit(); + // Always true thanks to the repr, but to demonstrate + const { + assert!(mem::offset_of!(Packed::, 0) == 0); + assert!(size_of::() == size_of::>()); + } + + let src = src.cast::>(); // SAFETY: the caller must guarantee that `src` is valid for reads. - // `src` cannot overlap `tmp` because `tmp` was just allocated on - // the stack as a separate allocation. + // Reading it as `Packed` instead of `T` reads those same bytes because + // it's the same size (thus zero offset), but with alignment 1 instead. // - // Also, since we just wrote a valid value into `tmp`, it is guaranteed - // to be properly initialized. + // Similarly, because it's the same bytes it's sound to transmute from the + // `Packed` to `T`. Transmute is a value-based (not a place-based) + // operation that doesn't care about alignment. unsafe { - copy_nonoverlapping(src as *const u8, tmp.as_mut_ptr() as *mut u8, size_of::()); - tmp.assume_init() + let packed = read(src); + // Can't just destructure because that's not allowed in const fn + mem::transmute_neo(packed) } } @@ -2012,14 +2020,18 @@ pub const unsafe fn write(dst: *mut T, src: T) { #[rustc_diagnostic_item = "ptr_write_unaligned"] #[track_caller] pub const unsafe fn write_unaligned(dst: *mut T, src: T) { - // 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((&raw const src) as *const u8, dst as *mut u8, size_of::()); - // We are calling the intrinsic directly to avoid function calls in the generated code. - intrinsics::forget(src); + // Always true thanks to the repr, but to demonstrate + const { + assert!(mem::offset_of!(Packed::, 0) == 0); + assert!(size_of::() == size_of::>()); } + + let dst = dst.cast::>(); + let src = Packed(src); + // SAFETY: the caller must guarantee that `dst` is valid for writes. + // Writing it as `Packed` instead of `T` writes those same bytes because + // it's the same size (thus zero offset), but with alignment 1 instead. + unsafe { write(dst, src) } } /// Performs a volatile read of the value from `src` without moving it. @@ -2809,3 +2821,6 @@ pub macro addr_of($place:expr) { pub macro addr_of_mut($place:expr) { &raw mut $place } + +#[repr(C, packed)] +struct Packed(T); diff --git a/tests/mir-opt/pre-codegen/unaligned.rs b/tests/mir-opt/pre-codegen/unaligned.rs index 70fbc373ca499..4aeb10c261e21 100644 --- a/tests/mir-opt/pre-codegen/unaligned.rs +++ b/tests/mir-opt/pre-codegen/unaligned.rs @@ -1,5 +1,5 @@ //@ compile-flags: -O -Zmir-opt-level=2 -//@ ignore-std-debug-assertions (there's one in `ptr::read` via `MaybeUninit::assume_init`) +//@ ignore-std-debug-assertions (there's one in `ptr::read`) #![crate_type = "lib"] @@ -24,12 +24,14 @@ pub unsafe fn unaligned_copy_generic(src: *const T, dst: *mut T) { // CHECK: debug src => _1; // CHECK: debug dst => _2; // CHECK: debug val => [[VAL:_.+]]; - // CHECK: copy _1 as *const u8 (PtrToPtr); - // CHECK: &raw mut - // CHECK: copy_nonoverlapping{{.+}}count = const ::SIZE - // CHECK: &raw const - // CHECK: copy _2 as *mut u8 (PtrToPtr); - // CHECK: copy_nonoverlapping{{.+}}count = const ::SIZE + // CHECK: [[SRC_P:_.+]] = copy _1 as *const {{.+}}::Packed (PtrToPtr); + // CHECK: [[PACKED1:_.+]] = copy (*[[SRC_P]]); + // CHECK: [[VAL]] = copy [[PACKED1]] as T (Transmute); + // CHECK: [[DST_P:_.+]] = copy _2 as *mut {{.+}}::Packed (PtrToPtr); + // CHECK: [[PACKED2:_.+]] = {{.+}}::Packed::(copy [[VAL]]); + // CHECK: (*[[DST_P]]) = copy [[PACKED2]]; + // CHECK-NOT: copy_nonoverlapping + // CHECK-NOT: drop unsafe { let val = std::ptr::read_unaligned(src); std::ptr::write_unaligned(dst, val); diff --git a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir index ad474ba1edb73..4d0cfa6a4dfa3 100644 --- a/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir +++ b/tests/mir-opt/pre-codegen/unaligned.unaligned_copy_generic.runtime-optimized.after.mir @@ -4,96 +4,54 @@ fn unaligned_copy_generic(_1: *const T, _2: *mut T) -> () { debug src => _1; debug dst => _2; let mut _0: (); - let _9: T; - let mut _10: T; + let _5: T; scope 1 { - debug val => _9; - scope 14 (inlined #[track_caller] write_unaligned::) { - let mut _11: *const T; - let mut _12: *const u8; - let mut _13: *mut u8; - scope 15 (inlined std::mem::size_of::) { - } - scope 16 (inlined std::ptr::copy_nonoverlapping::) { - scope 17 (inlined core::ub_checks::check_language_ub) { - scope 18 (inlined core::ub_checks::check_language_ub::runtime) { + debug val => _5; + scope 8 (inlined #[track_caller] write_unaligned::) { + let _6: *mut std::ptr::Packed; + scope 9 { + let _7: std::ptr::Packed; + scope 10 { + scope 12 (inlined #[track_caller] std::ptr::write::>) { } } - scope 19 (inlined std::mem::size_of::) { - } - scope 20 (inlined std::mem::align_of::) { - } + } + scope 11 (inlined std::ptr::mut_ptr::::cast::>) { } } } scope 2 (inlined #[track_caller] read_unaligned::) { - let mut _3: std::mem::MaybeUninit; - let mut _4: *const u8; - let mut _6: *mut u8; - let mut _7: std::mem::MaybeUninit; + let _3: *const std::ptr::Packed; scope 3 { - scope 5 (inlined MaybeUninit::::as_mut_ptr) { - let mut _5: *mut std::mem::MaybeUninit; - } - scope 6 (inlined std::mem::size_of::) { - } - scope 7 (inlined std::ptr::copy_nonoverlapping::) { - scope 8 (inlined core::ub_checks::check_language_ub) { - scope 9 (inlined core::ub_checks::check_language_ub::runtime) { - } - } - scope 10 (inlined std::mem::size_of::) { - } - scope 11 (inlined std::mem::align_of::) { + let _4: std::ptr::Packed; + scope 4 { + scope 7 (inlined transmute_neo::, T>) { } } - scope 12 (inlined #[track_caller] MaybeUninit::::assume_init) { - let _8: (); - scope 13 (inlined transmute_neo::, T>) { - } + scope 6 (inlined #[track_caller] std::ptr::read::>) { } } - scope 4 (inlined MaybeUninit::::uninit) { + scope 5 (inlined std::ptr::const_ptr::::cast::>) { } } bb0: { - StorageLive(_9); StorageLive(_5); StorageLive(_3); - _3 = MaybeUninit:: { uninit: const () }; + _3 = copy _1 as *const std::ptr::Packed (PtrToPtr); StorageLive(_4); - _4 = copy _1 as *const u8 (PtrToPtr); - StorageLive(_6); - _5 = &raw mut _3; - _6 = copy _5 as *mut u8 (PtrToPtr); - copy_nonoverlapping(dst = copy _6, src = copy _4, count = const ::SIZE); - StorageDead(_6); + _4 = copy (*_3); + _5 = copy _4 as T (Transmute); StorageDead(_4); + StorageDead(_3); + StorageLive(_6); + _6 = copy _2 as *mut std::ptr::Packed (PtrToPtr); StorageLive(_7); - _7 = move _3; - _8 = assert_inhabited::() -> [return: bb1, unwind unreachable]; - } - - bb1: { - _9 = copy _7 as T (Transmute); + _7 = std::ptr::Packed::(copy _5); + (*_6) = copy _7; StorageDead(_7); - StorageDead(_3); + StorageDead(_6); StorageDead(_5); - StorageLive(_10); - _10 = move _9; - StorageLive(_11); - StorageLive(_12); - _11 = &raw const _10; - _12 = copy _11 as *const u8 (PtrToPtr); - StorageLive(_13); - _13 = copy _2 as *mut u8 (PtrToPtr); - copy_nonoverlapping(dst = copy _13, src = copy _12, count = const ::SIZE); - StorageDead(_13); - StorageDead(_12); - StorageDead(_11); - StorageDead(_10); - StorageDead(_9); return; } } From 7cddcd6fec4ed1e6d7b6088e57326cc5548494b5 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 23 Jun 2026 21:00:04 +0800 Subject: [PATCH 08/15] rustc_target/asm: add LoongArch LSX/LASX inline asm register support Add support for LoongArch LSX and LASX registers in inline assembly by introducing the `vreg` and `xreg` register classes, along with their associated vector types and operand modifiers. The new register classes are gated behind the `asm_experimental_reg` feature. Also model the overlap between FPU, LSX, and LASX registers so register conflict checking works correctly for aliased registers. --- compiler/rustc_codegen_gcc/src/asm.rs | 27 +++- compiler/rustc_codegen_llvm/src/asm.rs | 23 ++- compiler/rustc_span/src/symbol.rs | 3 + compiler/rustc_target/src/asm/loongarch.rs | 151 +++++++++++++++++- compiler/rustc_target/src/asm/mod.rs | 4 +- .../language-features/asm-experimental-reg.md | 13 ++ 6 files changed, 214 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 53074e313f9ce..6fd7188f656c5 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -706,7 +706,9 @@ fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str { unreachable!("clobber-only") } InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) + | InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::vreg) + | InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::xreg) => "f", InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r", InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => "a", InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => "d", @@ -815,6 +817,12 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl } InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::vreg) => { + cx.type_vector(cx.type_i32(), 4) + } + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::xreg) => { + cx.type_vector(cx.type_i32(), 8) + } InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(), InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), @@ -1013,7 +1021,22 @@ fn modifier_to_gcc( } } InlineAsmRegClass::Hexagon(_) => None, - InlineAsmRegClass::LoongArch(_) => None, + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => None, + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => modifier, + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::vreg) => { + if modifier.is_none() { + Some('w') + } else { + modifier + } + } + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::xreg) => { + if modifier.is_none() { + Some('u') + } else { + modifier + } + } InlineAsmRegClass::Mips(_) => None, InlineAsmRegClass::Nvptx(_) => None, InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vsreg) => { diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 2598c1b38ff88..8f192a5a73b63 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -709,7 +709,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> Hexagon(HexagonInlineAsmRegClass::vreg_pair) => "v", Hexagon(HexagonInlineAsmRegClass::qreg) => unreachable!("clobber-only"), LoongArch(LoongArchInlineAsmRegClass::reg) => "r", - LoongArch(LoongArchInlineAsmRegClass::freg) => "f", + LoongArch(LoongArchInlineAsmRegClass::freg) + | LoongArch(LoongArchInlineAsmRegClass::vreg) + | LoongArch(LoongArchInlineAsmRegClass::xreg) => "f", Mips(MipsInlineAsmRegClass::reg) => "r", Mips(MipsInlineAsmRegClass::freg) => "f", Nvptx(NvptxInlineAsmRegClass::reg16) => "h", @@ -814,7 +816,22 @@ fn modifier_to_llvm( } Amdgpu(_) => None, Hexagon(_) => None, - LoongArch(_) => None, + LoongArch(LoongArchInlineAsmRegClass::reg) => None, + LoongArch(LoongArchInlineAsmRegClass::freg) => modifier, + LoongArch(LoongArchInlineAsmRegClass::vreg) => { + if modifier.is_none() { + Some('w') + } else { + modifier + } + } + LoongArch(LoongArchInlineAsmRegClass::xreg) => { + if modifier.is_none() { + Some('u') + } else { + modifier + } + } Mips(_) => None, Nvptx(_) => None, PowerPC(PowerPCInlineAsmRegClass::vsreg) => { @@ -917,6 +934,8 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' Hexagon(HexagonInlineAsmRegClass::qreg) => unreachable!("clobber-only"), LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(), LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(), + LoongArch(LoongArchInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i32(), 4), + LoongArch(LoongArchInlineAsmRegClass::xreg) => cx.type_vector(cx.type_i32(), 8), Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(), Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 827e33dcd6632..5394ed6f75868 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1168,6 +1168,7 @@ symbols! { lang_items, large_assignments, last, + lasx, lateout, lazy_normalization_consts, lazy_type_alias, @@ -1225,6 +1226,7 @@ symbols! { loop_hints, loop_match, lr, + lsx, lt, m68k, m68k_target_feature, @@ -2365,6 +2367,7 @@ symbols! { xloop, xmm_reg, xop_target_feature, + xreg, xtensa, xtensa_target_feature, yeet_desugar_details, diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs index 51e7ee8daa439..4aa69dac2d7db 100644 --- a/compiler/rustc_target/src/asm/loongarch.rs +++ b/compiler/rustc_target/src/asm/loongarch.rs @@ -8,12 +8,19 @@ def_reg_class! { LoongArch LoongArchInlineAsmRegClass { reg, freg, + vreg, + xreg, } } impl LoongArchInlineAsmRegClass { pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { - &[] + match self { + Self::freg => &['w', 'u'], + Self::vreg => &['u'], + Self::xreg => &['w'], + _ => &[], + } } pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { @@ -35,6 +42,7 @@ impl LoongArchInlineAsmRegClass { pub fn supported_types( self, arch: InlineAsmArch, + allow_experimental_reg: bool, ) -> &'static [(InlineAsmType, Option)] { match (self, arch) { (Self::reg, InlineAsmArch::LoongArch64) => { @@ -42,6 +50,27 @@ impl LoongArchInlineAsmRegClass { } (Self::reg, InlineAsmArch::LoongArch32) => types! { _: I8, I16, I32, F16, F32; }, (Self::freg, _) => types! { f: F16, F32; d: F64; }, + (Self::vreg, _) => { + if allow_experimental_reg { + types! { + lsx: F16, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); + } + } else { + &[] + } + } + (Self::xreg, _) => { + if allow_experimental_reg { + types! { + lasx: F16, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4); + } + } else { + &[] + } + } _ => unreachable!("unsupported register class"), } } @@ -108,6 +137,70 @@ def_regs! { f29: freg = ["$f29","$fs5"], f30: freg = ["$f30","$fs6"], f31: freg = ["$f31","$fs7"], + vr0: vreg = ["$vr0"], + vr1: vreg = ["$vr1"], + vr2: vreg = ["$vr2"], + vr3: vreg = ["$vr3"], + vr4: vreg = ["$vr4"], + vr5: vreg = ["$vr5"], + vr6: vreg = ["$vr6"], + vr7: vreg = ["$vr7"], + vr8: vreg = ["$vr8"], + vr9: vreg = ["$vr9"], + vr10: vreg = ["$vr10"], + vr11: vreg = ["$vr11"], + vr12: vreg = ["$vr12"], + vr13: vreg = ["$vr13"], + vr14: vreg = ["$vr14"], + vr15: vreg = ["$vr15"], + vr16: vreg = ["$vr16"], + vr17: vreg = ["$vr17"], + vr18: vreg = ["$vr18"], + vr19: vreg = ["$vr19"], + vr20: vreg = ["$vr20"], + vr21: vreg = ["$vr21"], + vr22: vreg = ["$vr22"], + vr23: vreg = ["$vr23"], + vr24: vreg = ["$vr24"], + vr25: vreg = ["$vr25"], + vr26: vreg = ["$vr26"], + vr27: vreg = ["$vr27"], + vr28: vreg = ["$vr28"], + vr29: vreg = ["$vr29"], + vr30: vreg = ["$vr30"], + vr31: vreg = ["$vr31"], + xr0: xreg = ["$xr0"], + xr1: xreg = ["$xr1"], + xr2: xreg = ["$xr2"], + xr3: xreg = ["$xr3"], + xr4: xreg = ["$xr4"], + xr5: xreg = ["$xr5"], + xr6: xreg = ["$xr6"], + xr7: xreg = ["$xr7"], + xr8: xreg = ["$xr8"], + xr9: xreg = ["$xr9"], + xr10: xreg = ["$xr10"], + xr11: xreg = ["$xr11"], + xr12: xreg = ["$xr12"], + xr13: xreg = ["$xr13"], + xr14: xreg = ["$xr14"], + xr15: xreg = ["$xr15"], + xr16: xreg = ["$xr16"], + xr17: xreg = ["$xr17"], + xr18: xreg = ["$xr18"], + xr19: xreg = ["$xr19"], + xr20: xreg = ["$xr20"], + xr21: xreg = ["$xr21"], + xr22: xreg = ["$xr22"], + xr23: xreg = ["$xr23"], + xr24: xreg = ["$xr24"], + xr25: xreg = ["$xr25"], + xr26: xreg = ["$xr26"], + xr27: xreg = ["$xr27"], + xr28: xreg = ["$xr28"], + xr29: xreg = ["$xr29"], + xr30: xreg = ["$xr30"], + xr31: xreg = ["$xr31"], #error = ["$r0","$zero"] => "constant zero cannot be used as an operand for inline asm", #error = ["$r2","$tp"] => @@ -132,4 +225,60 @@ impl LoongArchInlineAsmReg { ) -> fmt::Result { out.write_str(self.name()) } + + pub fn overlapping_regs(self, mut cb: impl FnMut(LoongArchInlineAsmReg)) { + macro_rules! reg_conflicts { + ( + $( + $f:ident : $v:ident : $x:ident + ),*; + ) => { + match self { + $( + Self::$f | Self::$v | Self::$x => { + cb(Self::$f); + cb(Self::$v); + cb(Self::$x); + } + )* + r => cb(r), + } + }; + } + + reg_conflicts! { + f0 : vr0 : xr0, + f1 : vr1 : xr1, + f2 : vr2 : xr2, + f3 : vr3 : xr3, + f4 : vr4 : xr4, + f5 : vr5 : xr5, + f6 : vr6 : xr6, + f7 : vr7 : xr7, + f8 : vr8 : xr8, + f9 : vr9 : xr9, + f10 : vr10 : xr10, + f11 : vr11 : xr11, + f12 : vr12 : xr12, + f13 : vr13 : xr13, + f14 : vr14 : xr14, + f15 : vr15 : xr15, + f16 : vr16 : xr16, + f17 : vr17 : xr17, + f18 : vr18 : xr18, + f19 : vr19 : xr19, + f20 : vr20 : xr20, + f21 : vr21 : xr21, + f22 : vr22 : xr22, + f23 : vr23 : xr23, + f24 : vr24 : xr24, + f25 : vr25 : xr25, + f26 : vr26 : xr26, + f27 : vr27 : xr27, + f28 : vr28 : xr28, + f29 : vr29 : xr29, + f30 : vr30 : xr30, + f31 : vr31 : xr31; + } + } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 1129c2eddf2d3..8d99035fd0db4 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -474,7 +474,7 @@ impl InlineAsmReg { Self::RiscV(_) => cb(self), Self::PowerPC(r) => r.overlapping_regs(|r| cb(Self::PowerPC(r))), Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), - Self::LoongArch(_) => cb(self), + Self::LoongArch(r) => r.overlapping_regs(|r| cb(Self::LoongArch(r))), Self::Mips(_) => cb(self), Self::S390x(r) => r.overlapping_regs(|r| cb(Self::S390x(r))), Self::Sparc(_) => cb(self), @@ -655,7 +655,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.supported_types(arch).into(), Self::PowerPC(r) => r.supported_types(arch).into(), Self::Hexagon(r) => r.supported_types(arch).into(), - Self::LoongArch(r) => r.supported_types(arch).into(), + Self::LoongArch(r) => r.supported_types(arch, allow_experimental_reg).into(), Self::Mips(r) => r.supported_types(arch).into(), Self::S390x(r) => r.supported_types(arch).into(), Self::Sparc(r) => r.supported_types(arch).into(), diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-reg.md b/src/doc/unstable-book/src/language-features/asm-experimental-reg.md index 5bbcb223b6026..095b541da6e46 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-reg.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-reg.md @@ -12,6 +12,8 @@ This tracks support for additional registers in architectures where inline assem | Architecture | Register class | Registers | LLVM constraint code | | ------------ | -------------- | --------- | -------------------- | +| LoongArch | `vreg` | `$vr[0-31]` | `f` | +| LoongArch | `xreg` | `$xr[0-31]` | `f` | ## Register class supported types @@ -20,11 +22,16 @@ This tracks support for additional registers in architectures where inline assem | x86 | `xmm_reg` | `sse` | `i128` | | x86 | `ymm_reg` | `avx` | `i128` | | x86 | `zmm_reg` | `avx512f` | `i128` | +| LoongArch | `vreg` | `lsx` | `f32`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` | +| LoongArch | `xreg` | `lasx` | `f32`, `f64`,
`i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2`,
`i8x32`, `i16x16`, `i32x8`, `i64x4`, `f32x8`, `f64x4` | ## Register aliases | Architecture | Base register | Aliases | | ------------ | ------------- | ------- | +| LoongArch | `$f[0-7]` | `$fa[0-7]`, `$vr[0-7]`, `$xr[0-7]` | +| LoongArch | `$f[8-23]` | `$ft[0-15]`, `$vr[8-23]`, `$xr[8-23]` | +| LoongArch | `$f[24-31]` | `$fs[0-7]`, `$vr[24-31]`, `$xr[24-31]` | ## Unsupported registers @@ -35,3 +42,9 @@ This tracks support for additional registers in architectures where inline assem | Architecture | Register class | Modifier | Example output | LLVM modifier | | ------------ | -------------- | -------- | -------------- | ------------- | +| LoongArch | `freg` | `w` | `$vr0` | `w` | +| LoongArch | `freg` | `u` | `$xr0` | `u` | +| LoongArch | `vreg` | None | `$vr0` | `w` | +| LoongArch | `vreg` | `u` | `$xr0` | `u` | +| LoongArch | `xreg` | None | `$xr0` | `u` | +| LoongArch | `xreg` | `w` | `$vr0` | `w` | From 748a979aca5b9a32eacb9cfe83c8a8766e020d9b Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Wed, 24 Jun 2026 22:08:37 +0800 Subject: [PATCH 09/15] tests/asm: cover LoongArch LSX/LASX register validation Add UI tests for LoongArch LSX/LASX inline asm registers, including target-feature gating and overlap diagnostics between `fN`, `vrN`, and `xrN` registers. --- .../bad-reg.loongarch32_ilp32d.stderr | 235 +++++++++++++++- .../bad-reg.loongarch32_ilp32s.stderr | 255 +++++++++++++++++- .../bad-reg.loongarch64_lp64d.stderr | 38 ++- .../bad-reg.loongarch64_lp64s.stderr | 255 +++++++++++++++++- tests/ui/asm/loongarch/bad-reg.rs | 47 +++- 5 files changed, 793 insertions(+), 37 deletions(-) diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr index c67c913d2a60d..28ac455c06441 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr @@ -1,38 +1,259 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:52:26 + | +LL | asm!("/* {} */", in(vreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:55:26 + | +LL | asm!("/* {} */", out(vreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:57:26 + | +LL | asm!("/* {} */", in(vreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:60:26 + | +LL | asm!("/* {} */", out(vreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:65:26 + | +LL | asm!("/* {} */", in(xreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:68:26 + | +LL | asm!("/* {} */", out(xreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:70:26 + | +LL | asm!("/* {} */", in(xreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:73:26 + | +LL | asm!("/* {} */", out(xreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:77:31 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:82:31 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:18 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:32 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:52:35 + | +LL | asm!("/* {} */", in(vreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:57:35 + | +LL | asm!("/* {} */", in(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:60:36 + | +LL | asm!("/* {} */", out(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:65:35 + | +LL | asm!("/* {} */", in(xreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:70:35 + | +LL | asm!("/* {} */", in(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:73:36 + | +LL | asm!("/* {} */", out(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:77:42 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:82:42 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:29 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:43 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 28 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr index 99c071919acfa..1ee1b86989c4d 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr @@ -1,62 +1,295 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:52:26 + | +LL | asm!("/* {} */", in(vreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:55:26 + | +LL | asm!("/* {} */", out(vreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:57:26 + | +LL | asm!("/* {} */", in(vreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:60:26 + | +LL | asm!("/* {} */", out(vreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:65:26 + | +LL | asm!("/* {} */", in(xreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:68:26 + | +LL | asm!("/* {} */", out(xreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:70:26 + | +LL | asm!("/* {} */", in(xreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:73:26 + | +LL | asm!("/* {} */", out(xreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:77:31 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:82:31 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:18 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:32 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:40:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:42:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:44:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:46:26 + --> $DIR/bad-reg.rs:48:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ -error: aborting due to 10 previous errors +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:52:35 + | +LL | asm!("/* {} */", in(vreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:57:35 + | +LL | asm!("/* {} */", in(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:60:36 + | +LL | asm!("/* {} */", out(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:65:35 + | +LL | asm!("/* {} */", in(xreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:70:35 + | +LL | asm!("/* {} */", in(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:73:36 + | +LL | asm!("/* {} */", out(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:77:18 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^ + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:77:42 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:82:18 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^ + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:82:42 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:29 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:43 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 34 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr index c67c913d2a60d..97462d6dc5f0b 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr @@ -1,38 +1,62 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: register `$vr0` conflicts with register `$f0` + --> $DIR/bad-reg.rs:77:31 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ----------- ^^^^^^^^^^^^ register `$vr0` + | | + | register `$f0` + +error: register `$xr0` conflicts with register `$f0` + --> $DIR/bad-reg.rs:82:31 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ----------- ^^^^^^^^^^^^ register `$xr0` + | | + | register `$f0` + +error: register `$xr0` conflicts with register `$vr0` + --> $DIR/bad-reg.rs:87:32 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ------------ ^^^^^^^^^^^^ register `$xr0` + | | + | register `$vr0` + +error: aborting due to 9 previous errors diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr index 99c071919acfa..1ee1b86989c4d 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr @@ -1,62 +1,295 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:26:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:28:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:30:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:32:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:34:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:36:18 + --> $DIR/bad-reg.rs:38:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:52:26 + | +LL | asm!("/* {} */", in(vreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:55:26 + | +LL | asm!("/* {} */", out(vreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:57:26 + | +LL | asm!("/* {} */", in(vreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:60:26 + | +LL | asm!("/* {} */", out(vreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:65:26 + | +LL | asm!("/* {} */", in(xreg) f); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:68:26 + | +LL | asm!("/* {} */", out(xreg) _); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:70:26 + | +LL | asm!("/* {} */", in(xreg) d); + | ^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:73:26 + | +LL | asm!("/* {} */", out(xreg) d); + | ^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:77:31 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:82:31 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `vreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:18 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: register class `xreg` can only be used as a clobber in stable + --> $DIR/bad-reg.rs:87:32 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^^^^^^^^^^^^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:40:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:42:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:44:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:46:26 + --> $DIR/bad-reg.rs:48:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ -error: aborting due to 10 previous errors +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:52:35 + | +LL | asm!("/* {} */", in(vreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:57:35 + | +LL | asm!("/* {} */", in(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:60:36 + | +LL | asm!("/* {} */", out(vreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:65:35 + | +LL | asm!("/* {} */", in(xreg) f); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:70:35 + | +LL | asm!("/* {} */", in(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:73:36 + | +LL | asm!("/* {} */", out(xreg) d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:77:18 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^^^^^^^^^^^ + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:77:42 + | +LL | asm!("", in("$f0") f, in("$vr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: register class `freg` requires at least one of the following target features: d, f + --> $DIR/bad-reg.rs:82:18 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^^^^^^^^^^^ + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:82:42 + | +LL | asm!("", in("$f0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f32` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:29 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: type `f64` cannot be used with this register class in stable + --> $DIR/bad-reg.rs:87:43 + | +LL | asm!("", in("$vr0") f, in("$xr0") d); + | ^ + | + = note: see issue #133416 for more information + = help: add `#![feature(asm_experimental_reg)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 34 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/asm/loongarch/bad-reg.rs b/tests/ui/asm/loongarch/bad-reg.rs index 7d28884966d46..6bda28eb10fde 100644 --- a/tests/ui/asm/loongarch/bad-reg.rs +++ b/tests/ui/asm/loongarch/bad-reg.rs @@ -4,12 +4,14 @@ //@[loongarch32_ilp32d] needs-llvm-components: loongarch //@[loongarch32_ilp32s] compile-flags: --target loongarch32-unknown-none-softfloat //@[loongarch32_ilp32s] needs-llvm-components: loongarch -//@[loongarch64_lp64d] compile-flags: --target loongarch64-unknown-linux-gnu +//@[loongarch64_lp64d] compile-flags: --target loongarch64-unknown-linux-gnu -Ctarget-feature=+lasx //@[loongarch64_lp64d] needs-llvm-components: loongarch //@[loongarch64_lp64s] compile-flags: --target loongarch64-unknown-none-softfloat //@[loongarch64_lp64s] needs-llvm-components: loongarch //@ ignore-backends: gcc +#![cfg_attr(loongarch64_lp64d, feature(asm_experimental_reg))] + #![crate_type = "lib"] #![feature(no_core)] #![no_core] @@ -45,5 +47,48 @@ fn f() { //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f asm!("/* {} */", out(freg) d); //[loongarch32_ilp32s,loongarch64_lp64s]~^ ERROR register class `freg` requires at least one of the following target features: d, f + + asm!("", out("$vr0") _); // ok + asm!("/* {} */", in(vreg) f); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f32` cannot be used with this register class in stable + asm!("/* {} */", out(vreg) _); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + asm!("/* {} */", in(vreg) d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + asm!("/* {} */", out(vreg) d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + + asm!("", out("$xr0") _); // ok + asm!("/* {} */", in(xreg) f); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f32` cannot be used with this register class in stable + asm!("/* {} */", out(xreg) _); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + asm!("/* {} */", in(xreg) d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + asm!("/* {} */", out(xreg) d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + + asm!("", in("$f0") f, in("$vr0") d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch64_lp64s]~| ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + //[loongarch64_lp64d]~^^^^ ERROR register `$vr0` conflicts with register `$f0` + asm!("", in("$f0") f, in("$xr0") d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch64_lp64s]~| ERROR register class `freg` requires at least one of the following target features: d, f + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + //[loongarch64_lp64d]~^^^^ ERROR register `$xr0` conflicts with register `$f0` + asm!("", in("$vr0") f, in("$xr0") d); + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~^ ERROR register class `vreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR register class `xreg` can only be used as a clobber in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f32` cannot be used with this register class in stable + //[loongarch32_ilp32s,loongarch32_ilp32d,loongarch64_lp64s]~| ERROR type `f64` cannot be used with this register class in stable + //[loongarch64_lp64d]~^^^^^ ERROR register `$xr0` conflicts with register `$vr0` } } From 3f89981519378fd0365845f35b308f2f33362e34 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Wed, 24 Jun 2026 23:03:41 +0800 Subject: [PATCH 10/15] tests/asm: verify LoongArch inline asm register modifiers Add assembly tests for LoongArch inline asm register modifiers. Verify that the `w` and `u` modifiers correctly select LSX and LASX register views for `freg`, `vreg`, and `xreg` operands and produce the expected register names in the generated assembly. --- .../assembly-llvm/asm/loongarch-modifiers.rs | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 tests/assembly-llvm/asm/loongarch-modifiers.rs diff --git a/tests/assembly-llvm/asm/loongarch-modifiers.rs b/tests/assembly-llvm/asm/loongarch-modifiers.rs new file mode 100644 index 0000000000000..93a58864ef451 --- /dev/null +++ b/tests/assembly-llvm/asm/loongarch-modifiers.rs @@ -0,0 +1,225 @@ +//@ add-minicore +//@ revisions: loongarch32 loongarch64 + +//@ assembly-output: emit-asm +//@ needs-llvm-components: loongarch + +//@[loongarch32] compile-flags: --target loongarch32-unknown-none +//@[loongarch32] compile-flags: -Ctarget-feature=+32s,+lasx + +//@[loongarch64] compile-flags: --target loongarch64-unknown-none +//@[loongarch64] compile-flags: -Ctarget-feature=+lasx + +//@ compile-flags: -Ctarget-feature=+32s +//@ compile-flags: -Zmerge-functions=disabled + +#![feature(asm_experimental_reg, no_core, repr_simd)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register)] + +extern crate minicore; +use minicore::*; + +#[repr(simd)] +pub struct i8x16([i8; 16]); +#[repr(simd)] +pub struct i16x8([i16; 8]); +#[repr(simd)] +pub struct i32x4([i32; 4]); +#[repr(simd)] +pub struct i64x2([i64; 2]); +#[repr(simd)] +pub struct f32x4([f32; 4]); +#[repr(simd)] +pub struct f64x2([f64; 2]); +#[repr(simd)] +pub struct i8x32([i8; 32]); +#[repr(simd)] +pub struct i16x16([i16; 16]); +#[repr(simd)] +pub struct i32x8([i32; 8]); +#[repr(simd)] +pub struct i64x4([i64; 4]); +#[repr(simd)] +pub struct f32x8([f32; 8]); +#[repr(simd)] +pub struct f64x4([f64; 4]); + +impl Copy for i8x16 {} +impl Copy for i16x8 {} +impl Copy for i32x4 {} +impl Copy for i64x2 {} +impl Copy for f32x4 {} +impl Copy for f64x2 {} +impl Copy for i8x32 {} +impl Copy for i16x16 {} +impl Copy for i32x8 {} +impl Copy for i64x4 {} +impl Copy for f32x8 {} +impl Copy for f64x4 {} + +macro_rules! check { ($func:ident, $ty:ty, $class:ident, $code:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + let y; + asm!($code, out($class) y, in($class) x); + y + } +};} + +// CHECK-LABEL: freg_f32_lsx: +// CHECK: #APP +// CHECK: vfadd.s $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(freg_f32_lsx, f32, freg, "vfadd.s {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: freg_f64_lasx: +// CHECK: #APP +// CHECK: xvfadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(freg_f64_lasx, f64, freg, "xvfadd.d {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_i8x16_lsx: +// CHECK: #APP +// CHECK: vadd.b $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i8x16_lsx, i8x16, vreg, "vadd.b {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_i8x16_lasx: +// CHECK: #APP +// CHECK: xvadd.b $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i8x16_lasx, i8x16, vreg, "xvadd.b {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_i16x8_lsx: +// CHECK: #APP +// CHECK: vadd.h $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i16x8_lsx, i16x8, vreg, "vadd.h {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_i16x8_lasx: +// CHECK: #APP +// CHECK: xvadd.h $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i16x8_lasx, i16x8, vreg, "xvadd.h {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_i32x4_lsx: +// CHECK: #APP +// CHECK: vadd.w $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i32x4_lsx, i32x4, vreg, "vadd.w {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_i32x4_lasx: +// CHECK: #APP +// CHECK: xvadd.w $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i32x4_lasx, i32x4, vreg, "xvadd.w {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_i64x2_lsx: +// CHECK: #APP +// CHECK: vadd.d $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i64x2_lsx, i64x2, vreg, "vadd.d {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_i64x2_lasx: +// CHECK: #APP +// CHECK: xvadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_i64x2_lasx, i64x2, vreg, "xvadd.d {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_f32x4_lsx: +// CHECK: #APP +// CHECK: vfadd.s $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_f32x4_lsx, f32x4, vreg, "vfadd.s {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_f32x4_lasx: +// CHECK: #APP +// CHECK: xvfadd.s $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_f32x4_lasx, f32x4, vreg, "xvfadd.s {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: vreg_f64x2_lsx: +// CHECK: #APP +// CHECK: vfadd.d $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_f64x2_lsx, f64x2, vreg, "vfadd.d {1}, {0}, {0}"); + +// CHECK-LABEL: vreg_f64x2_lasx: +// CHECK: #APP +// CHECK: xvfadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(vreg_f64x2_lasx, f64x2, vreg, "xvfadd.d {1:u}, {0:u}, {0:u}"); + +// CHECK-LABEL: xreg_i8x32_lasx: +// CHECK: #APP +// CHECK: xvadd.b $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i8x32_lasx, i8x32, xreg, "xvadd.b {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_i8x32_lsx: +// CHECK: #APP +// CHECK: vadd.b $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i8x32_lsx, i8x32, xreg, "vadd.b {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_i16x16_lasx: +// CHECK: #APP +// CHECK: xvadd.h $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i16x16_lasx, i16x16, xreg, "xvadd.h {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_i16x16_lsx: +// CHECK: #APP +// CHECK: vadd.h $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i16x16_lsx, i16x16, xreg, "vadd.h {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_i32x8_lasx: +// CHECK: #APP +// CHECK: xvadd.w $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i32x8_lasx, i32x8, xreg, "xvadd.w {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_i32x8_lsx: +// CHECK: #APP +// CHECK: vadd.w $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i32x8_lsx, i32x8, xreg, "vadd.w {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_i64x4_lasx: +// CHECK: #APP +// CHECK: xvadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i64x4_lasx, i64x4, xreg, "xvadd.d {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_i64x4_lsx: +// CHECK: #APP +// CHECK: vadd.d $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_i64x4_lsx, i64x4, xreg, "vadd.d {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_f32x8_lasx: +// CHECK: #APP +// CHECK: xvfadd.s $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_f32x8_lasx, f32x8, xreg, "xvfadd.s {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_f32x8_lsx: +// CHECK: #APP +// CHECK: vfadd.s $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_f32x8_lsx, f32x8, xreg, "vfadd.s {1:w}, {0:w}, {0:w}"); + +// CHECK-LABEL: xreg_f64x4_lasx: +// CHECK: #APP +// CHECK: xvfadd.d $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}}, $xr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_f64x4_lasx, f64x4, xreg, "xvfadd.d {1}, {0}, {0}"); + +// CHECK-LABEL: xreg_f64x4_lsx: +// CHECK: #APP +// CHECK: vfadd.d $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}}, $vr{{[a-z0-9]+}} +// CHECK: #NO_APP +check!(xreg_f64x4_lsx, f64x4, xreg, "vfadd.d {1:w}, {0:w}, {0:w}"); From 2606b40aa766a581c0a9d01212f0bcf92908bcb0 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 20 Jun 2026 17:50:50 +0200 Subject: [PATCH 11/15] std: use `OnceLock` for SGX environment variable storage --- library/std/src/sys/env/sgx.rs | 41 +++++++++------------------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/library/std/src/sys/env/sgx.rs b/library/std/src/sys/env/sgx.rs index 094a7dfa90581..b45acfe7c1ba7 100644 --- a/library/std/src/sys/env/sgx.rs +++ b/library/std/src/sys/env/sgx.rs @@ -1,54 +1,35 @@ -#![allow(implicit_provenance_casts)] // FIXME: this module systematically confuses pointers and integers - pub use super::common::Env; use crate::collections::HashMap; use crate::ffi::{OsStr, OsString}; use crate::io; -use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::sync::{Mutex, Once}; +use crate::sync::{Mutex, OnceLock}; + +type EnvStore = Mutex>; // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] #[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os3ENVE")] -static ENV: Atomic = AtomicUsize::new(0); -// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests -#[cfg_attr(test, linkage = "available_externally")] -#[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os8ENV_INITE")] -static ENV_INIT: Once = Once::new(); -type EnvStore = Mutex>; - -fn get_env_store() -> Option<&'static EnvStore> { - unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() } -} - -fn create_env_store() -> &'static EnvStore { - ENV_INIT.call_once(|| { - ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed) - }); - unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) } -} +static ENV: OnceLock = OnceLock::new(); pub fn env() -> Env { - let clone_to_vec = |map: &HashMap| -> Vec<_> { - map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - }; - - let env = get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default(); + let env = ENV + .get() + .map(|env| env.lock().unwrap().iter().map(|(k, v)| (k.clone(), v.clone())).collect()) + .unwrap_or_default(); Env::new(env) } pub fn getenv(k: &OsStr) -> Option { - get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()) + ENV.get().and_then(|s| s.lock().unwrap().get(k).cloned()) } pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let (k, v) = (k.to_owned(), v.to_owned()); - create_env_store().lock().unwrap().insert(k, v); + ENV.get_or_init(|| EnvStore::default()).lock().unwrap().insert(k.to_owned(), v.to_owned()); Ok(()) } pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - if let Some(env) = get_env_store() { + if let Some(env) = ENV.get() { env.lock().unwrap().remove(k); } Ok(()) From 9134e8632b1ed252261969a8a79aad6e8a916564 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 28 Jun 2026 16:11:08 -0500 Subject: [PATCH 12/15] Fix adjust_ident_and_get_scope with LocalDefId Change HirId to its parent item LocalDefId. --- .../src/hir_ty_lowering/mod.rs | 12 +++++------- compiler/rustc_hir_typeck/src/expr.rs | 17 ++++++++--------- compiler/rustc_hir_typeck/src/method/probe.rs | 5 ++--- compiler/rustc_middle/src/ty/mod.rs | 5 ++--- compiler/rustc_privacy/src/lib.rs | 3 ++- .../src/error_reporting/traits/suggestions.rs | 8 +++++--- 6 files changed, 24 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 4781525ad686e..ecc156fc8f936 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1637,8 +1637,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .inherent_impls(adt_did) .iter() .filter_map(|&impl_| { - let (item, scope) = - self.probe_assoc_item_unchecked(name, assoc_tag, block, impl_)?; + let (item, scope) = self.probe_assoc_item_unchecked(name, assoc_tag, impl_)?; Some(InherentAssocCandidate { impl_, assoc_item: item.def_id, scope }) }) .collect(); @@ -1714,7 +1713,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, scope: DefId, ) -> Option { - let (item, scope) = self.probe_assoc_item_unchecked(ident, assoc_tag, block, scope)?; + let (item, scope) = self.probe_assoc_item_unchecked(ident, assoc_tag, scope)?; self.check_assoc_item(item.def_id, ident, scope, block, span); Some(item) } @@ -1727,12 +1726,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, ident: Ident, assoc_tag: ty::AssocTag, - block: HirId, scope: DefId, ) -> Option<(ty::AssocItem, /*scope*/ DefId)> { let tcx = self.tcx(); - let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block); + let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, self.item_def_id()); // We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()` // instead of calling `filter_by_name_and_kind` which would needlessly normalize the // `ident` again and again. @@ -3427,8 +3425,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } (FIRST_VARIANT, def.non_enum_variant()) }; - let block = tcx.local_def_id_to_hir_id(item_def_id); - let (ident, def_scope) = tcx.adjust_ident_and_get_scope(field, def.did(), block); + let (ident, def_scope) = + tcx.adjust_ident_and_get_scope(field, def.did(), item_def_id); if let Some((field_idx, field)) = variant .fields .iter_enumerated() diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 22c211b199474..0cd80a6a83c87 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2764,9 +2764,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ty::new_error(self.tcx(), guar); } - let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(self.body_id); let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, base_def.did(), fn_body_hir_id); + self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id); if let Some((idx, field)) = self.find_adt_field(*base_def, ident) { self.write_field_index(expr.hir_id, idx); @@ -3768,9 +3767,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match container.kind() { ty::Adt(container_def, args) if container_def.is_enum() => { - let block = self.tcx.local_def_id_to_hir_id(self.body_id); - let (ident, _def_scope) = - self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block); + let ident = self.tcx.adjust_ident(field, container_def.did()); if !self.tcx.features().offset_of_enum() { rustc_session::errors::feature_err( @@ -3806,7 +3803,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { break; }; let (subident, sub_def_scope) = - self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, block); + self.tcx.adjust_ident_and_get_scope(subfield, variant.def_id, self.body_id); let Some((subindex, field)) = variant .fields @@ -3854,9 +3851,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } ty::Adt(container_def, args) => { - let block = self.tcx.local_def_id_to_hir_id(self.body_id); - let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block); + let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope( + field, + container_def.did(), + self.body_id, + ); let fields = &container_def.non_enum_variant().fields; if let Some((index, field)) = fields diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e8921841c4405..c8714c7ed81b6 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -810,9 +810,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) { let is_accessible = if let Some(name) = self.method_name { let item = candidate.item; - let hir_id = self.tcx.local_def_id_to_hir_id(self.body_id); - let def_scope = - self.tcx.adjust_ident_and_get_scope(name, item.container_id(self.tcx), hir_id).1; + let container_id = item.container_id(self.tcx); + let def_scope = self.tcx.adjust_ident_and_get_scope(name, container_id, self.body_id).1; item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx) } else { true diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 98b44f072075a..41079d431edf9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2137,18 +2137,17 @@ impl<'tcx> TyCtxt<'tcx> { ident } - // FIXME(vincenzopalazzo): move the HirId to a LocalDefId pub fn adjust_ident_and_get_scope( self, mut ident: Ident, scope: DefId, - block: hir::HirId, + item_id: LocalDefId, ) -> (Ident, DefId) { let scope = ident .span .normalize_to_macros_2_0_and_adjust(self.expn_that_defined(scope)) .and_then(|actual_expansion| actual_expansion.expn_data().parent_module) - .unwrap_or_else(|| self.parent_module(block).to_def_id()); + .unwrap_or_else(|| self.parent_module_from_def_id(item_id).to_def_id()); (ident, scope) } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 6f09e53f0a8e7..76af497c1d063 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -948,7 +948,8 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { // definition of the field let ident = Ident::new(sym::dummy, use_ctxt); - let (_, def_id) = self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id); + let (_, def_id) = + self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id.owner.def_id); !field.vis.is_accessible_from(def_id, self.tcx) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 4e3a31f0e84b5..3efe8ad3941c2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -334,7 +334,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return; } - let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(typeck_results.hir_owner.def_id); let mut private_candidate: Option<(Ty<'tcx>, Ty<'tcx>, Span)> = None; for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) { @@ -346,8 +345,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { continue; } - let (adjusted_ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field_ident, base_def.did(), fn_body_hir_id); + let (adjusted_ident, def_scope) = self.tcx.adjust_ident_and_get_scope( + field_ident, + base_def.did(), + typeck_results.hir_owner.def_id, + ); let Some((_, field_def)) = base_def.non_enum_variant().fields.iter_enumerated().find(|(_, field)| { From 1f4b09e15a9b1c3f3d4ab4224f57870396e9a329 Mon Sep 17 00:00:00 2001 From: lapla Date: Wed, 24 Jun 2026 08:31:00 +0900 Subject: [PATCH 13/15] Show use-site paths for unevaluated const array lengths --- src/librustdoc/clean/utils.rs | 35 ++++++++----------- .../rustdoc-html/type-const-free-in-array.rs | 11 ++++++ .../type-const-inherent-with-body.rs | 15 ++++++++ 3 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 tests/rustdoc-html/type-const-free-in-array.rs create mode 100644 tests/rustdoc-html/type-const-inherent-with-body.rs diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 554bcb2621ac6..f77e201805a2b 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -350,28 +350,21 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { match n.kind() { - ty::ConstKind::Alias(_, ty::AliasConst { kind, .. }) => match kind { - ty::AliasConstKind::Projection { def_id } => { - if let Some(local_def_id) = def_id.as_local() - && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) - { - rendered_const(tcx, body_id, local_def_id) - } else { - n.to_string() - } - } - ty::AliasConstKind::Inherent { def_id } - | ty::AliasConstKind::Free { def_id } - | ty::AliasConstKind::Anon { def_id } => { - if let Some(local_def_id) = def_id.as_local() - && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) - { - rendered_const(tcx, body_id, local_def_id) - } else { - inline::print_inlined_const(tcx, def_id) - } + ty::ConstKind::Alias(_, ty::AliasConst { kind, .. }) => { + let def_id: DefId = match kind { + ty::AliasConstKind::Projection { def_id } => def_id.into(), + ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::Free { def_id } => def_id.into(), + ty::AliasConstKind::Anon { def_id } => def_id.into(), + }; + if let Some(local_def_id) = def_id.as_local() + && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) + { + rendered_const(tcx, body_id, local_def_id) + } else { + n.to_string() } - }, + } // array lengths are obviously usize ty::ConstKind::Value(cv) if *cv.ty.kind() == ty::Uint(ty::UintTy::Usize) => { cv.to_leaf().to_string() diff --git a/tests/rustdoc-html/type-const-free-in-array.rs b/tests/rustdoc-html/type-const-free-in-array.rs new file mode 100644 index 0000000000000..bea46596caa58 --- /dev/null +++ b/tests/rustdoc-html/type-const-free-in-array.rs @@ -0,0 +1,11 @@ +#![crate_name = "foo"] +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +type const N: usize = 2; + +//@ has 'foo/trait.CollectArray.html' +//@ has - '//pre[@class="rust item-decl"]/code' '[A; N]' +pub trait CollectArray { + fn inner_array(&mut self) -> [A; N]; +} diff --git a/tests/rustdoc-html/type-const-inherent-with-body.rs b/tests/rustdoc-html/type-const-inherent-with-body.rs new file mode 100644 index 0000000000000..e9771b1f606c1 --- /dev/null +++ b/tests/rustdoc-html/type-const-inherent-with-body.rs @@ -0,0 +1,15 @@ +#![crate_name = "foo"] +#![feature(min_generic_const_args, inherent_associated_types)] +#![expect(incomplete_features)] + +pub struct Foo; + +impl Foo { + type const LEN: usize = 4; +} + +//@ has 'foo/fn.mk_array.html' +//@ has - '//pre[@class="rust item-decl"]/code' '[u8; Foo::LEN]' +pub fn mk_array() -> [u8; Foo::LEN] { + [0u8; Foo::LEN] +} From 9596318563b159eb494b50f38f0572b93dc83829 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 1 Jul 2026 09:43:13 -0700 Subject: [PATCH 14/15] rustc_sanitizers: use twox-hash without default features We don't need any features from `twox-hash`, and in particular this removes the last use of `rand v0.8` in our lock file. --- Cargo.lock | 35 ++-------------------------- compiler/rustc_sanitizers/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c14a9f26e175..873182dde9102 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3235,24 +3235,13 @@ dependencies = [ "ptr_meta", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - [[package]] name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.3", ] @@ -3267,16 +3256,6 @@ dependencies = [ "rand_core 0.10.1", ] -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - [[package]] name = "rand_chacha" version = "0.9.0" @@ -3287,15 +3266,6 @@ dependencies = [ "rand_core 0.9.3", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - [[package]] name = "rand_core" version = "0.9.3" @@ -5550,7 +5520,7 @@ dependencies = [ "indicatif", "num", "rand 0.9.2", - "rand_chacha 0.9.0", + "rand_chacha", "rayon", ] @@ -5924,7 +5894,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand 0.8.5", "static_assertions", ] diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml index 9069d2c233db7..8eff14d0cfcfa 100644 --- a/compiler/rustc_sanitizers/Cargo.toml +++ b/compiler/rustc_sanitizers/Cargo.toml @@ -14,5 +14,5 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } tracing = "0.1" -twox-hash = "1.6.3" +twox-hash = { version = "1.6.3", default-features = false } # tidy-alphabetical-end From 13bcaadd77bb348b2191a95ead34ebdce141d906 Mon Sep 17 00:00:00 2001 From: Schneems Date: Thu, 25 Jun 2026 15:11:22 -0500 Subject: [PATCH 15/15] Clarify ExitStatusExt documentation ## Fix struct links There are several structs (`ExitStatus` and `ExitStatusError`) that could be linked, but aren't. Fixed. All links show their struct now instead of previously some showed `process::ExitStatus` when rendered. ## Add `wait` links The docs distinguish `wait` as code, but do not link to **what** wait they're referring to. As this is Unix extension documentation, adding a link to the POSIX standard for `wait`. Showing a construction of an `ExitStatus` for both a signal and an exit code helps to highlight and internalize what **wait status, not an exit status** means. ## Example for `from_raw` Current documentation calls out "The value should be a **wait status, not an exit status**." but it's unclear what exactly that means without a reference or an example. I added a doctest that shows you need to shift by 8 to get the desired result and annotated it with information about how that's derived based on the linked `wait` documentation. ## Example to `ExitStatus::signal` Called out that a signaled process does not also produce an exit code. Added an example. Called out that just because a process reports that it was not terminated via a signal doesn't mean it never got one (it could be handled). Also added a note correlating shell behavior to exit codes: GNU Bash manual: https://web.archive.org/web/20260625050034/https://www.gnu.org/software/bash/manual/bash.html#Exit-Status-1 > [...] a fatal signal whose number is N, Bash uses the value 128+N as the exit status. FreeBSD bash(1) man page: https://man.freebsd.org/bash/1 > The return value [...] 128+n if the command is terminated by signal n. ## Panic scope on `from_raw` This change is based on preference/experience: When I originally read this documentation, it was because I clicked through `Output` -> `ExitStatus`. While the text "Creates a new `ExitStatus` or `ExitStatusError`" is present at the top, I didn't internalize what that meant exactly. Therefore, when I got to the panic section, I was mildly confused. This edit makes the behavior more explicitly attached to the type. While the original format had all of the same information available, it was left for the reader to bridge that "making an ExitStatusError" is referring to `ExitStatus::from_raw` as that's the only constructor (for now). Making this bridge more explicit also future-proofs us for future constructors (if they're ever added) that may or may not panic. While the context of the panic documentation is directly attached to `from_raw`, it's slightly (pedantically) more correct to not imply that ANY way of creating an ExitStatusError` from a `wait` of `0` will panic (a different constructor could perhaps return an option instead). Technically, the syntax isn't correct, as it should be `::from_raw`, but I think this conveys intent without being overly verbose. Co-authored-by: Colin Casey --- library/std/src/os/unix/process.rs | 110 ++++++++++++++++++++++++----- 1 file changed, 94 insertions(+), 16 deletions(-) diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 8a7b94d914ab0..325a428f2e3d4 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -7,6 +7,8 @@ use crate::ffi::OsStr; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; +#[cfg(doc)] +use crate::process::{ExitStatus, ExitStatusError}; use crate::sys::process::ChildPipe; use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, process, sys}; @@ -272,35 +274,105 @@ impl CommandExt for process::Command { } } -/// Unix-specific extensions to [`process::ExitStatus`] and -/// [`ExitStatusError`](process::ExitStatusError). +/// Unix-specific extensions to [`ExitStatus`] and [`ExitStatusError`]. /// -/// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as +/// On Unix, [`ExitStatus`] **does not necessarily represent an exit status**, as /// passed to the `_exit` system call or returned by -/// [`ExitStatus::code()`](crate::process::ExitStatus::code). It represents **any wait status** -/// as returned by one of the `wait` family of system +/// [`ExitStatus::code()`](ExitStatus::code). It represents **any wait status** +/// as returned by one of the [`wait`] family of system /// calls. /// -/// A Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but can also +/// A Unix wait status (a Rust [`ExitStatus`]) can represent a Unix exit status, but can also /// represent other kinds of process event. +/// +/// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "rust1", since = "1.0.0")] pub impl(self) trait ExitStatusExt { - /// Creates a new `ExitStatus` or `ExitStatusError` from the raw underlying integer status - /// value from `wait` + /// Creates a new [`ExitStatus`] or [`ExitStatusError`] from the raw underlying integer status + /// value from [`wait`]. /// /// The value should be a **wait status, not an exit status**. /// + /// # Example + /// + /// A signal-terminated [`wait`] status carries the signal number, which [`ExitStatus::signal`] + /// recovers using the platform's [`WTERMSIG`][`wait`] macro. Note that the bit layout of a + /// wait status is **not** specified by POSIX and is platform-specific. By convention on most + /// Unix platforms, the signal number occupies the low 7 bits with the exit-code byte left + /// zero, so a bare signal number between 1 and 126 is treated as a signal-terminated wait + /// status. The following example relies on that convention and is therefore not guaranteed to + /// hold on every target: + /// + /// ``` + /// # if cfg!(target_os = "fuchsia") { return; } + /// use std::os::unix::process::ExitStatusExt; + /// use std::process::ExitStatus; + /// + /// let signal = 15; // SIGTERM + /// assert!(signal > 0 && signal < 0x7f, "not a valid Unix termination signal: {signal}"); + /// + /// let status = ExitStatus::from_raw(signal); + /// assert!(!status.success()); + /// assert_eq!(status.code(), None); + /// assert_eq!(status.signal(), Some(15)); + /// ``` + /// + /// Generating an [`ExitStatus`] with a given exit code (0-255) is system-dependent. + /// The value returned by [`ExitStatus::code`] is specified to come from applying the + /// [`WEXITSTATUS`][`wait`] macro, but there is no POSIX-specified constructor and the bit + /// layout is left unspecified. By near-universal convention every Unix libc stores the + /// 8-bit exit code in bits 8..16, so a status built with `(code & 0xff) << 8` will usually + /// round-trip back to the original exit code: + /// + /// ``` + /// # if cfg!(target_os = "fuchsia") { return; } + /// use std::os::unix::process::ExitStatusExt; + /// use std::process::ExitStatus; + /// + /// let code = 41; + /// let status = ExitStatus::from_raw((code & 0xff) << 8); + /// assert_eq!(status.code(), Some(41)); + /// assert!(!status.success()); + /// ``` + /// /// # Panics /// - /// Panics on an attempt to make an `ExitStatusError` from a wait status of `0`. + /// - `ExitStatusError::from_raw` panics on an attempt to make an [`ExitStatusError`] from a + /// [`wait`] status of `0`. + /// - `ExitStatus::from_raw` always succeeds and never panics. /// - /// Making an `ExitStatus` always succeeds and never panics. + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "exit_status_from", since = "1.12.0")] fn from_raw(raw: i32) -> Self; /// If the process was terminated by a signal, returns that signal. /// - /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`. + /// In other words, if [`WIFSIGNALED`][`wait`], this returns [`WTERMSIG`][`wait`]. For such a status, + /// [`ExitStatus::code`] returns `None`: + /// + /// ``` + /// # if cfg!(target_os = "fuchsia") { return; } + /// use std::os::unix::process::ExitStatusExt; + /// use std::process::ExitStatus; + /// + /// let sigterm = 15; + /// let status = ExitStatus::from_raw(sigterm); + /// assert_eq!(status.code(), None); + /// assert_eq!(status.signal(), Some(sigterm)); + /// ``` + /// + /// A process that receives a signal may catch and handle it, then exit normally with an + /// exit code. When that happens, `signal` returns `None`. + /// + /// Rust does not pass commands through a shell, such as `bash` and `sh`, but it + /// is possible to do so manually. When invoking a shell, the signal value indicates whether + /// the top-level shell itself received a terminating signal. If instead a command *within* + /// an invoked shell receives a terminating signal, many shells convert the signal number + /// into an exit code by adding 128. For example, a command run under `sh` that receives a + /// [`SIGTERM`] canonically causes the shell to report an exit code of `15 + 128`, i.e. `143`. + /// + /// [`SIGTERM`]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/kill.html + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "rust1", since = "1.0.0")] fn signal(&self) -> Option; @@ -310,21 +382,27 @@ pub impl(self) trait ExitStatusExt { /// If the process was stopped by a signal, returns that signal. /// - /// In other words, if `WIFSTOPPED`, this returns `WSTOPSIG`. This is only possible if the status came from - /// a `wait` system call which was passed `WUNTRACED`, and was then converted into an `ExitStatus`. + /// In other words, if [`WIFSTOPPED`][`wait`], this returns [`WSTOPSIG`][`wait`]. This is only possible if the status came from + /// a [`wait`] system call which was passed [`WUNTRACED`][`wait`], and was then converted into an [`ExitStatus`]. + /// + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "unix_process_wait_more", since = "1.58.0")] fn stopped_signal(&self) -> Option; /// Whether the process was continued from a stopped status. /// - /// Ie, `WIFCONTINUED`. This is only possible if the status came from a `wait` system call - /// which was passed `WCONTINUED`, and was then converted into an `ExitStatus`. + /// I.e. [`WIFCONTINUED`][`wait`]. This is only possible if the status came from a [`wait`] system call + /// which was passed [`WCONTINUED`][`wait`], and was then converted into an [`ExitStatus`]. + /// + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "unix_process_wait_more", since = "1.58.0")] fn continued(&self) -> bool; - /// Returns the underlying raw `wait` status. + /// Returns the underlying raw [`wait`] status. /// /// The returned integer is a **wait status, not an exit status**. + /// + /// [`wait`]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html #[stable(feature = "unix_process_wait_more", since = "1.58.0")] fn into_raw(self) -> i32; }