Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions compiler/rustc_arena/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
#![feature(dropck_eyepatch)]
#![feature(never_type)]
#![feature(rustc_attrs)]
#![feature(sized_type_properties)]
Copy link
Copy Markdown
Member

@ChrisDenton ChrisDenton Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than enabling this whole feature, would it be better for the compiler to have it's own IsZst trait specifically for IS_ZST?

View changes since the review

Copy link
Copy Markdown
Contributor Author

@Lars-Schumann Lars-Schumann Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sized_type_properties is a pretty small feature, it only gates the SizedTypeProperties trait which does nothing but define a few constants. I don't know what rustc's policy on avoiding unstable features is, if it is an issue I'm happy to make such a trait.

#![feature(unwrap_infallible)]
// tidy-alphabetical-end

use std::alloc::Layout;
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::mem::{self, MaybeUninit, SizedTypeProperties};
use std::ptr::{self, NonNull};
use std::{cmp, hint, slice};

Expand Down Expand Up @@ -87,7 +88,7 @@ impl<T> ArenaChunk<T> {
#[inline]
fn end(&mut self) -> *mut T {
unsafe {
if size_of::<T>() == 0 {
if T::IS_ZST {
// A pointer as large as possible for zero-sized elements.
ptr::without_provenance_mut(!0)
} else {
Expand Down Expand Up @@ -140,7 +141,7 @@ impl<T> TypedArena<T> {
/// Allocates an object in the `TypedArena`, returning a reference to it.
#[inline]
pub fn alloc(&self, object: T) -> &mut T {
assert!(size_of::<T>() != 0);
assert!(!T::IS_ZST);

if self.ptr == self.end {
self.grow(1)
Expand Down Expand Up @@ -181,7 +182,7 @@ impl<T> TypedArena<T> {
/// approach to arena-allocating slices of droppable values.
#[inline]
unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T {
assert!(size_of::<T>() != 0);
assert!(!T::IS_ZST);
assert!(len != 0);

// Ensure the current chunk can fit `len` objects.
Expand Down Expand Up @@ -227,7 +228,7 @@ impl<T> TypedArena<T> {
// So we collect all the elements beforehand, which takes care of reentrancy and panic
// safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it
// doesn't need to be hyper-optimized.
assert!(size_of::<T>() != 0);
assert!(!T::IS_ZST);

let vec: Result<SmallVec<[T; 8]>, E> = iter.into_iter().collect();
let mut vec = vec?;
Expand Down Expand Up @@ -296,7 +297,7 @@ impl<T> TypedArena<T> {
let end = self.ptr.get().addr();
// We then calculate the number of elements to be dropped in the last chunk,
// which is the filled area's length.
assert_ne!(size_of::<T>(), 0);
assert!(!T::IS_ZST);
// FIXME: this should *likely* use `offset_from`, but more
// investigation is needed (including running tests in miri).
let diff = (end - start) / size_of::<T>();
Expand Down Expand Up @@ -459,7 +460,7 @@ impl DroplessArena {
#[inline]
pub fn alloc<T>(&self, object: T) -> &mut T {
assert!(!mem::needs_drop::<T>());
assert!(size_of::<T>() != 0);
assert!(!T::IS_ZST);

let mem = self.alloc_raw(Layout::new::<T>()) as *mut T;

Expand All @@ -483,7 +484,7 @@ impl DroplessArena {
T: Copy,
{
assert!(!mem::needs_drop::<T>());
assert!(size_of::<T>() != 0);
assert!(!T::IS_ZST);
assert!(!slice.is_empty());

let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T;
Expand Down Expand Up @@ -545,7 +546,7 @@ impl DroplessArena {
// Warning: this function is reentrant: `iter` could hold a reference to `&self` and
// allocate additional elements while we're iterating.
let iter = iter.into_iter();
assert!(size_of::<T>() != 0);
assert!(!T::IS_ZST);
assert!(!mem::needs_drop::<T>());

let size_hint = iter.size_hint();
Expand Down Expand Up @@ -577,7 +578,7 @@ impl DroplessArena {
) -> Result<&mut [T], E> {
// Despite the similarity with `alloc_from_iter`, we cannot reuse their fast case, as we
// cannot know the minimum length of the iterator in this case.
assert!(size_of::<T>() != 0);
assert!(!T::IS_ZST);

// Takes care of reentrancy.
let vec: Result<SmallVec<[T; 8]>, E> = iter.into_iter().collect();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#![feature(range_bounds_is_empty)]
#![feature(rustc_attrs)]
#![feature(sized_hierarchy)]
#![feature(sized_type_properties)]
#![feature(trait_alias)]
#![feature(try_blocks)]
#![feature(try_trait_v2)]
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_middle/src/ty/list.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::alloc::Layout;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::mem::{self, SizedTypeProperties};
use std::ops::Deref;
use std::{fmt, iter, mem, ptr, slice};
use std::{fmt, iter, ptr, slice};

use rustc_data_structures::aligned::{Aligned, align_of};
use rustc_data_structures::sync::DynSync;
Expand Down Expand Up @@ -103,7 +104,7 @@ impl<H, T> RawList<H, T> {
T: Copy,
{
assert!(!mem::needs_drop::<T>());
assert!(size_of::<T>() != 0);
assert!(!T::IS_ZST);
assert!(!slice.is_empty());

let (layout, _offset) =
Expand Down
9 changes: 5 additions & 4 deletions library/alloc/src/boxed/thin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use core::marker::PhantomData;
#[cfg(not(no_global_oom_handling))]
use core::marker::Unsize;
#[cfg(not(no_global_oom_handling))]
use core::mem::{self, SizedTypeProperties};
use core::mem;
use core::mem::SizedTypeProperties;
use core::ops::{Deref, DerefMut};
use core::ptr::{self, NonNull, Pointee};

Expand Down Expand Up @@ -112,7 +113,7 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
where
T: Unsize<Dyn>,
{
if size_of::<T>() == 0 {
if T::IS_ZST {
let ptr = WithOpaqueHeader::new_unsize_zst::<Dyn, T>(value);
ThinBox { ptr, _marker: PhantomData }
} else {
Expand Down Expand Up @@ -281,7 +282,7 @@ impl<H> WithHeader<H> {
let ptr = if layout.size() == 0 {
// Some paranoia checking, mostly so that the ThinBox tests are
// more able to catch issues.
debug_assert!(value_offset == 0 && size_of::<T>() == 0 && size_of::<H>() == 0);
debug_assert!(value_offset == 0 && T::IS_ZST && H::IS_ZST);
layout.dangling_ptr()
} else {
let ptr = alloc::alloc(layout);
Expand Down Expand Up @@ -311,7 +312,7 @@ impl<H> WithHeader<H> {
Dyn: Pointee<Metadata = H> + ?Sized,
T: Unsize<Dyn>,
{
assert!(size_of::<T>() == 0);
assert!(T::IS_ZST);

const fn max(a: usize, b: usize) -> usize {
if a > b { a } else { b }
Expand Down
3 changes: 2 additions & 1 deletion library/compiler-builtins/compiler-builtins/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
#![feature(compiler_builtins)]
#![feature(core_intrinsics)]
#![feature(linkage)]
#![feature(repr_simd)]
#![feature(macro_metavar_expr_concat)]
#![feature(repr_simd)]
#![feature(rustc_attrs)]
#![feature(sized_type_properties)]
#![cfg_attr(f16_enabled, feature(f16))]
#![cfg_attr(f128_enabled, feature(f128))]
#![no_builtins]
Expand Down
5 changes: 3 additions & 2 deletions library/compiler-builtins/compiler-builtins/src/mem/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
// Note that ERMSB does not enhance the backwards (DF=1) "rep movsb".

use core::arch::asm;
use core::{intrinsics, mem};
use core::intrinsics;
use core::mem::{self, SizedTypeProperties};

#[inline(always)]
#[cfg(target_feature = "ermsb")]
Expand Down Expand Up @@ -135,7 +136,7 @@ pub unsafe fn compare_bytes(a: *const u8, b: *const u8, n: usize) -> i32 {
F: FnOnce(*const U, *const U, usize) -> i32,
{
// Ensure T is not a ZST.
const { assert!(mem::size_of::<T>() != 0) };
const { assert!(!T::IS_ZST) };
Comment on lines 137 to +139
Copy link
Copy Markdown
Contributor

@tgross35 tgross35 Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason for this change? The top post says clearer intent and possible perf improvement, but it has a comment and it's in const.

Copy link
Copy Markdown
Contributor Author

@Lars-Schumann Lars-Schumann Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to assure T is not a ZST, !T::IS_ZST is easier to read than mem::size_of::<T>() != 0) and the canonical way to make this check.
Regarding the comment, it seems to be redundant with this change, we could remove it.


let end = a.add(intrinsics::unchecked_div(n, mem::size_of::<T>()));
while a != end {
Expand Down
4 changes: 2 additions & 2 deletions library/core/src/iter/adapters/map_windows.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::iter::FusedIterator;
use crate::mem::MaybeUninit;
use crate::mem::{MaybeUninit, SizedTypeProperties};
use crate::{fmt, ptr};

/// An iterator over the mapped windows of another iterator.
Expand Down Expand Up @@ -47,7 +47,7 @@ impl<I: Iterator, F, const N: usize> MapWindows<I, F, N> {
assert!(N != 0, "array in `Iterator::map_windows` must contain more than 0 elements");

// Only ZST arrays' length can be so large.
if size_of::<I::Item>() == 0 {
if I::Item::IS_ZST {
assert!(
N.checked_mul(2).is_some(),
"array size of `Iterator::map_windows` is too large"
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1619,7 +1619,7 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
#[rustc_const_unstable(feature = "mem_conjure_zst", issue = "95383")]
pub const unsafe fn conjure_zst<T>() -> T {
const_assert!(
size_of::<T>() == 0,
T::IS_ZST,
"mem::conjure_zst invoked on a non-zero-sized type",
"mem::conjure_zst invoked on type {name}, which is not zero-sized",
name: &str = crate::any::type_name::<T>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ impl<T, const N: usize> SimdConstPtr for Simd<*const T, N> {
// SimdElement currently requires zero-sized metadata, so this should never fail.
// If this ever changes, `simd_cast_ptr` should produce a post-mono error.
use core::ptr::Pointee;
assert_eq!(size_of::<<T as Pointee>::Metadata>(), 0);
assert_eq!(size_of::<<U as Pointee>::Metadata>(), 0);
use core::mem::SizedTypeProperties;
assert!(<<T as Pointee>::Metadata>::IS_ZST);
assert!(<<U as Pointee>::Metadata>::IS_ZST);

// Safety: pointers can be cast
unsafe { core::intrinsics::simd::simd_cast_ptr(self) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ impl<T, const N: usize> SimdMutPtr for Simd<*mut T, N> {
// SimdElement currently requires zero-sized metadata, so this should never fail.
// If this ever changes, `simd_cast_ptr` should produce a post-mono error.
use core::ptr::Pointee;
assert_eq!(size_of::<<T as Pointee>::Metadata>(), 0);
assert_eq!(size_of::<<U as Pointee>::Metadata>(), 0);
use core::mem::SizedTypeProperties;
assert!(<<T as Pointee>::Metadata>::IS_ZST);
assert!(<<U as Pointee>::Metadata>::IS_ZST);

// Safety: pointers can be cast
unsafe { core::intrinsics::simd::simd_cast_ptr(self) }
Expand Down
4 changes: 2 additions & 2 deletions library/proc_macro/src/bridge/selfless_reify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
//! }
//! ```

use std::mem;
use std::mem::{self, SizedTypeProperties};

pub(super) const fn reify_to_extern_c_fn_hrt_bridge<
R,
Expand All @@ -47,7 +47,7 @@ pub(super) const fn reify_to_extern_c_fn_hrt_bridge<
// FIXME(eddyb) describe the `F` type (e.g. via `type_name::<F>`) once panic
// formatting becomes possible in `const fn`.
const {
assert!(size_of::<F>() == 0, "selfless_reify: closure must be zero-sized");
assert!(F::IS_ZST, "selfless_reify: closure must be zero-sized");
}
extern "C" fn wrapper<R, F: Fn(super::BridgeConfig<'_>) -> R + Copy>(
bridge: super::BridgeConfig<'_>,
Expand Down
9 changes: 5 additions & 4 deletions library/proc_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]
#![doc(rust_logo)]
#![feature(rustdoc_internals)]
#![feature(staged_api)]
#![feature(allow_internal_unstable)]
#![feature(decl_macro)]
#![feature(extend_one)]
#![feature(mem_conjure_zst)]
#![feature(negative_impls)]
#![feature(panic_can_unwind)]
#![feature(restricted_std)]
#![feature(rustc_attrs)]
#![feature(extend_one)]
#![feature(mem_conjure_zst)]
#![feature(rustdoc_internals)]
#![feature(sized_type_properties)]
#![feature(staged_api)]
#![recursion_limit = "256"]
#![allow(internal_features)]
#![deny(ffi_unwind_calls)]
Expand Down
Loading