From 502df1b7d4f3ba61d55cf2b3a47785a7a4df940b Mon Sep 17 00:00:00 2001 From: The 8472 Date: Thu, 12 Jan 2023 22:54:27 +0100 Subject: [PATCH 1/4] add more niches to rawvec --- library/alloc/src/raw_vec.rs | 51 +++++++++++++++------- library/alloc/src/raw_vec/tests.rs | 9 ++++ tests/codegen/issues/issue-86106.rs | 28 ++++++++---- tests/ui/hygiene/panic-location.run.stderr | 2 +- 4 files changed, 66 insertions(+), 24 deletions(-) diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 817b93720ce28..52b2948060f54 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -25,6 +25,16 @@ enum AllocInit { Zeroed, } +#[repr(transparent)] +#[cfg_attr(target_pointer_width = "16", rustc_layout_scalar_valid_range_end(0x7fff))] +#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0x7fff_ffff))] +#[cfg_attr(target_pointer_width = "64", rustc_layout_scalar_valid_range_end(0x7fff_ffff_ffff_ffff))] +struct Cap(usize); + +impl Cap { + const ZERO: Cap = unsafe { Cap(0) }; +} + /// A low-level utility for more ergonomically allocating, reallocating, and deallocating /// a buffer of memory on the heap without having to worry about all the corner cases /// involved. This type is excellent for building your own data structures like Vec and VecDeque. @@ -50,7 +60,7 @@ enum AllocInit { #[allow(missing_debug_implementations)] pub(crate) struct RawVec { ptr: Unique, - cap: usize, + cap: Cap, alloc: A, } @@ -119,7 +129,7 @@ impl RawVec { /// the returned `RawVec`. pub const fn new_in(alloc: A) -> Self { // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr: Unique::dangling(), cap: 0, alloc } + Self { ptr: Unique::dangling(), cap: Cap::ZERO, alloc } } /// Like `with_capacity`, but parameterized over the choice of @@ -194,7 +204,7 @@ impl RawVec { // here should change to `ptr.len() / mem::size_of::()`. Self { ptr: unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) }, - cap: capacity, + cap: unsafe { Cap(capacity) }, alloc, } } @@ -207,12 +217,13 @@ impl RawVec { /// The `ptr` must be allocated (via the given allocator `alloc`), and with the given /// `capacity`. /// The `capacity` cannot exceed `isize::MAX` for sized types. (only a concern on 32-bit - /// systems). ZST vectors may have a capacity up to `usize::MAX`. + /// systems). For ZSTs capacity is ignored. /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is /// guaranteed. #[inline] pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { - Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap: capacity, alloc } + let cap = if T::IS_ZST { Cap::ZERO } else { unsafe { Cap(capacity) } }; + Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } } /// Gets a raw pointer to the start of the allocation. Note that this is @@ -228,7 +239,7 @@ impl RawVec { /// This will always be `usize::MAX` if `T` is zero-sized. #[inline(always)] pub fn capacity(&self) -> usize { - if T::IS_ZST { usize::MAX } else { self.cap } + if T::IS_ZST { usize::MAX } else { self.cap.0 } } /// Returns a shared reference to the allocator backing this `RawVec`. @@ -237,7 +248,7 @@ impl RawVec { } fn current_memory(&self) -> Option<(NonNull, Layout)> { - if T::IS_ZST || self.cap == 0 { + if T::IS_ZST || self.cap.0 == 0 { None } else { // We could use Layout::array here which ensures the absence of isize and usize overflows @@ -247,7 +258,7 @@ impl RawVec { let _: () = const { assert!(mem::size_of::() % mem::align_of::() == 0) }; unsafe { let align = mem::align_of::(); - let size = mem::size_of::().unchecked_mul(self.cap); + let size = mem::size_of::().unchecked_mul(self.cap.0); let layout = Layout::from_size_align_unchecked(size, align); Some((self.ptr.cast().into(), layout)) } @@ -375,12 +386,15 @@ impl RawVec { additional > self.capacity().wrapping_sub(len) } - fn set_ptr_and_cap(&mut self, ptr: NonNull<[u8]>, cap: usize) { + /// # Safety: + /// + /// `cap` must not exceed `isize::MAX`. + unsafe fn set_ptr_and_cap(&mut self, ptr: NonNull<[u8]>, cap: usize) { // Allocators currently return a `NonNull<[u8]>` whose length matches // the size requested. If that ever changes, the capacity here should // change to `ptr.len() / mem::size_of::()`. self.ptr = unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) }; - self.cap = cap; + self.cap = unsafe { Cap(cap) }; } // This method is usually instantiated many times. So we want it to be as @@ -405,14 +419,15 @@ impl RawVec { // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. - let cap = cmp::max(self.cap * 2, required_cap); + let cap = cmp::max(self.cap.0 * 2, required_cap); let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap); let new_layout = Layout::array::(cap); // `finish_grow` is non-generic over `T`. let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; - self.set_ptr_and_cap(ptr, cap); + // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than isize::MAX items + unsafe { self.set_ptr_and_cap(ptr, cap) }; Ok(()) } @@ -431,7 +446,10 @@ impl RawVec { // `finish_grow` is non-generic over `T`. let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; - self.set_ptr_and_cap(ptr, cap); + // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than isize::MAX items + unsafe { + self.set_ptr_and_cap(ptr, cap); + } Ok(()) } @@ -449,7 +467,7 @@ impl RawVec { if cap == 0 { unsafe { self.alloc.deallocate(ptr, layout) }; self.ptr = Unique::dangling(); - self.cap = 0; + self.cap = Cap::ZERO; } else { let ptr = unsafe { // `Layout::array` cannot overflow here because it would have @@ -460,7 +478,10 @@ impl RawVec { .shrink(ptr, layout, new_layout) .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })? }; - self.set_ptr_and_cap(ptr, cap); + // SAFETY: if the allocation is valid, then the capacity is too + unsafe { + self.set_ptr_and_cap(ptr, cap); + } } Ok(()) } diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index ff322f0da97c6..f8cada01c0309 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -1,4 +1,5 @@ use super::*; +use core::mem::size_of; use std::cell::Cell; #[test] @@ -161,3 +162,11 @@ fn zst_reserve_exact_panic() { v.reserve_exact(101, usize::MAX - 100); } + +#[test] +fn niches() { + let baseline = size_of::>(); + assert_eq!(size_of::>>(), baseline); + assert_eq!(size_of::>>>(), baseline); + assert_eq!(size_of::>>>>(), baseline); +} diff --git a/tests/codegen/issues/issue-86106.rs b/tests/codegen/issues/issue-86106.rs index 15aef344ac0c9..5f71d46fb2023 100644 --- a/tests/codegen/issues/issue-86106.rs +++ b/tests/codegen/issues/issue-86106.rs @@ -9,9 +9,12 @@ // CHECK-LABEL: define {{(dso_local )?}}void @string_new #[no_mangle] pub fn string_new() -> String { - // CHECK: store ptr inttoptr + // CHECK-NOT: load i8 + // CHECK: store i{{32|64}} // CHECK-NEXT: getelementptr - // CHECK-NEXT: call void @llvm.memset + // CHECK-NEXT: store ptr + // CHECK-NEXT: getelementptr + // CHECK-NEXT: store i{{32|64}} // CHECK-NEXT: ret void String::new() } @@ -19,9 +22,12 @@ pub fn string_new() -> String { // CHECK-LABEL: define {{(dso_local )?}}void @empty_to_string #[no_mangle] pub fn empty_to_string() -> String { - // CHECK: store ptr inttoptr + // CHECK-NOT: load i8 + // CHECK: store i{{32|64}} + // CHECK-NEXT: getelementptr + // CHECK-NEXT: store ptr // CHECK-NEXT: getelementptr - // CHECK-NEXT: call void @llvm.memset + // CHECK-NEXT: store i{{32|64}} // CHECK-NEXT: ret void "".to_string() } @@ -32,9 +38,12 @@ pub fn empty_to_string() -> String { // CHECK-LABEL: @empty_vec #[no_mangle] pub fn empty_vec() -> Vec { - // CHECK: store ptr inttoptr + // CHECK: store i{{32|64}} + // CHECK-NOT: load i8 // CHECK-NEXT: getelementptr - // CHECK-NEXT: call void @llvm.memset + // CHECK-NEXT: store ptr + // CHECK-NEXT: getelementptr + // CHECK-NEXT: store i{{32|64}} // CHECK-NEXT: ret void vec![] } @@ -42,9 +51,12 @@ pub fn empty_vec() -> Vec { // CHECK-LABEL: @empty_vec_clone #[no_mangle] pub fn empty_vec_clone() -> Vec { - // CHECK: store ptr inttoptr + // CHECK: store i{{32|64}} + // CHECK-NOT: load i8 + // CHECK-NEXT: getelementptr + // CHECK-NEXT: store ptr // CHECK-NEXT: getelementptr - // CHECK-NEXT: call void @llvm.memset + // CHECK-NEXT: store i{{32|64}} // CHECK-NEXT: ret void vec![].clone() } diff --git a/tests/ui/hygiene/panic-location.run.stderr b/tests/ui/hygiene/panic-location.run.stderr index e0dc13c0c95c8..5c552411da7f3 100644 --- a/tests/ui/hygiene/panic-location.run.stderr +++ b/tests/ui/hygiene/panic-location.run.stderr @@ -1,3 +1,3 @@ -thread 'main' panicked at library/alloc/src/raw_vec.rs:545:5: +thread 'main' panicked at library/alloc/src/raw_vec.rs:571:5: capacity overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace From 6a2f44e9d850d241e9483792710dba7c58a249ec Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sat, 29 Apr 2023 21:47:24 +0200 Subject: [PATCH 2/4] add comment to RawVec::cap field --- library/alloc/src/raw_vec.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 52b2948060f54..99ec68f5aa53d 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -60,6 +60,11 @@ impl Cap { #[allow(missing_debug_implementations)] pub(crate) struct RawVec { ptr: Unique, + /// Never used for ZSTs; it's `capacity()`'s responsibility to return usize::MAX in that case. + /// + /// # Safety + /// + /// `cap` must be in the `0..=isize::MAX` range. cap: Cap, alloc: A, } From 81997094e6e38da15a8939c7a8ab8aae2e00e09a Mon Sep 17 00:00:00 2001 From: The 8472 Date: Mon, 11 Dec 2023 01:08:30 +0100 Subject: [PATCH 3/4] update debug providers to match new RawVec capacity field --- src/etc/gdb_providers.py | 6 +++++- src/etc/lldb_providers.py | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index e851aa6263446..7d7277d240877 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -154,7 +154,11 @@ def __init__(self, valobj): self._valobj = valobj self._head = int(valobj["head"]) self._size = int(valobj["len"]) - self._cap = int(valobj["buf"]["cap"]) + # BACKCOMPAT: rust 1.75 + cap = valobj["buf"]["cap"] + if cap.type.code != gdb.TYPE_CODE_INT: + cap = cap[ZERO_FIELD] + self._cap = int(cap) self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) def to_string(self): diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 4c86b2146463d..cfb3f0a4eaebe 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -267,7 +267,8 @@ class StdVecSyntheticProvider: """Pretty-printer for alloc::vec::Vec struct Vec { buf: RawVec, len: usize } - struct RawVec { ptr: Unique, cap: usize, ... } + rust 1.75: struct RawVec { ptr: Unique, cap: usize, ... } + rust 1.76: struct RawVec { ptr: Unique, cap: Cap(usize), ... } rust 1.31.1: struct Unique { pointer: NonZero<*const T>, ... } rust 1.33.0: struct Unique { pointer: *const T, ... } rust 1.62.0: struct Unique { pointer: NonNull, ... } @@ -390,7 +391,10 @@ def update(self): self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned() self.size = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() self.buf = self.valobj.GetChildMemberWithName("buf") - self.cap = self.buf.GetChildMemberWithName("cap").GetValueAsUnsigned() + cap = self.buf.GetChildMemberWithName("cap") + if cap.GetType().num_fields == 1: + cap = cap.GetChildAtIndex(0) + self.cap = cap.GetValueAsUnsigned() self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr")) From f6150db78ff066db1dd2840765cff690e48e6b29 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Tue, 19 Dec 2023 22:16:58 +0100 Subject: [PATCH 4/4] update natvis to match changed RawVec structure --- src/etc/natvis/liballoc.natvis | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index 00c17d83322a8..da307809f7b04 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -4,7 +4,7 @@ {{ len={len} }} len - buf.cap + buf.cap.__0 len buf.ptr.pointer.pointer @@ -15,7 +15,7 @@ {{ len={len} }} len - buf.cap + buf.cap.__0 len @@ -23,7 +23,7 @@ - buf.ptr.pointer.pointer[(i + head) % buf.cap] + buf.ptr.pointer.pointer[(i + head) % buf.cap.__0] i = i + 1 @@ -45,7 +45,7 @@ (char*)vec.buf.ptr.pointer.pointer,[vec.len]s8 vec.len - vec.buf.cap + vec.buf.cap.__0 {(char*)vec.buf.ptr.pointer.pointer,[vec.len]s8}