mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-11 11:36:49 +00:00
Auto merge of #113344 - scottmcm:alt-slice-zst-handing, r=the8472
Get `!nonnull` metadata on slice iterators, without `assume`s This updates the non-ZST paths to read the end pointer through a pointer-to-`NonNull`, so that they all get `!nonnull` metadata. That means that the last `assume(!ptr.is_null())` can be deleted, without impacting codegen -- the codegen tests confirm the LLVM-IR ends up exactly the same as before.
This commit is contained in:
commit
c720a9cd12
@ -462,6 +462,30 @@ impl<T: ?Sized> NonNull<T> {
|
|||||||
// And the caller promised the `delta` is sound to add.
|
// And the caller promised the `delta` is sound to add.
|
||||||
unsafe { NonNull { pointer: self.pointer.add(delta) } }
|
unsafe { NonNull { pointer: self.pointer.add(delta) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See [`pointer::sub`] for semantics and safety requirements.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) const unsafe fn sub(self, delta: usize) -> Self
|
||||||
|
where
|
||||||
|
T: Sized,
|
||||||
|
{
|
||||||
|
// SAFETY: We require that the delta stays in-bounds of the object, and
|
||||||
|
// thus it cannot become null, as no legal objects can be allocated
|
||||||
|
// in such as way that the null address is part of them.
|
||||||
|
// And the caller promised the `delta` is sound to subtract.
|
||||||
|
unsafe { NonNull { pointer: self.pointer.sub(delta) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`pointer::sub_ptr`] for semantics and safety requirements.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) const unsafe fn sub_ptr(self, subtrahend: Self) -> usize
|
||||||
|
where
|
||||||
|
T: Sized,
|
||||||
|
{
|
||||||
|
// SAFETY: The caller promised that this is safe to do, and
|
||||||
|
// the non-nullness is irrelevant to the operation.
|
||||||
|
unsafe { self.pointer.sub_ptr(subtrahend.pointer) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> NonNull<[T]> {
|
impl<T> NonNull<[T]> {
|
||||||
|
@ -13,7 +13,7 @@ use crate::iter::{
|
|||||||
use crate::marker::{PhantomData, Send, Sized, Sync};
|
use crate::marker::{PhantomData, Send, Sized, Sync};
|
||||||
use crate::mem::{self, SizedTypeProperties};
|
use crate::mem::{self, SizedTypeProperties};
|
||||||
use crate::num::NonZeroUsize;
|
use crate::num::NonZeroUsize;
|
||||||
use crate::ptr::{invalid, invalid_mut, NonNull};
|
use crate::ptr::{self, invalid, invalid_mut, NonNull};
|
||||||
|
|
||||||
use super::{from_raw_parts, from_raw_parts_mut};
|
use super::{from_raw_parts, from_raw_parts_mut};
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ pub struct Iter<'a, T: 'a> {
|
|||||||
/// For non-ZSTs, the non-null pointer to the past-the-end element.
|
/// For non-ZSTs, the non-null pointer to the past-the-end element.
|
||||||
///
|
///
|
||||||
/// For ZSTs, this is `ptr::invalid(len)`.
|
/// For ZSTs, this is `ptr::invalid(len)`.
|
||||||
end: *const T,
|
end_or_len: *const T,
|
||||||
_marker: PhantomData<&'a T>,
|
_marker: PhantomData<&'a T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,9 +90,9 @@ impl<'a, T> Iter<'a, T> {
|
|||||||
let ptr = slice.as_ptr();
|
let ptr = slice.as_ptr();
|
||||||
// SAFETY: Similar to `IterMut::new`.
|
// SAFETY: Similar to `IterMut::new`.
|
||||||
unsafe {
|
unsafe {
|
||||||
let end = if T::IS_ZST { invalid(slice.len()) } else { ptr.add(slice.len()) };
|
let end_or_len = if T::IS_ZST { invalid(slice.len()) } else { ptr.add(slice.len()) };
|
||||||
|
|
||||||
Self { ptr: NonNull::new_unchecked(ptr as *mut T), end, _marker: PhantomData }
|
Self { ptr: NonNull::new_unchecked(ptr as *mut T), end_or_len, _marker: PhantomData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ impl<'a, T> Iter<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, {
|
iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, {
|
||||||
fn is_sorted_by<F>(self, mut compare: F) -> bool
|
fn is_sorted_by<F>(self, mut compare: F) -> bool
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
@ -142,7 +142,7 @@ iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, {
|
|||||||
impl<T> Clone for Iter<'_, T> {
|
impl<T> Clone for Iter<'_, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Iter { ptr: self.ptr, end: self.end, _marker: self._marker }
|
Iter { ptr: self.ptr, end_or_len: self.end_or_len, _marker: self._marker }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +189,7 @@ pub struct IterMut<'a, T: 'a> {
|
|||||||
/// For non-ZSTs, the non-null pointer to the past-the-end element.
|
/// For non-ZSTs, the non-null pointer to the past-the-end element.
|
||||||
///
|
///
|
||||||
/// For ZSTs, this is `ptr::invalid_mut(len)`.
|
/// For ZSTs, this is `ptr::invalid_mut(len)`.
|
||||||
end: *mut T,
|
end_or_len: *mut T,
|
||||||
_marker: PhantomData<&'a mut T>,
|
_marker: PhantomData<&'a mut T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,15 +220,16 @@ impl<'a, T> IterMut<'a, T> {
|
|||||||
// for direct pointer equality with `ptr` to check if the iterator is
|
// for direct pointer equality with `ptr` to check if the iterator is
|
||||||
// done.
|
// done.
|
||||||
//
|
//
|
||||||
// In the case of a ZST, the end pointer is just the start pointer plus
|
// In the case of a ZST, the end pointer is just the length. It's never
|
||||||
// the length, to also allows for the fast `ptr == end` check.
|
// used as a pointer at all, and thus it's fine to have no provenance.
|
||||||
//
|
//
|
||||||
// See the `next_unchecked!` and `is_empty!` macros as well as the
|
// See the `next_unchecked!` and `is_empty!` macros as well as the
|
||||||
// `post_inc_start` method for more information.
|
// `post_inc_start` method for more information.
|
||||||
unsafe {
|
unsafe {
|
||||||
let end = if T::IS_ZST { invalid_mut(slice.len()) } else { ptr.add(slice.len()) };
|
let end_or_len =
|
||||||
|
if T::IS_ZST { invalid_mut(slice.len()) } else { ptr.add(slice.len()) };
|
||||||
|
|
||||||
Self { ptr: NonNull::new_unchecked(ptr), end, _marker: PhantomData }
|
Self { ptr: NonNull::new_unchecked(ptr), end_or_len, _marker: PhantomData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,7 +361,7 @@ impl<T> AsRef<[T]> for IterMut<'_, T> {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, {}}
|
iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, {}}
|
||||||
|
|
||||||
/// An internal abstraction over the splitting iterators, so that
|
/// An internal abstraction over the splitting iterators, so that
|
||||||
/// splitn, splitn_mut etc can be implemented once.
|
/// splitn, splitn_mut etc can be implemented once.
|
||||||
|
@ -1,45 +1,62 @@
|
|||||||
//! Macros used by iterators of slice.
|
//! Macros used by iterators of slice.
|
||||||
|
|
||||||
// Shrinks the iterator when T is a ZST, setting the length to `new_len`.
|
/// Convenience & performance macro for consuming the `end_or_len` field, by
|
||||||
// `new_len` must not exceed `self.len()`.
|
/// giving a `(&mut) usize` or `(&mut) NonNull<T>` depending whether `T` is
|
||||||
macro_rules! zst_set_len {
|
/// or is not a ZST respectively.
|
||||||
($self: ident, $new_len: expr) => {{
|
///
|
||||||
|
/// Internally, this reads the `end` through a pointer-to-`NonNull` so that
|
||||||
|
/// it'll get the appropriate non-null metadata in the backend without needing
|
||||||
|
/// to call `assume` manually.
|
||||||
|
macro_rules! if_zst {
|
||||||
|
(mut $this:ident, $len:ident => $zst_body:expr, $end:ident => $other_body:expr,) => {{
|
||||||
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block
|
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block
|
||||||
|
|
||||||
// SAFETY: same as `invalid(_mut)`, but the macro doesn't know
|
if T::IS_ZST {
|
||||||
// which versions of that function to call, so open-code it.
|
// SAFETY: for ZSTs, the pointer is storing a provenance-free length,
|
||||||
$self.end = unsafe { mem::transmute::<usize, _>($new_len) };
|
// so consuming and updating it as a `usize` is fine.
|
||||||
|
let $len = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::<usize>() };
|
||||||
|
$zst_body
|
||||||
|
} else {
|
||||||
|
// SAFETY: for non-ZSTs, the type invariant ensures it cannot be null
|
||||||
|
let $end = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::<NonNull<T>>() };
|
||||||
|
$other_body
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
}
|
($this:ident, $len:ident => $zst_body:expr, $end:ident => $other_body:expr,) => {{
|
||||||
|
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block
|
||||||
|
|
||||||
// Shrinks the iterator when T is a ZST, reducing the length by `n`.
|
if T::IS_ZST {
|
||||||
// `n` must not exceed `self.len()`.
|
let $len = $this.end_or_len.addr();
|
||||||
macro_rules! zst_shrink {
|
$zst_body
|
||||||
($self: ident, $n: ident) => {
|
} else {
|
||||||
let new_len = $self.end.addr() - $n;
|
// SAFETY: for non-ZSTs, the type invariant ensures it cannot be null
|
||||||
zst_set_len!($self, new_len);
|
let $end = unsafe { *ptr::addr_of!($this.end_or_len).cast::<NonNull<T>>() };
|
||||||
};
|
$other_body
|
||||||
|
}
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inlining is_empty and len makes a huge performance difference
|
// Inlining is_empty and len makes a huge performance difference
|
||||||
macro_rules! is_empty {
|
macro_rules! is_empty {
|
||||||
($self: ident) => {
|
($self: ident) => {
|
||||||
if T::IS_ZST { $self.end.addr() == 0 } else { $self.ptr.as_ptr() as *const _ == $self.end }
|
if_zst!($self,
|
||||||
|
len => len == 0,
|
||||||
|
end => $self.ptr == end,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! len {
|
macro_rules! len {
|
||||||
($self: ident) => {{
|
($self: ident) => {{
|
||||||
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block
|
if_zst!($self,
|
||||||
|
len => len,
|
||||||
if T::IS_ZST {
|
end => {
|
||||||
$self.end.addr()
|
// To get rid of some bounds checks (see `position`), we use ptr_sub instead of
|
||||||
} else {
|
// offset_from (Tested by `codegen/slice-position-bounds-check`.)
|
||||||
// To get rid of some bounds checks (see `position`), we use ptr_sub instead of
|
// SAFETY: by the type invariant pointers are aligned and `start <= end`
|
||||||
// offset_from (Tested by `codegen/slice-position-bounds-check`.)
|
unsafe { end.sub_ptr($self.ptr) }
|
||||||
// SAFETY: by the type invariant pointers are aligned and `start <= end`
|
},
|
||||||
unsafe { $self.end.sub_ptr($self.ptr.as_ptr()) }
|
)
|
||||||
}
|
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,20 +67,21 @@ macro_rules! iterator {
|
|||||||
$elem:ty,
|
$elem:ty,
|
||||||
$raw_mut:tt,
|
$raw_mut:tt,
|
||||||
{$( $mut_:tt )?},
|
{$( $mut_:tt )?},
|
||||||
|
$into_ref:ident,
|
||||||
{$($extra:tt)*}
|
{$($extra:tt)*}
|
||||||
) => {
|
) => {
|
||||||
// Returns the first element and moves the start of the iterator forwards by 1.
|
// Returns the first element and moves the start of the iterator forwards by 1.
|
||||||
// Greatly improves performance compared to an inlined function. The iterator
|
// Greatly improves performance compared to an inlined function. The iterator
|
||||||
// must not be empty.
|
// must not be empty.
|
||||||
macro_rules! next_unchecked {
|
macro_rules! next_unchecked {
|
||||||
($self: ident) => {& $( $mut_ )? *$self.post_inc_start(1)}
|
($self: ident) => { $self.post_inc_start(1).$into_ref() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the last element and moves the end of the iterator backwards by 1.
|
// Returns the last element and moves the end of the iterator backwards by 1.
|
||||||
// Greatly improves performance compared to an inlined function. The iterator
|
// Greatly improves performance compared to an inlined function. The iterator
|
||||||
// must not be empty.
|
// must not be empty.
|
||||||
macro_rules! next_back_unchecked {
|
macro_rules! next_back_unchecked {
|
||||||
($self: ident) => {& $( $mut_ )? *$self.pre_dec_end(1)}
|
($self: ident) => { $self.pre_dec_end(1).$into_ref() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> $name<'a, T> {
|
impl<'a, T> $name<'a, T> {
|
||||||
@ -80,33 +98,40 @@ macro_rules! iterator {
|
|||||||
// returning the old start.
|
// returning the old start.
|
||||||
// Unsafe because the offset must not exceed `self.len()`.
|
// Unsafe because the offset must not exceed `self.len()`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn post_inc_start(&mut self, offset: usize) -> * $raw_mut T {
|
unsafe fn post_inc_start(&mut self, offset: usize) -> NonNull<T> {
|
||||||
let old = self.ptr;
|
let old = self.ptr;
|
||||||
if T::IS_ZST {
|
|
||||||
zst_shrink!(self, offset);
|
// SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`,
|
||||||
} else {
|
// so this new pointer is inside `self` and thus guaranteed to be non-null.
|
||||||
// SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`,
|
unsafe {
|
||||||
// so this new pointer is inside `self` and thus guaranteed to be non-null.
|
if_zst!(mut self,
|
||||||
self.ptr = unsafe { self.ptr.add(offset) };
|
len => *len = len.unchecked_sub(offset),
|
||||||
|
_end => self.ptr = self.ptr.add(offset),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
old.as_ptr()
|
old
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for moving the end of the iterator backwards by `offset` elements,
|
// Helper function for moving the end of the iterator backwards by `offset` elements,
|
||||||
// returning the new end.
|
// returning the new end.
|
||||||
// Unsafe because the offset must not exceed `self.len()`.
|
// Unsafe because the offset must not exceed `self.len()`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn pre_dec_end(&mut self, offset: usize) -> * $raw_mut T {
|
unsafe fn pre_dec_end(&mut self, offset: usize) -> NonNull<T> {
|
||||||
if T::IS_ZST {
|
if_zst!(mut self,
|
||||||
zst_shrink!(self, offset);
|
// SAFETY: By our precondition, `offset` can be at most the
|
||||||
self.ptr.as_ptr()
|
// current length, so the subtraction can never overflow.
|
||||||
} else {
|
len => unsafe {
|
||||||
|
*len = len.unchecked_sub(offset);
|
||||||
|
self.ptr
|
||||||
|
},
|
||||||
// SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`,
|
// SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`,
|
||||||
// which is guaranteed to not overflow an `isize`. Also, the resulting pointer
|
// which is guaranteed to not overflow an `isize`. Also, the resulting pointer
|
||||||
// is in bounds of `slice`, which fulfills the other requirements for `offset`.
|
// is in bounds of `slice`, which fulfills the other requirements for `offset`.
|
||||||
self.end = unsafe { self.end.sub(offset) };
|
end => unsafe {
|
||||||
self.end
|
*end = end.sub(offset);
|
||||||
}
|
*end
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,13 +156,9 @@ macro_rules! iterator {
|
|||||||
fn next(&mut self) -> Option<$elem> {
|
fn next(&mut self) -> Option<$elem> {
|
||||||
// could be implemented with slices, but this avoids bounds checks
|
// could be implemented with slices, but this avoids bounds checks
|
||||||
|
|
||||||
// SAFETY: `assume` call is safe because slices over non-ZSTs must
|
// SAFETY: The call to `next_unchecked!` is
|
||||||
// have a non-null end pointer. The call to `next_unchecked!` is
|
|
||||||
// safe since we check if the iterator is empty first.
|
// safe since we check if the iterator is empty first.
|
||||||
unsafe {
|
unsafe {
|
||||||
if !<T>::IS_ZST {
|
|
||||||
assume(!self.end.is_null());
|
|
||||||
}
|
|
||||||
if is_empty!(self) {
|
if is_empty!(self) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -161,14 +182,10 @@ macro_rules! iterator {
|
|||||||
fn nth(&mut self, n: usize) -> Option<$elem> {
|
fn nth(&mut self, n: usize) -> Option<$elem> {
|
||||||
if n >= len!(self) {
|
if n >= len!(self) {
|
||||||
// This iterator is now empty.
|
// This iterator is now empty.
|
||||||
if T::IS_ZST {
|
if_zst!(mut self,
|
||||||
zst_set_len!(self, 0);
|
len => *len = 0,
|
||||||
} else {
|
end => self.ptr = *end,
|
||||||
// SAFETY: end can't be 0 if T isn't ZST because ptr isn't 0 and end >= ptr
|
);
|
||||||
unsafe {
|
|
||||||
self.ptr = NonNull::new_unchecked(self.end as *mut T);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// SAFETY: We are in bounds. `post_inc_start` does the right thing even for ZSTs.
|
// SAFETY: We are in bounds. `post_inc_start` does the right thing even for ZSTs.
|
||||||
@ -375,13 +392,9 @@ macro_rules! iterator {
|
|||||||
fn next_back(&mut self) -> Option<$elem> {
|
fn next_back(&mut self) -> Option<$elem> {
|
||||||
// could be implemented with slices, but this avoids bounds checks
|
// could be implemented with slices, but this avoids bounds checks
|
||||||
|
|
||||||
// SAFETY: `assume` call is safe because slices over non-ZSTs must
|
// SAFETY: The call to `next_back_unchecked!`
|
||||||
// have a non-null end pointer. The call to `next_back_unchecked!`
|
|
||||||
// is safe since we check if the iterator is empty first.
|
// is safe since we check if the iterator is empty first.
|
||||||
unsafe {
|
unsafe {
|
||||||
if !<T>::IS_ZST {
|
|
||||||
assume(!self.end.is_null());
|
|
||||||
}
|
|
||||||
if is_empty!(self) {
|
if is_empty!(self) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -394,11 +407,10 @@ macro_rules! iterator {
|
|||||||
fn nth_back(&mut self, n: usize) -> Option<$elem> {
|
fn nth_back(&mut self, n: usize) -> Option<$elem> {
|
||||||
if n >= len!(self) {
|
if n >= len!(self) {
|
||||||
// This iterator is now empty.
|
// This iterator is now empty.
|
||||||
if T::IS_ZST {
|
if_zst!(mut self,
|
||||||
zst_set_len!(self, 0);
|
len => *len = 0,
|
||||||
} else {
|
end => *end = self.ptr,
|
||||||
self.end = self.ptr.as_ptr();
|
);
|
||||||
}
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// SAFETY: We are in bounds. `pre_dec_end` does the right thing even for ZSTs.
|
// SAFETY: We are in bounds. `pre_dec_end` does the right thing even for ZSTs.
|
||||||
|
@ -34,6 +34,7 @@ pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN<NotCopy>) -> Option<NotCop
|
|||||||
// CHECK: [[EMPTY]]:
|
// CHECK: [[EMPTY]]:
|
||||||
// CHECK-NOT: br
|
// CHECK-NOT: br
|
||||||
// CHECK: phi i16
|
// CHECK: phi i16
|
||||||
|
// CHECK-SAME: [ %[[VAL]], %[[NOT_EMPTY]] ]
|
||||||
// CHECK-NOT: br
|
// CHECK-NOT: br
|
||||||
// CHECK: ret
|
// CHECK: ret
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ type Demo = [u8; 3];
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn slice_iter_len_eq_zero(y: std::slice::Iter<'_, Demo>) -> bool {
|
pub fn slice_iter_len_eq_zero(y: std::slice::Iter<'_, Demo>) -> bool {
|
||||||
// CHECK-NOT: sub
|
// CHECK-NOT: sub
|
||||||
// CHECK: %_0 = icmp eq {{i8\*|ptr}} {{%1|%0}}, {{%1|%0}}
|
// CHECK: %[[RET:.+]] = icmp eq {{i8\*|ptr}} {{%1|%0}}, {{%1|%0}}
|
||||||
// CHECK: ret i1 %_0
|
// CHECK: ret i1 %[[RET]]
|
||||||
y.len() == 0
|
y.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,16 @@
|
|||||||
// compile-flags: -O
|
// compile-flags: -O
|
||||||
// ignore-debug (these add extra checks that make it hard to verify)
|
// ignore-debug (these add extra checks that make it hard to verify)
|
||||||
#![crate_type = "lib"]
|
#![crate_type = "lib"]
|
||||||
|
#![feature(exact_size_is_empty)]
|
||||||
|
|
||||||
// The slice iterator used to `assume` that the `start` pointer was non-null.
|
// The slice iterator used to `assume` that the `start` pointer was non-null.
|
||||||
// That ought to be unneeded, though, since the type is `NonNull`, so this test
|
// That ought to be unneeded, though, since the type is `NonNull`, so this test
|
||||||
// confirms that the appropriate metadata is included to denote that.
|
// confirms that the appropriate metadata is included to denote that.
|
||||||
|
|
||||||
|
// It also used to `assume` the `end` pointer was non-null, but that's no longer
|
||||||
|
// needed as the code changed to read it as a `NonNull`, and thus gets the
|
||||||
|
// appropriate `!nonnull` annotations naturally.
|
||||||
|
|
||||||
// CHECK-LABEL: @slice_iter_next(
|
// CHECK-LABEL: @slice_iter_next(
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn slice_iter_next<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32> {
|
pub fn slice_iter_next<'a>(it: &mut std::slice::Iter<'a, u32>) -> Option<&'a u32> {
|
||||||
@ -75,3 +80,37 @@ pub fn slice_iter_mut_new(slice: &mut [u32]) -> std::slice::IterMut<'_, u32> {
|
|||||||
// CHECK: }
|
// CHECK: }
|
||||||
slice.iter_mut()
|
slice.iter_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @slice_iter_is_empty
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn slice_iter_is_empty(it: &std::slice::Iter<'_, u32>) -> bool {
|
||||||
|
// CHECK: %[[ENDP:.+]] = getelementptr{{.+}}ptr %it,{{.+}} 1
|
||||||
|
// CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
|
||||||
|
// CHECK-SAME: !nonnull
|
||||||
|
// CHECK-SAME: !noundef
|
||||||
|
// CHECK: %[[START:.+]] = load ptr, ptr %it,
|
||||||
|
// CHECK-SAME: !nonnull
|
||||||
|
// CHECK-SAME: !noundef
|
||||||
|
|
||||||
|
// CHECK: %[[RET:.+]] = icmp eq ptr %[[START]], %[[END]]
|
||||||
|
// CHECK: ret i1 %[[RET]]
|
||||||
|
it.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @slice_iter_len
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn slice_iter_len(it: &std::slice::Iter<'_, u32>) -> usize {
|
||||||
|
// CHECK: %[[START:.+]] = load ptr, ptr %it,
|
||||||
|
// CHECK-SAME: !nonnull
|
||||||
|
// CHECK-SAME: !noundef
|
||||||
|
// CHECK: %[[ENDP:.+]] = getelementptr{{.+}}ptr %it,{{.+}} 1
|
||||||
|
// CHECK: %[[END:.+]] = load ptr, ptr %[[ENDP]]
|
||||||
|
// CHECK-SAME: !nonnull
|
||||||
|
// CHECK-SAME: !noundef
|
||||||
|
|
||||||
|
// CHECK: ptrtoint
|
||||||
|
// CHECK: ptrtoint
|
||||||
|
// CHECK: sub nuw
|
||||||
|
// CHECK: lshr exact
|
||||||
|
it.len()
|
||||||
|
}
|
||||||
|
@ -38,7 +38,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
|
|||||||
scope 6 {
|
scope 6 {
|
||||||
let _7: *const T;
|
let _7: *const T;
|
||||||
scope 7 {
|
scope 7 {
|
||||||
debug end => _7;
|
debug end_or_len => _7;
|
||||||
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
||||||
debug ptr => _9;
|
debug ptr => _9;
|
||||||
let mut _10: *const T;
|
let mut _10: *const T;
|
||||||
@ -138,7 +138,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
|
|||||||
StorageDead(_9);
|
StorageDead(_9);
|
||||||
StorageLive(_12);
|
StorageLive(_12);
|
||||||
_12 = _7;
|
_12 = _7;
|
||||||
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
||||||
StorageDead(_12);
|
StorageDead(_12);
|
||||||
StorageDead(_11);
|
StorageDead(_11);
|
||||||
StorageDead(_7);
|
StorageDead(_7);
|
||||||
|
@ -38,7 +38,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
|
|||||||
scope 6 {
|
scope 6 {
|
||||||
let _7: *const T;
|
let _7: *const T;
|
||||||
scope 7 {
|
scope 7 {
|
||||||
debug end => _7;
|
debug end_or_len => _7;
|
||||||
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
||||||
debug ptr => _9;
|
debug ptr => _9;
|
||||||
let mut _10: *const T;
|
let mut _10: *const T;
|
||||||
@ -138,7 +138,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
|
|||||||
StorageDead(_9);
|
StorageDead(_9);
|
||||||
StorageLive(_12);
|
StorageLive(_12);
|
||||||
_12 = _7;
|
_12 = _7;
|
||||||
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
||||||
StorageDead(_12);
|
StorageDead(_12);
|
||||||
StorageDead(_11);
|
StorageDead(_11);
|
||||||
StorageDead(_7);
|
StorageDead(_7);
|
||||||
|
@ -35,7 +35,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () {
|
|||||||
scope 6 {
|
scope 6 {
|
||||||
let _7: *const T;
|
let _7: *const T;
|
||||||
scope 7 {
|
scope 7 {
|
||||||
debug end => _7;
|
debug end_or_len => _7;
|
||||||
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
||||||
debug ptr => _9;
|
debug ptr => _9;
|
||||||
let mut _10: *const T;
|
let mut _10: *const T;
|
||||||
@ -128,7 +128,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () {
|
|||||||
StorageDead(_9);
|
StorageDead(_9);
|
||||||
StorageLive(_12);
|
StorageLive(_12);
|
||||||
_12 = _7;
|
_12 = _7;
|
||||||
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
||||||
StorageDead(_12);
|
StorageDead(_12);
|
||||||
StorageDead(_11);
|
StorageDead(_11);
|
||||||
StorageDead(_7);
|
StorageDead(_7);
|
||||||
|
@ -35,7 +35,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () {
|
|||||||
scope 6 {
|
scope 6 {
|
||||||
let _7: *const T;
|
let _7: *const T;
|
||||||
scope 7 {
|
scope 7 {
|
||||||
debug end => _7;
|
debug end_or_len => _7;
|
||||||
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
||||||
debug ptr => _9;
|
debug ptr => _9;
|
||||||
let mut _10: *const T;
|
let mut _10: *const T;
|
||||||
@ -128,7 +128,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () {
|
|||||||
StorageDead(_9);
|
StorageDead(_9);
|
||||||
StorageLive(_12);
|
StorageLive(_12);
|
||||||
_12 = _7;
|
_12 = _7;
|
||||||
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
||||||
StorageDead(_12);
|
StorageDead(_12);
|
||||||
StorageDead(_11);
|
StorageDead(_11);
|
||||||
StorageDead(_7);
|
StorageDead(_7);
|
||||||
|
@ -39,7 +39,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
|
|||||||
scope 6 {
|
scope 6 {
|
||||||
let _7: *const T;
|
let _7: *const T;
|
||||||
scope 7 {
|
scope 7 {
|
||||||
debug end => _7;
|
debug end_or_len => _7;
|
||||||
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
||||||
debug ptr => _9;
|
debug ptr => _9;
|
||||||
let mut _10: *const T;
|
let mut _10: *const T;
|
||||||
@ -139,7 +139,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
|
|||||||
StorageDead(_9);
|
StorageDead(_9);
|
||||||
StorageLive(_12);
|
StorageLive(_12);
|
||||||
_12 = _7;
|
_12 = _7;
|
||||||
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
||||||
StorageDead(_12);
|
StorageDead(_12);
|
||||||
StorageDead(_11);
|
StorageDead(_11);
|
||||||
StorageDead(_7);
|
StorageDead(_7);
|
||||||
|
@ -39,7 +39,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
|
|||||||
scope 6 {
|
scope 6 {
|
||||||
let _7: *const T;
|
let _7: *const T;
|
||||||
scope 7 {
|
scope 7 {
|
||||||
debug end => _7;
|
debug end_or_len => _7;
|
||||||
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
scope 13 (inlined NonNull::<T>::new_unchecked) {
|
||||||
debug ptr => _9;
|
debug ptr => _9;
|
||||||
let mut _10: *const T;
|
let mut _10: *const T;
|
||||||
@ -139,7 +139,7 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () {
|
|||||||
StorageDead(_9);
|
StorageDead(_9);
|
||||||
StorageLive(_12);
|
StorageLive(_12);
|
||||||
_12 = _7;
|
_12 = _7;
|
||||||
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
_13 = std::slice::Iter::<'_, T> { ptr: move _11, end_or_len: move _12, _marker: const ZeroSized: PhantomData<&T> };
|
||||||
StorageDead(_12);
|
StorageDead(_12);
|
||||||
StorageDead(_11);
|
StorageDead(_11);
|
||||||
StorageDead(_7);
|
StorageDead(_7);
|
||||||
|
Loading…
Reference in New Issue
Block a user