Skip to content

Commit e58621a

Browse files
committed
Auto merge of #102169 - scottmcm:constify-some-conditions, r=thomcc
Make ZST checks in core/alloc more readable There's a bunch of these checks because of special handing for ZSTs in various unsafe implementations of stuff. This lets them be `T::IS_ZST` instead of `mem::size_of::<T>() == 0` every time, making them both more readable and more terse. *Not* proposed for stabilization. Would be `pub(crate)` except `alloc` wants to use it too. (And while it doesn't matter now, if we ever get something like #85836 making it a const can help codegen be simpler.)
2 parents d0ece44 + ed16dbf commit e58621a

File tree

14 files changed

+94
-54
lines changed

14 files changed

+94
-54
lines changed

library/alloc/src/collections/vec_deque/mod.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ use core::fmt;
1212
use core::hash::{Hash, Hasher};
1313
use core::iter::{repeat_with, FromIterator};
1414
use core::marker::PhantomData;
15-
use core::mem::{self, ManuallyDrop, MaybeUninit};
15+
use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties};
1616
use core::ops::{Index, IndexMut, Range, RangeBounds};
1717
use core::ptr::{self, NonNull};
1818
use core::slice;
1919

20+
// This is used in a bunch of intra-doc links.
21+
// FIXME: For some reason, `#[cfg(doc)]` wasn't sufficient, resulting in
22+
// failures in linkchecker even though rustdoc built the docs just fine.
23+
#[allow(unused_imports)]
24+
use core::mem;
25+
2026
use crate::alloc::{Allocator, Global};
2127
use crate::collections::TryReserveError;
2228
use crate::collections::TryReserveErrorKind;
@@ -177,7 +183,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
177183
/// Marginally more convenient
178184
#[inline]
179185
fn cap(&self) -> usize {
180-
if mem::size_of::<T>() == 0 {
186+
if T::IS_ZST {
181187
// For zero sized types, we are always at maximum capacity
182188
MAXIMUM_ZST_CAPACITY
183189
} else {
@@ -3038,7 +3044,7 @@ impl<T, A: Allocator> From<Vec<T, A>> for VecDeque<T, A> {
30383044
/// `Vec<T>` came from `From<VecDeque<T>>` and hasn't been reallocated.
30393045
fn from(mut other: Vec<T, A>) -> Self {
30403046
let len = other.len();
3041-
if mem::size_of::<T>() == 0 {
3047+
if T::IS_ZST {
30423048
// There's no actual allocation for ZSTs to worry about capacity,
30433049
// but `VecDeque` can't handle as much length as `Vec`.
30443050
assert!(len < MAXIMUM_ZST_CAPACITY, "capacity overflow");
@@ -3124,7 +3130,7 @@ impl<T, const N: usize> From<[T; N]> for VecDeque<T> {
31243130
fn from(arr: [T; N]) -> Self {
31253131
let mut deq = VecDeque::with_capacity(N);
31263132
let arr = ManuallyDrop::new(arr);
3127-
if mem::size_of::<T>() != 0 {
3133+
if !<T>::IS_ZST {
31283134
// SAFETY: VecDeque::with_capacity ensures that there is enough capacity.
31293135
unsafe {
31303136
ptr::copy_nonoverlapping(arr.as_ptr(), deq.ptr(), N);

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
#![feature(receiver_trait)]
140140
#![feature(saturating_int_impl)]
141141
#![feature(set_ptr_value)]
142+
#![feature(sized_type_properties)]
142143
#![feature(slice_from_ptr_range)]
143144
#![feature(slice_group_by)]
144145
#![feature(slice_ptr_get)]

library/alloc/src/raw_vec.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use core::alloc::LayoutError;
44
use core::cmp;
55
use core::intrinsics;
6-
use core::mem::{self, ManuallyDrop, MaybeUninit};
6+
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
77
use core::ops::Drop;
88
use core::ptr::{self, NonNull, Unique};
99
use core::slice;
@@ -168,7 +168,7 @@ impl<T, A: Allocator> RawVec<T, A> {
168168
#[cfg(not(no_global_oom_handling))]
169169
fn allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Self {
170170
// Don't allocate here because `Drop` will not deallocate when `capacity` is 0.
171-
if mem::size_of::<T>() == 0 || capacity == 0 {
171+
if T::IS_ZST || capacity == 0 {
172172
Self::new_in(alloc)
173173
} else {
174174
// We avoid `unwrap_or_else` here because it bloats the amount of
@@ -229,7 +229,7 @@ impl<T, A: Allocator> RawVec<T, A> {
229229
/// This will always be `usize::MAX` if `T` is zero-sized.
230230
#[inline(always)]
231231
pub fn capacity(&self) -> usize {
232-
if mem::size_of::<T>() == 0 { usize::MAX } else { self.cap }
232+
if T::IS_ZST { usize::MAX } else { self.cap }
233233
}
234234

235235
/// Returns a shared reference to the allocator backing this `RawVec`.
@@ -238,7 +238,7 @@ impl<T, A: Allocator> RawVec<T, A> {
238238
}
239239

240240
fn current_memory(&self) -> Option<(NonNull<u8>, Layout)> {
241-
if mem::size_of::<T>() == 0 || self.cap == 0 {
241+
if T::IS_ZST || self.cap == 0 {
242242
None
243243
} else {
244244
// We have an allocated chunk of memory, so we can bypass runtime
@@ -380,7 +380,7 @@ impl<T, A: Allocator> RawVec<T, A> {
380380
// This is ensured by the calling contexts.
381381
debug_assert!(additional > 0);
382382

383-
if mem::size_of::<T>() == 0 {
383+
if T::IS_ZST {
384384
// Since we return a capacity of `usize::MAX` when `elem_size` is
385385
// 0, getting to here necessarily means the `RawVec` is overfull.
386386
return Err(CapacityOverflow.into());
@@ -406,7 +406,7 @@ impl<T, A: Allocator> RawVec<T, A> {
406406
// `grow_amortized`, but this method is usually instantiated less often so
407407
// it's less critical.
408408
fn grow_exact(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> {
409-
if mem::size_of::<T>() == 0 {
409+
if T::IS_ZST {
410410
// Since we return a capacity of `usize::MAX` when the type size is
411411
// 0, getting to here necessarily means the `RawVec` is overfull.
412412
return Err(CapacityOverflow.into());

library/alloc/src/slice.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ use core::borrow::{Borrow, BorrowMut};
1616
#[cfg(not(no_global_oom_handling))]
1717
use core::cmp::Ordering::{self, Less};
1818
#[cfg(not(no_global_oom_handling))]
19-
use core::mem;
20-
#[cfg(not(no_global_oom_handling))]
21-
use core::mem::size_of;
19+
use core::mem::{self, SizedTypeProperties};
2220
#[cfg(not(no_global_oom_handling))]
2321
use core::ptr;
2422

@@ -1018,7 +1016,7 @@ where
10181016
const MIN_RUN: usize = 10;
10191017

10201018
// Sorting has no meaningful behavior on zero-sized types.
1021-
if size_of::<T>() == 0 {
1019+
if T::IS_ZST {
10221020
return;
10231021
}
10241022

library/alloc/src/vec/drain.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::alloc::{Allocator, Global};
22
use core::fmt;
33
use core::iter::{FusedIterator, TrustedLen};
4-
use core::mem::{self, ManuallyDrop};
4+
use core::mem::{self, ManuallyDrop, SizedTypeProperties};
55
use core::ptr::{self, NonNull};
66
use core::slice::{self};
77

@@ -202,7 +202,7 @@ impl<T, A: Allocator> Drop for Drain<'_, T, A> {
202202

203203
let mut vec = self.vec;
204204

205-
if mem::size_of::<T>() == 0 {
205+
if T::IS_ZST {
206206
// ZSTs have no identity, so we don't need to move them around, we only need to drop the correct amount.
207207
// this can be achieved by manipulating the Vec length instead of moving values out from `iter`.
208208
unsafe {

library/alloc/src/vec/in_place_collect.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
//! vec.truncate(write_idx);
136136
//! ```
137137
use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce};
138-
use core::mem::{self, ManuallyDrop};
138+
use core::mem::{self, ManuallyDrop, SizedTypeProperties};
139139
use core::ptr::{self};
140140

141141
use super::{InPlaceDrop, SpecFromIter, SpecFromIterNested, Vec};
@@ -154,7 +154,7 @@ where
154154
default fn from_iter(mut iterator: I) -> Self {
155155
// See "Layout constraints" section in the module documentation. We rely on const
156156
// optimization here since these conditions currently cannot be expressed as trait bounds
157-
if mem::size_of::<T>() == 0
157+
if T::IS_ZST
158158
|| mem::size_of::<T>()
159159
!= mem::size_of::<<<I as SourceIter>::Source as AsVecIntoIter>::Item>()
160160
|| mem::align_of::<T>()

library/alloc/src/vec/into_iter.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use core::iter::{
88
FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccessNoCoerce,
99
};
1010
use core::marker::PhantomData;
11-
use core::mem::{self, ManuallyDrop, MaybeUninit};
11+
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
1212
#[cfg(not(no_global_oom_handling))]
1313
use core::ops::Deref;
1414
use core::ptr::{self, NonNull};
@@ -149,7 +149,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
149149
fn next(&mut self) -> Option<T> {
150150
if self.ptr == self.end {
151151
None
152-
} else if mem::size_of::<T>() == 0 {
152+
} else if T::IS_ZST {
153153
// purposefully don't use 'ptr.offset' because for
154154
// vectors with 0-size elements this would return the
155155
// same pointer.
@@ -167,7 +167,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
167167

168168
#[inline]
169169
fn size_hint(&self) -> (usize, Option<usize>) {
170-
let exact = if mem::size_of::<T>() == 0 {
170+
let exact = if T::IS_ZST {
171171
self.end.addr().wrapping_sub(self.ptr.addr())
172172
} else {
173173
unsafe { self.end.sub_ptr(self.ptr) }
@@ -179,7 +179,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
179179
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
180180
let step_size = self.len().min(n);
181181
let to_drop = ptr::slice_from_raw_parts_mut(self.ptr as *mut T, step_size);
182-
if mem::size_of::<T>() == 0 {
182+
if T::IS_ZST {
183183
// SAFETY: due to unchecked casts of unsigned amounts to signed offsets the wraparound
184184
// effectively results in unsigned pointers representing positions 0..usize::MAX,
185185
// which is valid for ZSTs.
@@ -209,7 +209,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
209209

210210
let len = self.len();
211211

212-
if mem::size_of::<T>() == 0 {
212+
if T::IS_ZST {
213213
if len < N {
214214
self.forget_remaining_elements();
215215
// Safety: ZSTs can be conjured ex nihilo, only the amount has to be correct
@@ -253,7 +253,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
253253
// that `T: Copy` so reading elements from the buffer doesn't invalidate
254254
// them for `Drop`.
255255
unsafe {
256-
if mem::size_of::<T>() == 0 { mem::zeroed() } else { ptr::read(self.ptr.add(i)) }
256+
if T::IS_ZST { mem::zeroed() } else { ptr::read(self.ptr.add(i)) }
257257
}
258258
}
259259
}
@@ -264,7 +264,7 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
264264
fn next_back(&mut self) -> Option<T> {
265265
if self.end == self.ptr {
266266
None
267-
} else if mem::size_of::<T>() == 0 {
267+
} else if T::IS_ZST {
268268
// See above for why 'ptr.offset' isn't used
269269
self.end = self.end.wrapping_byte_sub(1);
270270

@@ -280,7 +280,7 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
280280
#[inline]
281281
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
282282
let step_size = self.len().min(n);
283-
if mem::size_of::<T>() == 0 {
283+
if T::IS_ZST {
284284
// SAFETY: same as for advance_by()
285285
self.end = self.end.wrapping_byte_sub(step_size);
286286
} else {

library/alloc/src/vec/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ use core::iter;
6464
#[cfg(not(no_global_oom_handling))]
6565
use core::iter::FromIterator;
6666
use core::marker::PhantomData;
67-
use core::mem::{self, ManuallyDrop, MaybeUninit};
67+
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
6868
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
6969
use core::ptr::{self, NonNull};
7070
use core::slice::{self, SliceIndex};
@@ -2347,7 +2347,7 @@ impl<T, A: Allocator, const N: usize> Vec<[T; N], A> {
23472347
#[unstable(feature = "slice_flatten", issue = "95629")]
23482348
pub fn into_flattened(self) -> Vec<T, A> {
23492349
let (ptr, len, cap, alloc) = self.into_raw_parts_with_alloc();
2350-
let (new_len, new_cap) = if mem::size_of::<T>() == 0 {
2350+
let (new_len, new_cap) = if T::IS_ZST {
23512351
(len.checked_mul(N).expect("vec len overflow"), usize::MAX)
23522352
} else {
23532353
// SAFETY:
@@ -2677,7 +2677,7 @@ impl<T, A: Allocator> IntoIterator for Vec<T, A> {
26772677
let mut me = ManuallyDrop::new(self);
26782678
let alloc = ManuallyDrop::new(ptr::read(me.allocator()));
26792679
let begin = me.as_mut_ptr();
2680-
let end = if mem::size_of::<T>() == 0 {
2680+
let end = if T::IS_ZST {
26812681
begin.wrapping_byte_add(me.len())
26822682
} else {
26832683
begin.add(me.len()) as *const T

library/core/src/mem/mod.rs

+41
Original file line numberDiff line numberDiff line change
@@ -1178,3 +1178,44 @@ pub const fn discriminant<T>(v: &T) -> Discriminant<T> {
11781178
pub const fn variant_count<T>() -> usize {
11791179
intrinsics::variant_count::<T>()
11801180
}
1181+
1182+
/// Provides associated constants for various useful properties of types,
1183+
/// to give them a canonical form in our code and make them easier to read.
1184+
///
1185+
/// This is here only to simplify all the ZST checks we need in the library.
1186+
/// It's not on a stabilization track right now.
1187+
#[doc(hidden)]
1188+
#[unstable(feature = "sized_type_properties", issue = "none")]
1189+
pub trait SizedTypeProperties: Sized {
1190+
/// `true` if this type requires no storage.
1191+
/// `false` if its [size](size_of) is greater than zero.
1192+
///
1193+
/// # Examples
1194+
///
1195+
/// ```
1196+
/// #![feature(sized_type_properties)]
1197+
/// use core::mem::SizedTypeProperties;
1198+
///
1199+
/// fn do_something_with<T>() {
1200+
/// if T::IS_ZST {
1201+
/// // ... special approach ...
1202+
/// } else {
1203+
/// // ... the normal thing ...
1204+
/// }
1205+
/// }
1206+
///
1207+
/// struct MyUnit;
1208+
/// assert!(MyUnit::IS_ZST);
1209+
///
1210+
/// // For negative checks, consider using UFCS to emphasize the negation
1211+
/// assert!(!<i32>::IS_ZST);
1212+
/// // As it can sometimes hide in the type otherwise
1213+
/// assert!(!String::IS_ZST);
1214+
/// ```
1215+
#[doc(hidden)]
1216+
#[unstable(feature = "sized_type_properties", issue = "none")]
1217+
const IS_ZST: bool = size_of::<Self>() == 0;
1218+
}
1219+
#[doc(hidden)]
1220+
#[unstable(feature = "sized_type_properties", issue = "none")]
1221+
impl<T> SizedTypeProperties for T {}

library/core/src/slice/iter.rs

+5-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::fmt;
99
use crate::intrinsics::{assume, exact_div, unchecked_sub};
1010
use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
1111
use crate::marker::{PhantomData, Send, Sized, Sync};
12-
use crate::mem;
12+
use crate::mem::{self, SizedTypeProperties};
1313
use crate::num::NonZeroUsize;
1414
use crate::ptr::NonNull;
1515

@@ -91,11 +91,8 @@ impl<'a, T> Iter<'a, T> {
9191
unsafe {
9292
assume(!ptr.is_null());
9393

94-
let end = if mem::size_of::<T>() == 0 {
95-
ptr.wrapping_byte_add(slice.len())
96-
} else {
97-
ptr.add(slice.len())
98-
};
94+
let end =
95+
if T::IS_ZST { ptr.wrapping_byte_add(slice.len()) } else { ptr.add(slice.len()) };
9996

10097
Self { ptr: NonNull::new_unchecked(ptr as *mut T), end, _marker: PhantomData }
10198
}
@@ -227,11 +224,8 @@ impl<'a, T> IterMut<'a, T> {
227224
unsafe {
228225
assume(!ptr.is_null());
229226

230-
let end = if mem::size_of::<T>() == 0 {
231-
ptr.wrapping_byte_add(slice.len())
232-
} else {
233-
ptr.add(slice.len())
234-
};
227+
let end =
228+
if T::IS_ZST { ptr.wrapping_byte_add(slice.len()) } else { ptr.add(slice.len()) };
235229

236230
Self { ptr: NonNull::new_unchecked(ptr), end, _marker: PhantomData }
237231
}

library/core/src/slice/iter/macros.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ macro_rules! iterator {
100100
// Unsafe because the offset must not exceed `self.len()`.
101101
#[inline(always)]
102102
unsafe fn pre_dec_end(&mut self, offset: usize) -> * $raw_mut T {
103-
if mem::size_of::<T>() == 0 {
103+
if T::IS_ZST {
104104
zst_shrink!(self, offset);
105105
self.ptr.as_ptr()
106106
} else {
@@ -140,7 +140,7 @@ macro_rules! iterator {
140140
// since we check if the iterator is empty first.
141141
unsafe {
142142
assume(!self.ptr.as_ptr().is_null());
143-
if mem::size_of::<T>() != 0 {
143+
if !<T>::IS_ZST {
144144
assume(!self.end.is_null());
145145
}
146146
if is_empty!(self) {
@@ -166,7 +166,7 @@ macro_rules! iterator {
166166
fn nth(&mut self, n: usize) -> Option<$elem> {
167167
if n >= len!(self) {
168168
// This iterator is now empty.
169-
if mem::size_of::<T>() == 0 {
169+
if T::IS_ZST {
170170
// We have to do it this way as `ptr` may never be 0, but `end`
171171
// could be (due to wrapping).
172172
self.end = self.ptr.as_ptr();
@@ -355,7 +355,7 @@ macro_rules! iterator {
355355
// empty first.
356356
unsafe {
357357
assume(!self.ptr.as_ptr().is_null());
358-
if mem::size_of::<T>() != 0 {
358+
if !<T>::IS_ZST {
359359
assume(!self.end.is_null());
360360
}
361361
if is_empty!(self) {

0 commit comments

Comments
 (0)