mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Auto merge of #131193 - EFanZh:asserts-vec-len, r=the8472
Asserts the maximum value that can be returned from `Vec::len` Currently, casting `Vec<i32>` to `Vec<u32>` takes O(1) time: ```rust // See <https://godbolt.org/z/hxq3hnYKG> for assembly output. pub fn cast(vec: Vec<i32>) -> Vec<u32> { vec.into_iter().map(|e| e as _).collect() } ``` But the generated assembly is not the same as the identity function, which prevents us from casting `Vec<Vec<i32>>` to `Vec<Vec<u32>>` within O(1) time: ```rust // See <https://godbolt.org/z/7n48bxd9f> for assembly output. pub fn cast(vec: Vec<Vec<i32>>) -> Vec<Vec<u32>> { vec.into_iter() .map(|e| e.into_iter().map(|e| e as _).collect()) .collect() } ``` This change tries to fix the problem. You can see the comparison here: <https://godbolt.org/z/jdManrKvx>.
This commit is contained in:
@@ -56,7 +56,6 @@
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::cmp;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::hash::{Hash, Hasher};
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
use core::iter;
|
||||
@@ -65,6 +64,7 @@
|
||||
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
|
||||
use core::ptr::{self, NonNull};
|
||||
use core::slice::{self, SliceIndex};
|
||||
use core::{fmt, intrinsics};
|
||||
|
||||
#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")]
|
||||
pub use self::extract_if::ExtractIf;
|
||||
@@ -2680,7 +2680,14 @@ pub fn clear(&mut self) {
|
||||
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
|
||||
#[rustc_confusables("length", "size")]
|
||||
pub const fn len(&self) -> usize {
|
||||
self.len
|
||||
let len = self.len;
|
||||
|
||||
// SAFETY: The maximum capacity of `Vec<T>` is `isize::MAX` bytes, so the maximum value can
|
||||
// be returned is `usize::checked_div(mem::size_of::<T>()).unwrap_or(usize::MAX)`, which
|
||||
// matches the definition of `T::MAX_SLICE_LEN`.
|
||||
unsafe { intrinsics::assume(len <= T::MAX_SLICE_LEN) };
|
||||
|
||||
len
|
||||
}
|
||||
|
||||
/// Returns `true` if the vector contains no elements.
|
||||
|
||||
@@ -1241,6 +1241,17 @@ pub trait SizedTypeProperties: Sized {
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "sized_type_properties", issue = "none")]
|
||||
const LAYOUT: Layout = Layout::new::<Self>();
|
||||
|
||||
/// The largest safe length for a `[Self]`.
|
||||
///
|
||||
/// Anything larger than this would make `size_of_val` overflow `isize::MAX`,
|
||||
/// which is never allowed for a single object.
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "sized_type_properties", issue = "none")]
|
||||
const MAX_SLICE_LEN: usize = match size_of::<Self>() {
|
||||
0 => usize::MAX,
|
||||
n => (isize::MAX as usize) / n,
|
||||
};
|
||||
}
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "sized_type_properties", issue = "none")]
|
||||
|
||||
@@ -36,6 +36,9 @@ pub struct Baz {
|
||||
// CHECK-LABEL: @vec_iterator_cast_primitive
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> {
|
||||
// CHECK-NOT: loop
|
||||
// CHECK: call
|
||||
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
|
||||
// CHECK-NOT: loop
|
||||
// CHECK-NOT: call
|
||||
vec.into_iter().map(|e| e as u8).collect()
|
||||
@@ -44,14 +47,37 @@ pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> {
|
||||
// CHECK-LABEL: @vec_iterator_cast_wrapper
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_wrapper(vec: Vec<u8>) -> Vec<Wrapper<u8>> {
|
||||
// CHECK-NOT: loop
|
||||
// CHECK: call
|
||||
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
|
||||
// CHECK-NOT: loop
|
||||
// CHECK-NOT: call
|
||||
vec.into_iter().map(|e| Wrapper(e)).collect()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @vec_iterator_cast_signed
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_signed(vec: Vec<i32>) -> Vec<u32> {
|
||||
// CHECK-NOT: and i{{[0-9]+}} %{{.*}}, {{[0-9]+}}
|
||||
vec.into_iter().map(|e| u32::from_ne_bytes(e.to_ne_bytes())).collect()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @vec_iterator_cast_signed_nested
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_signed_nested(vec: Vec<Vec<i32>>) -> Vec<Vec<u32>> {
|
||||
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
|
||||
// CHECK-NOT: %{{.*}} = udiv
|
||||
vec.into_iter()
|
||||
.map(|e| e.into_iter().map(|e| u32::from_ne_bytes(e.to_ne_bytes())).collect())
|
||||
.collect()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @vec_iterator_cast_unwrap
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
|
||||
// CHECK-NOT: loop
|
||||
// CHECK: call
|
||||
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
|
||||
// CHECK-NOT: loop
|
||||
// CHECK-NOT: call
|
||||
vec.into_iter().map(|e| e.0).collect()
|
||||
@@ -60,6 +86,9 @@ pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
|
||||
// CHECK-LABEL: @vec_iterator_cast_aggregate
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_aggregate(vec: Vec<[u64; 4]>) -> Vec<Foo> {
|
||||
// CHECK-NOT: loop
|
||||
// CHECK: call
|
||||
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
|
||||
// CHECK-NOT: loop
|
||||
// CHECK-NOT: call
|
||||
vec.into_iter().map(|e| unsafe { std::mem::transmute(e) }).collect()
|
||||
@@ -68,6 +97,9 @@ pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
|
||||
// CHECK-LABEL: @vec_iterator_cast_deaggregate_tra
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_deaggregate_tra(vec: Vec<Bar>) -> Vec<[u64; 4]> {
|
||||
// CHECK-NOT: loop
|
||||
// CHECK: call
|
||||
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
|
||||
// CHECK-NOT: loop
|
||||
// CHECK-NOT: call
|
||||
|
||||
@@ -81,6 +113,9 @@ pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
|
||||
// CHECK-LABEL: @vec_iterator_cast_deaggregate_fold
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_deaggregate_fold(vec: Vec<Baz>) -> Vec<[u64; 4]> {
|
||||
// CHECK-NOT: loop
|
||||
// CHECK: call
|
||||
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
|
||||
// CHECK-NOT: loop
|
||||
// CHECK-NOT: call
|
||||
|
||||
@@ -94,6 +129,11 @@ pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
|
||||
// CHECK-LABEL: @vec_iterator_cast_unwrap_drop
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_unwrap_drop(vec: Vec<Wrapper<String>>) -> Vec<String> {
|
||||
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
|
||||
// CHECK-NOT: %{{.*}} = mul
|
||||
// CHECK-NOT: %{{.*}} = udiv
|
||||
// CHECK: call
|
||||
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
|
||||
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
|
||||
// CHECK-NOT: call
|
||||
// CHECK-NOT: %{{.*}} = mul
|
||||
@@ -105,10 +145,16 @@ pub fn vec_iterator_cast_unwrap_drop(vec: Vec<Wrapper<String>>) -> Vec<String> {
|
||||
// CHECK-LABEL: @vec_iterator_cast_wrap_drop
|
||||
#[no_mangle]
|
||||
pub fn vec_iterator_cast_wrap_drop(vec: Vec<String>) -> Vec<Wrapper<String>> {
|
||||
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
|
||||
// CHECK-NOT: %{{.*}} = mul
|
||||
// CHECK-NOT: %{{.*}} = udiv
|
||||
// CHECK: call
|
||||
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
|
||||
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
|
||||
// CHECK-NOT: call
|
||||
// CHECK-NOT: %{{.*}} = mul
|
||||
// CHECK-NOT: %{{.*}} = udiv
|
||||
// CHECK: ret void
|
||||
|
||||
vec.into_iter().map(Wrapper).collect()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//@ revisions: llvm-pre-19 llvm-19
|
||||
//@ [llvm-19] min-llvm-version: 19
|
||||
//@ [llvm-pre-19] max-llvm-major-version: 18
|
||||
//@ compile-flags: -O
|
||||
|
||||
#![crate_type = "lib"]
|
||||
@@ -9,8 +12,11 @@ pub fn noop(v: &mut Vec<u8>) {
|
||||
// CHECK-NOT: call
|
||||
// CHECK: tail call void @llvm.assume
|
||||
// CHECK-NOT: grow_one
|
||||
// llvm-pre-19: call
|
||||
// llvm-pre-19-same: void @llvm.assume
|
||||
// llvm-pre-19-NOT: grow_one
|
||||
// CHECK-NOT: call
|
||||
// CHECK: ret
|
||||
// CHECK: {{ret|[}]}}
|
||||
if let Some(x) = v.pop() {
|
||||
v.push(x)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user