about summary refs log tree commit diff
diff options
context:
space:
mode:
authorScott McMurray <scottmcm@users.noreply.github.com>2025-01-07 19:35:37 -0800
committerScott McMurray <scottmcm@users.noreply.github.com>2025-01-09 23:47:11 -0800
commit6f2a78345ee7f55c5ce33fd7b9804c665c646dd2 (patch)
tree733a3be2e42e4cf70a5194c0fbfcb370e7ae7045
parent88ab2d8acb342a36324103b6f95ec1335db27d7d (diff)
downloadrust-6f2a78345ee7f55c5ce33fd7b9804c665c646dd2.tar.gz
rust-6f2a78345ee7f55c5ce33fd7b9804c665c646dd2.zip
Update a bunch of library types for MCP807
This greatly reduces the number of places that actually use the `rustc_layout_scalar_valid_range_*` attributes down to just 3:
```
library/core\src\ptr\non_null.rs
68:#[rustc_layout_scalar_valid_range_start(1)]

library/core\src\num\niche_types.rs
19:        #[rustc_layout_scalar_valid_range_start($low)]
20:        #[rustc_layout_scalar_valid_range_end($high)]
```

Everything else -- PAL Nanoseconds, alloc's `Cap`, niched FDs, etc -- all just wrap those `niche_types` types.
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/src/raw_vec.rs46
-rw-r--r--library/core/src/num/mod.rs4
-rw-r--r--library/core/src/num/niche_types.rs162
-rw-r--r--library/core/src/num/nonzero.rs31
-rw-r--r--library/core/src/time.rs96
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--library/std/src/os/fd/owned.rs34
-rw-r--r--library/std/src/os/solid/io.rs27
-rw-r--r--library/std/src/os/windows/io/socket.rs39
-rw-r--r--library/std/src/sys/pal/solid/fs.rs14
-rw-r--r--library/std/src/sys/pal/unix/time.rs33
-rw-r--r--tests/codegen/vec-in-place.rs54
-rw-r--r--tests/codegen/vec_pop_push_noop.rs3
-rw-r--r--tests/debuginfo/strings-and-strs.rs2
-rw-r--r--tests/ui/lint/invalid_value.stderr10
-rw-r--r--tests/ui/print_type_sizes/niche-filling.stdout2
17 files changed, 345 insertions, 214 deletions
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 784af940769..a4c3b7dd1b1 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -142,6 +142,7 @@
 #![feature(slice_range)]
 #![feature(std_internals)]
 #![feature(str_internals)]
+#![feature(temporary_niche_types)]
 #![feature(trusted_fused)]
 #![feature(trusted_len)]
 #![feature(trusted_random_access)]
diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs
index e93ff2f9023..ad86bf4bf07 100644
--- a/library/alloc/src/raw_vec.rs
+++ b/library/alloc/src/raw_vec.rs
@@ -33,21 +33,15 @@ 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);
+type Cap = core::num::niche_types::UsizeNoHighBit;
 
-impl Cap {
-    const ZERO: Cap = unsafe { Cap(0) };
+const ZERO_CAP: Cap = unsafe { Cap::new_unchecked(0) };
 
-    /// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`.
-    ///
-    /// # Safety: cap must be <= `isize::MAX`.
-    unsafe fn new<T>(cap: usize) -> Self {
-        if T::IS_ZST { Cap::ZERO } else { unsafe { Self(cap) } }
-    }
+/// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`.
+///
+/// # Safety: cap must be <= `isize::MAX`.
+unsafe fn new_cap<T>(cap: usize) -> Cap {
+    if T::IS_ZST { ZERO_CAP } else { unsafe { Cap::new_unchecked(cap) } }
 }
 
 /// A low-level utility for more ergonomically allocating, reallocating, and deallocating
@@ -257,7 +251,7 @@ impl<T, A: Allocator> RawVec<T, A> {
         // SAFETY: Precondition passed to the caller
         unsafe {
             let ptr = ptr.cast();
-            let capacity = Cap::new::<T>(capacity);
+            let capacity = new_cap::<T>(capacity);
             Self {
                 inner: RawVecInner::from_raw_parts_in(ptr, capacity, alloc),
                 _marker: PhantomData,
@@ -275,7 +269,7 @@ impl<T, A: Allocator> RawVec<T, A> {
         // SAFETY: Precondition passed to the caller
         unsafe {
             let ptr = ptr.cast();
-            let capacity = Cap::new::<T>(capacity);
+            let capacity = new_cap::<T>(capacity);
             Self { inner: RawVecInner::from_nonnull_in(ptr, capacity, alloc), _marker: PhantomData }
         }
     }
@@ -410,7 +404,7 @@ impl<A: Allocator> RawVecInner<A> {
     const fn new_in(alloc: A, align: usize) -> Self {
         let ptr = unsafe { core::mem::transmute(align) };
         // `cap: 0` means "unallocated". zero-sized types are ignored.
-        Self { ptr, cap: Cap::ZERO, alloc }
+        Self { ptr, cap: ZERO_CAP, alloc }
     }
 
     #[cfg(not(no_global_oom_handling))]
@@ -483,7 +477,11 @@ impl<A: Allocator> RawVecInner<A> {
         // 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::<T>()`.
-        Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc })
+        Ok(Self {
+            ptr: Unique::from(ptr.cast()),
+            cap: unsafe { Cap::new_unchecked(capacity) },
+            alloc,
+        })
     }
 
     #[inline]
@@ -508,7 +506,7 @@ impl<A: Allocator> RawVecInner<A> {
 
     #[inline]
     const fn capacity(&self, elem_size: usize) -> usize {
-        if elem_size == 0 { usize::MAX } else { self.cap.0 }
+        if elem_size == 0 { usize::MAX } else { self.cap.as_inner() }
     }
 
     #[inline]
@@ -518,7 +516,7 @@ impl<A: Allocator> RawVecInner<A> {
 
     #[inline]
     fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull<u8>, Layout)> {
-        if elem_layout.size() == 0 || self.cap.0 == 0 {
+        if elem_layout.size() == 0 || self.cap.as_inner() == 0 {
             None
         } else {
             // We could use Layout::array here which ensures the absence of isize and usize overflows
@@ -526,7 +524,7 @@ impl<A: Allocator> RawVecInner<A> {
             // has already been allocated so we know it can't overflow and currently Rust does not
             // support such types. So we can do better by skipping some checks and avoid an unwrap.
             unsafe {
-                let alloc_size = elem_layout.size().unchecked_mul(self.cap.0);
+                let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner());
                 let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align());
                 Some((self.ptr.into(), layout))
             }
@@ -562,7 +560,7 @@ impl<A: Allocator> RawVecInner<A> {
     #[inline]
     #[track_caller]
     fn grow_one(&mut self, elem_layout: Layout) {
-        if let Err(err) = self.grow_amortized(self.cap.0, 1, elem_layout) {
+        if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) {
             handle_error(err);
         }
     }
@@ -627,7 +625,7 @@ impl<A: Allocator> RawVecInner<A> {
         // the size requested. If that ever changes, the capacity here should
         // change to `ptr.len() / mem::size_of::<T>()`.
         self.ptr = Unique::from(ptr.cast());
-        self.cap = unsafe { Cap(cap) };
+        self.cap = unsafe { Cap::new_unchecked(cap) };
     }
 
     fn grow_amortized(
@@ -650,7 +648,7 @@ impl<A: Allocator> RawVecInner<A> {
 
         // 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.0 * 2, required_cap);
+        let cap = cmp::max(self.cap.as_inner() * 2, required_cap);
         let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap);
 
         let new_layout = layout_array(cap, elem_layout)?;
@@ -719,7 +717,7 @@ impl<A: Allocator> RawVecInner<A> {
             unsafe { self.alloc.deallocate(ptr, layout) };
             self.ptr =
                 unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) };
-            self.cap = Cap::ZERO;
+            self.cap = ZERO_CAP;
         } else {
             let ptr = unsafe {
                 // Layout cannot overflow here because it would have
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index 67d219654b9..6c1b568e231 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -51,6 +51,10 @@ mod overflow_panic;
 mod saturating;
 mod wrapping;
 
+/// 100% perma-unstable
+#[doc(hidden)]
+pub mod niche_types;
+
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg(not(no_fp_fmt_parse))]
 pub use dec2flt::ParseFloatError;
diff --git a/library/core/src/num/niche_types.rs b/library/core/src/num/niche_types.rs
new file mode 100644
index 00000000000..e4caf8a104c
--- /dev/null
+++ b/library/core/src/num/niche_types.rs
@@ -0,0 +1,162 @@
+#![unstable(
+    feature = "temporary_niche_types",
+    issue = "none",
+    reason = "for core, alloc, and std internals until pattern types are further along"
+)]
+
+use crate::cmp::Ordering;
+use crate::fmt;
+use crate::hash::{Hash, Hasher};
+use crate::marker::StructuralPartialEq;
+
+macro_rules! define_valid_range_type {
+    ($(
+        $(#[$m:meta])*
+        $vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal);
+    )+) => {$(
+        #[derive(Clone, Copy, Eq)]
+        #[repr(transparent)]
+        #[rustc_layout_scalar_valid_range_start($low)]
+        #[rustc_layout_scalar_valid_range_end($high)]
+        $(#[$m])*
+        $vis struct $name($int);
+
+        const _: () = {
+            // With the `valid_range` attributes, it's always specified as unsigned
+            assert!(<$uint>::MIN == 0);
+            let ulow: $uint = $low;
+            let uhigh: $uint = $high;
+            assert!(ulow <= uhigh);
+
+            assert!(size_of::<$int>() == size_of::<$uint>());
+        };
+
+        impl $name {
+            #[inline]
+            pub const unsafe fn new_unchecked(val: $int) -> Self {
+                // SAFETY: same precondition
+                unsafe { $name(val) }
+            }
+
+            #[inline]
+            pub const fn as_inner(self) -> $int {
+                // SAFETY: This is a transparent wrapper, so unwrapping it is sound
+                // (Not using `.0` due to MCP#807.)
+                unsafe { crate::mem::transmute(self) }
+            }
+        }
+
+        // This is required to allow matching a constant.  We don't get it from a derive
+        // because the derived `PartialEq` would do a field projection, which is banned
+        // by <https://github.com/rust-lang/compiler-team/issues/807>.
+        impl StructuralPartialEq for $name {}
+
+        impl PartialEq for $name {
+            #[inline]
+            fn eq(&self, other: &Self) -> bool {
+                self.as_inner() == other.as_inner()
+            }
+        }
+
+        impl Ord for $name {
+            #[inline]
+            fn cmp(&self, other: &Self) -> Ordering {
+                Ord::cmp(&self.as_inner(), &other.as_inner())
+            }
+        }
+
+        impl PartialOrd for $name {
+            #[inline]
+            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+                Some(Ord::cmp(self, other))
+            }
+        }
+
+        impl Hash for $name {
+            // Required method
+            fn hash<H: Hasher>(&self, state: &mut H) {
+                Hash::hash(&self.as_inner(), state);
+            }
+        }
+
+        impl fmt::Debug for $name {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                <$int as fmt::Debug>::fmt(&self.as_inner(), f)
+            }
+        }
+    )+};
+}
+
+define_valid_range_type! {
+    pub struct Nanoseconds(u32 as u32 in 0..=999_999_999);
+}
+
+impl Nanoseconds {
+    // SAFETY: 0 is within the valid range
+    pub const ZERO: Self = unsafe { Nanoseconds::new_unchecked(0) };
+}
+
+impl Default for Nanoseconds {
+    #[inline]
+    fn default() -> Self {
+        Self::ZERO
+    }
+}
+
+define_valid_range_type! {
+    pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff);
+    pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff);
+    pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff);
+    pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff);
+    pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff);
+
+    pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff);
+    pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff);
+    pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff);
+    pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff);
+    pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff);
+}
+
+#[cfg(target_pointer_width = "16")]
+define_valid_range_type! {
+    pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff);
+    pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff);
+    pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff);
+}
+#[cfg(target_pointer_width = "32")]
+define_valid_range_type! {
+    pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff);
+    pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff);
+    pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff);
+}
+#[cfg(target_pointer_width = "64")]
+define_valid_range_type! {
+    pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff);
+    pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff);
+    pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff);
+}
+
+define_valid_range_type! {
+    pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe);
+    pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe);
+
+    pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe);
+    pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe);
+}
+
+pub trait NotAllOnesHelper {
+    type Type;
+}
+pub type NotAllOnes<T> = <T as NotAllOnesHelper>::Type;
+impl NotAllOnesHelper for u32 {
+    type Type = U32NotAllOnes;
+}
+impl NotAllOnesHelper for i32 {
+    type Type = I32NotAllOnes;
+}
+impl NotAllOnesHelper for u64 {
+    type Type = U64NotAllOnes;
+}
+impl NotAllOnesHelper for i64 {
+    type Type = I64NotAllOnes;
+}
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index 926723b3dba..dbce64420ac 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -37,41 +37,12 @@ pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed {
 macro_rules! impl_zeroable_primitive {
     ($($NonZeroInner:ident ( $primitive:ty )),+ $(,)?) => {
         mod private {
-            use super::*;
-
             #[unstable(
                 feature = "nonzero_internals",
                 reason = "implementation detail which may disappear or be replaced at any time",
                 issue = "none"
             )]
             pub trait Sealed {}
-
-            $(
-                // This inner type is never shown directly, so intentionally does not have Debug
-                #[expect(missing_debug_implementations)]
-                // Since this struct is non-generic and derives Copy,
-                // the derived Clone is `*self` and thus doesn't field-project.
-                #[derive(Clone, Copy)]
-                #[repr(transparent)]
-                #[rustc_layout_scalar_valid_range_start(1)]
-                #[rustc_nonnull_optimization_guaranteed]
-                #[unstable(
-                    feature = "nonzero_internals",
-                    reason = "implementation detail which may disappear or be replaced at any time",
-                    issue = "none"
-                )]
-                pub struct $NonZeroInner($primitive);
-
-                // This is required to allow matching a constant.  We don't get it from a derive
-                // because the derived `PartialEq` would do a field projection, which is banned
-                // by <https://github.com/rust-lang/compiler-team/issues/807>.
-                #[unstable(
-                    feature = "nonzero_internals",
-                    reason = "implementation detail which may disappear or be replaced at any time",
-                    issue = "none"
-                )]
-                impl StructuralPartialEq for $NonZeroInner {}
-            )+
         }
 
         $(
@@ -88,7 +59,7 @@ macro_rules! impl_zeroable_primitive {
                 issue = "none"
             )]
             unsafe impl ZeroablePrimitive for $primitive {
-                type NonZeroInner = private::$NonZeroInner;
+                type NonZeroInner = super::niche_types::$NonZeroInner;
             }
         )+
     };
diff --git a/library/core/src/time.rs b/library/core/src/time.rs
index 2d93148bac0..22bd46c567e 100644
--- a/library/core/src/time.rs
+++ b/library/core/src/time.rs
@@ -21,6 +21,7 @@
 
 use crate::fmt;
 use crate::iter::Sum;
+use crate::num::niche_types::Nanoseconds;
 use crate::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
 
 const NANOS_PER_SEC: u32 = 1_000_000_000;
@@ -37,24 +38,6 @@ const HOURS_PER_DAY: u64 = 24;
 #[unstable(feature = "duration_units", issue = "120301")]
 const DAYS_PER_WEEK: u64 = 7;
 
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(transparent)]
-#[rustc_layout_scalar_valid_range_start(0)]
-#[rustc_layout_scalar_valid_range_end(999_999_999)]
-struct Nanoseconds(u32);
-
-impl Nanoseconds {
-    // SAFETY: 0 is within the valid range
-    const ZERO: Self = unsafe { Nanoseconds(0) };
-}
-
-impl Default for Nanoseconds {
-    #[inline]
-    fn default() -> Self {
-        Self::ZERO
-    }
-}
-
 /// A `Duration` type to represent a span of time, typically used for system
 /// timeouts.
 ///
@@ -211,14 +194,14 @@ impl Duration {
     pub const fn new(secs: u64, nanos: u32) -> Duration {
         if nanos < NANOS_PER_SEC {
             // SAFETY: nanos < NANOS_PER_SEC, therefore nanos is within the valid range
-            Duration { secs, nanos: unsafe { Nanoseconds(nanos) } }
+            Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } }
         } else {
             let secs = secs
                 .checked_add((nanos / NANOS_PER_SEC) as u64)
                 .expect("overflow in Duration::new");
             let nanos = nanos % NANOS_PER_SEC;
             // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range
-            Duration { secs, nanos: unsafe { Nanoseconds(nanos) } }
+            Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } }
         }
     }
 
@@ -263,7 +246,7 @@ impl Duration {
         let subsec_millis = (millis % MILLIS_PER_SEC) as u32;
         // SAFETY: (x % 1_000) * 1_000_000 < 1_000_000_000
         //         => x % 1_000 < 1_000
-        let subsec_nanos = unsafe { Nanoseconds(subsec_millis * NANOS_PER_MILLI) };
+        let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_millis * NANOS_PER_MILLI) };
 
         Duration { secs, nanos: subsec_nanos }
     }
@@ -289,7 +272,7 @@ impl Duration {
         let subsec_micros = (micros % MICROS_PER_SEC) as u32;
         // SAFETY: (x % 1_000_000) * 1_000 < 1_000_000_000
         //         => x % 1_000_000 < 1_000_000
-        let subsec_nanos = unsafe { Nanoseconds(subsec_micros * NANOS_PER_MICRO) };
+        let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_micros * NANOS_PER_MICRO) };
 
         Duration { secs, nanos: subsec_nanos }
     }
@@ -320,7 +303,7 @@ impl Duration {
         let secs = nanos / NANOS_PER_SEC;
         let subsec_nanos = (nanos % NANOS_PER_SEC) as u32;
         // SAFETY: x % 1_000_000_000 < 1_000_000_000
-        let subsec_nanos = unsafe { Nanoseconds(subsec_nanos) };
+        let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_nanos) };
 
         Duration { secs, nanos: subsec_nanos }
     }
@@ -458,7 +441,7 @@ impl Duration {
     #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")]
     #[inline]
     pub const fn is_zero(&self) -> bool {
-        self.secs == 0 && self.nanos.0 == 0
+        self.secs == 0 && self.nanos.as_inner() == 0
     }
 
     /// Returns the number of _whole_ seconds contained by this `Duration`.
@@ -509,7 +492,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn subsec_millis(&self) -> u32 {
-        self.nanos.0 / NANOS_PER_MILLI
+        self.nanos.as_inner() / NANOS_PER_MILLI
     }
 
     /// Returns the fractional part of this `Duration`, in whole microseconds.
@@ -532,7 +515,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn subsec_micros(&self) -> u32 {
-        self.nanos.0 / NANOS_PER_MICRO
+        self.nanos.as_inner() / NANOS_PER_MICRO
     }
 
     /// Returns the fractional part of this `Duration`, in nanoseconds.
@@ -555,7 +538,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn subsec_nanos(&self) -> u32 {
-        self.nanos.0
+        self.nanos.as_inner()
     }
 
     /// Returns the total number of whole milliseconds contained by this `Duration`.
@@ -573,7 +556,8 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn as_millis(&self) -> u128 {
-        self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MILLI) as u128
+        self.secs as u128 * MILLIS_PER_SEC as u128
+            + (self.nanos.as_inner() / NANOS_PER_MILLI) as u128
     }
 
     /// Returns the total number of whole microseconds contained by this `Duration`.
@@ -591,7 +575,8 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn as_micros(&self) -> u128 {
-        self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MICRO) as u128
+        self.secs as u128 * MICROS_PER_SEC as u128
+            + (self.nanos.as_inner() / NANOS_PER_MICRO) as u128
     }
 
     /// Returns the total number of nanoseconds contained by this `Duration`.
@@ -609,7 +594,7 @@ impl Duration {
     #[must_use]
     #[inline]
     pub const fn as_nanos(&self) -> u128 {
-        self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.0 as u128
+        self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.as_inner() as u128
     }
 
     /// Computes the absolute difference between `self` and `other`.
@@ -649,7 +634,7 @@ impl Duration {
     #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")]
     pub const fn checked_add(self, rhs: Duration) -> Option<Duration> {
         if let Some(mut secs) = self.secs.checked_add(rhs.secs) {
-            let mut nanos = self.nanos.0 + rhs.nanos.0;
+            let mut nanos = self.nanos.as_inner() + rhs.nanos.as_inner();
             if nanos >= NANOS_PER_SEC {
                 nanos -= NANOS_PER_SEC;
                 if let Some(new_secs) = secs.checked_add(1) {
@@ -707,11 +692,11 @@ impl Duration {
     #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")]
     pub const fn checked_sub(self, rhs: Duration) -> Option<Duration> {
         if let Some(mut secs) = self.secs.checked_sub(rhs.secs) {
-            let nanos = if self.nanos.0 >= rhs.nanos.0 {
-                self.nanos.0 - rhs.nanos.0
+            let nanos = if self.nanos.as_inner() >= rhs.nanos.as_inner() {
+                self.nanos.as_inner() - rhs.nanos.as_inner()
             } else if let Some(sub_secs) = secs.checked_sub(1) {
                 secs = sub_secs;
-                self.nanos.0 + NANOS_PER_SEC - rhs.nanos.0
+                self.nanos.as_inner() + NANOS_PER_SEC - rhs.nanos.as_inner()
             } else {
                 return None;
             };
@@ -763,7 +748,7 @@ impl Duration {
     #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")]
     pub const fn checked_mul(self, rhs: u32) -> Option<Duration> {
         // Multiply nanoseconds as u64, because it cannot overflow that way.
-        let total_nanos = self.nanos.0 as u64 * rhs as u64;
+        let total_nanos = self.nanos.as_inner() as u64 * rhs as u64;
         let extra_secs = total_nanos / (NANOS_PER_SEC as u64);
         let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32;
         // FIXME(const-hack): use `and_then` once that is possible.
@@ -820,7 +805,8 @@ impl Duration {
     pub const fn checked_div(self, rhs: u32) -> Option<Duration> {
         if rhs != 0 {
             let (secs, extra_secs) = (self.secs / (rhs as u64), self.secs % (rhs as u64));
-            let (mut nanos, extra_nanos) = (self.nanos.0 / rhs, self.nanos.0 % rhs);
+            let (mut nanos, extra_nanos) =
+                (self.nanos.as_inner() / rhs, self.nanos.as_inner() % rhs);
             nanos +=
                 ((extra_secs * (NANOS_PER_SEC as u64) + extra_nanos as u64) / (rhs as u64)) as u32;
             debug_assert!(nanos < NANOS_PER_SEC);
@@ -846,7 +832,7 @@ impl Duration {
     #[inline]
     #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")]
     pub const fn as_secs_f64(&self) -> f64 {
-        (self.secs as f64) + (self.nanos.0 as f64) / (NANOS_PER_SEC as f64)
+        (self.secs as f64) + (self.nanos.as_inner() as f64) / (NANOS_PER_SEC as f64)
     }
 
     /// Returns the number of seconds contained by this `Duration` as `f32`.
@@ -865,7 +851,7 @@ impl Duration {
     #[inline]
     #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")]
     pub const fn as_secs_f32(&self) -> f32 {
-        (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32)
+        (self.secs as f32) + (self.nanos.as_inner() as f32) / (NANOS_PER_SEC as f32)
     }
 
     /// Returns the number of milliseconds contained by this `Duration` as `f64`.
@@ -885,7 +871,7 @@ impl Duration {
     #[inline]
     pub const fn as_millis_f64(&self) -> f64 {
         (self.secs as f64) * (MILLIS_PER_SEC as f64)
-            + (self.nanos.0 as f64) / (NANOS_PER_MILLI as f64)
+            + (self.nanos.as_inner() as f64) / (NANOS_PER_MILLI as f64)
     }
 
     /// Returns the number of milliseconds contained by this `Duration` as `f32`.
@@ -905,7 +891,7 @@ impl Duration {
     #[inline]
     pub const fn as_millis_f32(&self) -> f32 {
         (self.secs as f32) * (MILLIS_PER_SEC as f32)
-            + (self.nanos.0 as f32) / (NANOS_PER_MILLI as f32)
+            + (self.nanos.as_inner() as f32) / (NANOS_PER_MILLI as f32)
     }
 
     /// Creates a new `Duration` from the specified number of seconds represented
@@ -1084,8 +1070,9 @@ impl Duration {
     #[inline]
     #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")]
     pub const fn div_duration_f64(self, rhs: Duration) -> f64 {
-        let self_nanos = (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.0 as f64);
-        let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.0 as f64);
+        let self_nanos =
+            (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.as_inner() as f64);
+        let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.as_inner() as f64);
         self_nanos / rhs_nanos
     }
 
@@ -1105,8 +1092,9 @@ impl Duration {
     #[inline]
     #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")]
     pub const fn div_duration_f32(self, rhs: Duration) -> f32 {
-        let self_nanos = (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.0 as f32);
-        let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.0 as f32);
+        let self_nanos =
+            (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.as_inner() as f32);
+        let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.as_inner() as f32);
         self_nanos / rhs_nanos
     }
 }
@@ -1201,13 +1189,13 @@ macro_rules! sum_durations {
         for entry in $iter {
             total_secs =
                 total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations");
-            total_nanos = match total_nanos.checked_add(entry.nanos.0 as u64) {
+            total_nanos = match total_nanos.checked_add(entry.nanos.as_inner() as u64) {
                 Some(n) => n,
                 None => {
                     total_secs = total_secs
                         .checked_add(total_nanos / NANOS_PER_SEC as u64)
                         .expect("overflow in iter::sum over durations");
-                    (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.0 as u64
+                    (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.as_inner() as u64
                 }
             };
         }
@@ -1399,27 +1387,27 @@ impl fmt::Debug for Duration {
         let prefix = if f.sign_plus() { "+" } else { "" };
 
         if self.secs > 0 {
-            fmt_decimal(f, self.secs, self.nanos.0, NANOS_PER_SEC / 10, prefix, "s")
-        } else if self.nanos.0 >= NANOS_PER_MILLI {
+            fmt_decimal(f, self.secs, self.nanos.as_inner(), NANOS_PER_SEC / 10, prefix, "s")
+        } else if self.nanos.as_inner() >= NANOS_PER_MILLI {
             fmt_decimal(
                 f,
-                (self.nanos.0 / NANOS_PER_MILLI) as u64,
-                self.nanos.0 % NANOS_PER_MILLI,
+                (self.nanos.as_inner() / NANOS_PER_MILLI) as u64,
+                self.nanos.as_inner() % NANOS_PER_MILLI,
                 NANOS_PER_MILLI / 10,
                 prefix,
                 "ms",
             )
-        } else if self.nanos.0 >= NANOS_PER_MICRO {
+        } else if self.nanos.as_inner() >= NANOS_PER_MICRO {
             fmt_decimal(
                 f,
-                (self.nanos.0 / NANOS_PER_MICRO) as u64,
-                self.nanos.0 % NANOS_PER_MICRO,
+                (self.nanos.as_inner() / NANOS_PER_MICRO) as u64,
+                self.nanos.as_inner() % NANOS_PER_MICRO,
                 NANOS_PER_MICRO / 10,
                 prefix,
                 "µs",
             )
         } else {
-            fmt_decimal(f, self.nanos.0 as u64, 0, 1, prefix, "ns")
+            fmt_decimal(f, self.nanos.as_inner() as u64, 0, 1, prefix, "ns")
         }
     }
 }
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 2f8f5c5c581..022bbab1012 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -357,6 +357,7 @@
 #![feature(str_internals)]
 #![feature(strict_provenance_atomic_ptr)]
 #![feature(sync_unsafe_cell)]
+#![feature(temporary_niche_types)]
 #![feature(ub_checks)]
 #![feature(used_with_arg)]
 // tidy-alphabetical-end
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs
index abb13b75f50..1e814eca3c1 100644
--- a/library/std/src/os/fd/owned.rs
+++ b/library/std/src/os/fd/owned.rs
@@ -11,6 +11,8 @@ use crate::sys::cvt;
 use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::{fmt, fs, io};
 
+type ValidRawFd = core::num::niche_types::NotAllOnes<RawFd>;
+
 /// A borrowed file descriptor.
 ///
 /// This has a lifetime parameter to tie it to the lifetime of something that owns the file
@@ -32,15 +34,10 @@ use crate::{fmt, fs, io};
 /// instead, but this is not supported on all platforms.
 #[derive(Copy, Clone)]
 #[repr(transparent)]
-#[rustc_layout_scalar_valid_range_start(0)]
-// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
-// 32-bit c_int. Below is -2, in two's complement, but that only works out
-// because c_int is 32 bits.
-#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
 #[rustc_nonnull_optimization_guaranteed]
 #[stable(feature = "io_safety", since = "1.63.0")]
 pub struct BorrowedFd<'fd> {
-    fd: RawFd,
+    fd: ValidRawFd,
     _phantom: PhantomData<&'fd OwnedFd>,
 }
 
@@ -56,15 +53,10 @@ pub struct BorrowedFd<'fd> {
 ///
 /// You can use [`AsFd::as_fd`] to obtain a [`BorrowedFd`].
 #[repr(transparent)]
-#[rustc_layout_scalar_valid_range_start(0)]
-// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
-// 32-bit c_int. Below is -2, in two's complement, but that only works out
-// because c_int is 32 bits.
-#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
 #[rustc_nonnull_optimization_guaranteed]
 #[stable(feature = "io_safety", since = "1.63.0")]
 pub struct OwnedFd {
-    fd: RawFd,
+    fd: ValidRawFd,
 }
 
 impl BorrowedFd<'_> {
@@ -80,7 +72,8 @@ impl BorrowedFd<'_> {
     pub const unsafe fn borrow_raw(fd: RawFd) -> Self {
         assert!(fd != u32::MAX as RawFd);
         // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
-        unsafe { Self { fd, _phantom: PhantomData } }
+        let fd = unsafe { ValidRawFd::new_unchecked(fd) };
+        Self { fd, _phantom: PhantomData }
     }
 }
 
@@ -130,7 +123,7 @@ impl BorrowedFd<'_> {
 impl AsRawFd for BorrowedFd<'_> {
     #[inline]
     fn as_raw_fd(&self) -> RawFd {
-        self.fd
+        self.fd.as_inner()
     }
 }
 
@@ -138,7 +131,7 @@ impl AsRawFd for BorrowedFd<'_> {
 impl AsRawFd for OwnedFd {
     #[inline]
     fn as_raw_fd(&self) -> RawFd {
-        self.fd
+        self.fd.as_inner()
     }
 }
 
@@ -146,7 +139,7 @@ impl AsRawFd for OwnedFd {
 impl IntoRawFd for OwnedFd {
     #[inline]
     fn into_raw_fd(self) -> RawFd {
-        ManuallyDrop::new(self).fd
+        ManuallyDrop::new(self).fd.as_inner()
     }
 }
 
@@ -164,7 +157,8 @@ impl FromRawFd for OwnedFd {
     unsafe fn from_raw_fd(fd: RawFd) -> Self {
         assert_ne!(fd, u32::MAX as RawFd);
         // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
-        unsafe { Self { fd } }
+        let fd = unsafe { ValidRawFd::new_unchecked(fd) };
+        Self { fd }
     }
 }
 
@@ -187,12 +181,12 @@ impl Drop for OwnedFd {
             #[cfg(not(target_os = "hermit"))]
             {
                 #[cfg(unix)]
-                crate::sys::fs::debug_assert_fd_is_open(self.fd);
+                crate::sys::fs::debug_assert_fd_is_open(self.fd.as_inner());
 
-                let _ = libc::close(self.fd);
+                let _ = libc::close(self.fd.as_inner());
             }
             #[cfg(target_os = "hermit")]
-            let _ = hermit_abi::close(self.fd);
+            let _ = hermit_abi::close(self.fd.as_inner());
         }
     }
 }
diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs
index 2d18f339615..b8601b533fe 100644
--- a/library/std/src/os/solid/io.rs
+++ b/library/std/src/os/solid/io.rs
@@ -54,6 +54,9 @@ use crate::{fmt, net, sys};
 /// Raw file descriptors.
 pub type RawFd = i32;
 
+// The max of this is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`.
+type ValidRawFd = core::num::niche_types::NotAllOnes<RawFd>;
+
 /// A borrowed SOLID Sockets file descriptor.
 ///
 /// This has a lifetime parameter to tie it to the lifetime of something that
@@ -69,12 +72,9 @@ pub type RawFd = i32;
 /// socket, which is then borrowed under the same lifetime.
 #[derive(Copy, Clone)]
 #[repr(transparent)]
-#[rustc_layout_scalar_valid_range_start(0)]
-// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`.
-#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
 #[rustc_nonnull_optimization_guaranteed]
 pub struct BorrowedFd<'socket> {
-    fd: RawFd,
+    fd: ValidRawFd,
     _phantom: PhantomData<&'socket OwnedFd>,
 }
 
@@ -87,12 +87,9 @@ pub struct BorrowedFd<'socket> {
 /// an argument, it is not captured or consumed, and it never has the value
 /// `SOLID_NET_INVALID_FD`.
 #[repr(transparent)]
-#[rustc_layout_scalar_valid_range_start(0)]
-// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`.
-#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
 #[rustc_nonnull_optimization_guaranteed]
 pub struct OwnedFd {
-    fd: RawFd,
+    fd: ValidRawFd,
 }
 
 impl BorrowedFd<'_> {
@@ -108,7 +105,8 @@ impl BorrowedFd<'_> {
         assert!(fd != -1 as RawFd);
         // SAFETY: we just asserted that the value is in the valid range and
         // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
-        unsafe { Self { fd, _phantom: PhantomData } }
+        let fd = unsafe { ValidRawFd::new_unchecked(fd) };
+        Self { fd, _phantom: PhantomData }
     }
 }
 
@@ -132,21 +130,21 @@ impl BorrowedFd<'_> {
 impl AsRawFd for BorrowedFd<'_> {
     #[inline]
     fn as_raw_fd(&self) -> RawFd {
-        self.fd
+        self.fd.as_inner()
     }
 }
 
 impl AsRawFd for OwnedFd {
     #[inline]
     fn as_raw_fd(&self) -> RawFd {
-        self.fd
+        self.fd.as_inner()
     }
 }
 
 impl IntoRawFd for OwnedFd {
     #[inline]
     fn into_raw_fd(self) -> RawFd {
-        ManuallyDrop::new(self).fd
+        ManuallyDrop::new(self).fd.as_inner()
     }
 }
 
@@ -162,14 +160,15 @@ impl FromRawFd for OwnedFd {
         assert_ne!(fd, -1 as RawFd);
         // SAFETY: we just asserted that the value is in the valid range and
         // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
-        unsafe { Self { fd } }
+        let fd = unsafe { ValidRawFd::new_unchecked(fd) };
+        Self { fd }
     }
 }
 
 impl Drop for OwnedFd {
     #[inline]
     fn drop(&mut self) {
-        unsafe { sys::net::netc::close(self.fd) };
+        unsafe { sys::net::netc::close(self.fd.as_inner()) };
     }
 }
 
diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs
index c6d7bad9440..6e13a8b502a 100644
--- a/library/std/src/os/windows/io/socket.rs
+++ b/library/std/src/os/windows/io/socket.rs
@@ -9,6 +9,9 @@ use crate::mem::{self, ManuallyDrop};
 use crate::sys::cvt;
 use crate::{fmt, io, sys};
 
+// The max here is -2, in two's complement. -1 is `INVALID_SOCKET`.
+type ValidRawSocket = core::num::niche_types::NotAllOnes<RawSocket>;
+
 /// A borrowed socket.
 ///
 /// This has a lifetime parameter to tie it to the lifetime of something that
@@ -24,17 +27,10 @@ use crate::{fmt, io, sys};
 /// socket, which is then borrowed under the same lifetime.
 #[derive(Copy, Clone)]
 #[repr(transparent)]
-#[rustc_layout_scalar_valid_range_start(0)]
-// This is -2, in two's complement. -1 is `INVALID_SOCKET`.
-#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))]
-#[cfg_attr(
-    target_pointer_width = "64",
-    rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE)
-)]
 #[rustc_nonnull_optimization_guaranteed]
 #[stable(feature = "io_safety", since = "1.63.0")]
 pub struct BorrowedSocket<'socket> {
-    socket: RawSocket,
+    socket: ValidRawSocket,
     _phantom: PhantomData<&'socket OwnedSocket>,
 }
 
@@ -47,17 +43,10 @@ pub struct BorrowedSocket<'socket> {
 /// argument or returned as an owned value, and it never has the value
 /// `INVALID_SOCKET`.
 #[repr(transparent)]
-#[rustc_layout_scalar_valid_range_start(0)]
-// This is -2, in two's complement. -1 is `INVALID_SOCKET`.
-#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))]
-#[cfg_attr(
-    target_pointer_width = "64",
-    rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE)
-)]
 #[rustc_nonnull_optimization_guaranteed]
 #[stable(feature = "io_safety", since = "1.63.0")]
 pub struct OwnedSocket {
-    socket: RawSocket,
+    socket: ValidRawSocket,
 }
 
 impl BorrowedSocket<'_> {
@@ -73,7 +62,8 @@ impl BorrowedSocket<'_> {
     #[stable(feature = "io_safety", since = "1.63.0")]
     pub const unsafe fn borrow_raw(socket: RawSocket) -> Self {
         assert!(socket != sys::c::INVALID_SOCKET as RawSocket);
-        unsafe { Self { socket, _phantom: PhantomData } }
+        let socket = unsafe { ValidRawSocket::new_unchecked(socket) };
+        Self { socket, _phantom: PhantomData }
     }
 }
 
@@ -172,7 +162,7 @@ fn last_error() -> io::Error {
 impl AsRawSocket for BorrowedSocket<'_> {
     #[inline]
     fn as_raw_socket(&self) -> RawSocket {
-        self.socket
+        self.socket.as_inner()
     }
 }
 
@@ -180,7 +170,7 @@ impl AsRawSocket for BorrowedSocket<'_> {
 impl AsRawSocket for OwnedSocket {
     #[inline]
     fn as_raw_socket(&self) -> RawSocket {
-        self.socket
+        self.socket.as_inner()
     }
 }
 
@@ -188,7 +178,7 @@ impl AsRawSocket for OwnedSocket {
 impl IntoRawSocket for OwnedSocket {
     #[inline]
     fn into_raw_socket(self) -> RawSocket {
-        ManuallyDrop::new(self).socket
+        ManuallyDrop::new(self).socket.as_inner()
     }
 }
 
@@ -196,10 +186,9 @@ impl IntoRawSocket for OwnedSocket {
 impl FromRawSocket for OwnedSocket {
     #[inline]
     unsafe fn from_raw_socket(socket: RawSocket) -> Self {
-        unsafe {
-            debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket);
-            Self { socket }
-        }
+        debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket);
+        let socket = unsafe { ValidRawSocket::new_unchecked(socket) };
+        Self { socket }
     }
 }
 
@@ -208,7 +197,7 @@ impl Drop for OwnedSocket {
     #[inline]
     fn drop(&mut self) {
         unsafe {
-            let _ = sys::c::closesocket(self.socket as sys::c::SOCKET);
+            let _ = sys::c::closesocket(self.socket.as_inner() as sys::c::SOCKET);
         }
     }
 }
diff --git a/library/std/src/sys/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs
index 04dd10ad806..fa2e470d6b6 100644
--- a/library/std/src/sys/pal/solid/fs.rs
+++ b/library/std/src/sys/pal/solid/fs.rs
@@ -12,15 +12,12 @@ use crate::sys::unsupported;
 pub use crate::sys_common::fs::exists;
 use crate::sys_common::ignore_notfound;
 
+type CIntNotMinusOne = core::num::niche_types::NotAllOnes<c_int>;
+
 /// A file descriptor.
 #[derive(Clone, Copy)]
-#[rustc_layout_scalar_valid_range_start(0)]
-// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
-// 32-bit c_int. Below is -2, in two's complement, but that only works out
-// because c_int is 32 bits.
-#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
 struct FileDesc {
-    fd: c_int,
+    fd: CIntNotMinusOne,
 }
 
 impl FileDesc {
@@ -29,12 +26,13 @@ impl FileDesc {
         assert_ne!(fd, -1i32);
         // Safety: we just asserted that the value is in the valid range and
         // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
-        unsafe { FileDesc { fd } }
+        let fd = unsafe { CIntNotMinusOne::new_unchecked(fd) };
+        FileDesc { fd }
     }
 
     #[inline]
     fn raw(&self) -> c_int {
-        self.fd
+        self.fd.as_inner()
     }
 }
 
diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs
index 343864d0b3f..e224980e95f 100644
--- a/library/std/src/sys/pal/unix/time.rs
+++ b/library/std/src/sys/pal/unix/time.rs
@@ -1,3 +1,5 @@
+use core::num::niche_types::Nanoseconds;
+
 use crate::time::Duration;
 use crate::{fmt, io};
 
@@ -16,12 +18,6 @@ pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec {
 };
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(transparent)]
-#[rustc_layout_scalar_valid_range_start(0)]
-#[rustc_layout_scalar_valid_range_end(999_999_999)]
-struct Nanoseconds(u32);
-
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct SystemTime {
     pub(crate) t: Timespec,
 }
@@ -59,14 +55,14 @@ impl fmt::Debug for SystemTime {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("SystemTime")
             .field("tv_sec", &self.t.tv_sec)
-            .field("tv_nsec", &self.t.tv_nsec.0)
+            .field("tv_nsec", &self.t.tv_nsec)
             .finish()
     }
 }
 
 impl Timespec {
     const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec {
-        Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } }
+        Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } }
     }
 
     pub const fn zero() -> Timespec {
@@ -147,12 +143,15 @@ impl Timespec {
             //
             // Ideally this code could be rearranged such that it more
             // directly expresses the lower-cost behavior we want from it.
-            let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 {
-                ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0)
+            let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() {
+                (
+                    (self.tv_sec - other.tv_sec) as u64,
+                    self.tv_nsec.as_inner() - other.tv_nsec.as_inner(),
+                )
             } else {
                 (
                     (self.tv_sec - other.tv_sec - 1) as u64,
-                    self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0,
+                    self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(),
                 )
             };
 
@@ -170,7 +169,7 @@ impl Timespec {
 
         // Nano calculations can't overflow because nanos are <1B which fit
         // in a u32.
-        let mut nsec = other.subsec_nanos() + self.tv_nsec.0;
+        let mut nsec = other.subsec_nanos() + self.tv_nsec.as_inner();
         if nsec >= NSEC_PER_SEC as u32 {
             nsec -= NSEC_PER_SEC as u32;
             secs = secs.checked_add(1)?;
@@ -182,7 +181,7 @@ impl Timespec {
         let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
 
         // Similar to above, nanos can't overflow.
-        let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32;
+        let mut nsec = self.tv_nsec.as_inner() as i32 - other.subsec_nanos() as i32;
         if nsec < 0 {
             nsec += NSEC_PER_SEC as i32;
             secs = secs.checked_sub(1)?;
@@ -194,7 +193,7 @@ impl Timespec {
     pub fn to_timespec(&self) -> Option<libc::timespec> {
         Some(libc::timespec {
             tv_sec: self.tv_sec.try_into().ok()?,
-            tv_nsec: self.tv_nsec.0.try_into().ok()?,
+            tv_nsec: self.tv_nsec.as_inner().try_into().ok()?,
         })
     }
 
@@ -203,7 +202,7 @@ impl Timespec {
     #[cfg(target_os = "nto")]
     pub(in crate::sys) fn to_timespec_capped(&self) -> Option<libc::timespec> {
         // Check if timeout in nanoseconds would fit into an u64
-        if (self.tv_nsec.0 as u64)
+        if (self.tv_nsec.as_inner() as u64)
             .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?)
             .is_none()
         {
@@ -219,7 +218,7 @@ impl Timespec {
         not(target_arch = "riscv32")
     ))]
     pub fn to_timespec64(&self) -> __timespec64 {
-        __timespec64::new(self.tv_sec, self.tv_nsec.0 as _)
+        __timespec64::new(self.tv_sec, self.tv_nsec.as_inner() as _)
     }
 }
 
@@ -293,7 +292,7 @@ impl fmt::Debug for Instant {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("Instant")
             .field("tv_sec", &self.t.tv_sec)
-            .field("tv_nsec", &self.t.tv_nsec.0)
+            .field("tv_nsec", &self.t.tv_nsec)
             .finish()
     }
 }
diff --git a/tests/codegen/vec-in-place.rs b/tests/codegen/vec-in-place.rs
index 33de0913f77..e835a7ef69b 100644
--- a/tests/codegen/vec-in-place.rs
+++ b/tests/codegen/vec-in-place.rs
@@ -37,8 +37,11 @@ pub struct Baz {
 #[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: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
+    // CHECK-NOT: loop
+    // CHECK-NOT: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
     // CHECK-NOT: loop
     // CHECK-NOT: call
     vec.into_iter().map(|e| e as u8).collect()
@@ -48,8 +51,11 @@ pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> {
 #[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: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
+    // CHECK-NOT: loop
+    // CHECK-NOT: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
     // CHECK-NOT: loop
     // CHECK-NOT: call
     vec.into_iter().map(|e| Wrapper(e)).collect()
@@ -76,8 +82,11 @@ pub fn vec_iterator_cast_signed_nested(vec: Vec<Vec<i32>>) -> Vec<Vec<u32>> {
 #[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: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
+    // CHECK-NOT: loop
+    // CHECK-NOT: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
     // CHECK-NOT: loop
     // CHECK-NOT: call
     vec.into_iter().map(|e| e.0).collect()
@@ -87,8 +96,11 @@ pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
 #[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: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
+    // CHECK-NOT: loop
+    // CHECK-NOT: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
     // CHECK-NOT: loop
     // CHECK-NOT: call
     vec.into_iter().map(|e| unsafe { std::mem::transmute(e) }).collect()
@@ -98,8 +110,11 @@ pub fn vec_iterator_cast_aggregate(vec: Vec<[u64; 4]>) -> Vec<Foo> {
 #[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: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
+    // CHECK-NOT: loop
+    // CHECK-NOT: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
     // CHECK-NOT: loop
     // CHECK-NOT: call
 
@@ -114,8 +129,11 @@ pub fn vec_iterator_cast_deaggregate_tra(vec: Vec<Bar>) -> Vec<[u64; 4]> {
 #[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: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
+    // CHECK-NOT: loop
+    // CHECK-NOT: call
+    // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
     // CHECK-NOT: loop
     // CHECK-NOT: call
 
@@ -138,6 +156,12 @@ pub fn vec_iterator_cast_unwrap_drop(vec: Vec<Wrapper<String>>) -> Vec<String> {
     // CHECK-NOT: call
     // 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
 
     vec.into_iter().map(|Wrapper(e)| e).collect()
 }
@@ -154,6 +178,12 @@ pub fn vec_iterator_cast_wrap_drop(vec: Vec<String>) -> Vec<Wrapper<String>> {
     // CHECK-NOT: call
     // 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()
diff --git a/tests/codegen/vec_pop_push_noop.rs b/tests/codegen/vec_pop_push_noop.rs
index a8ad5b6f1a3..4821e840884 100644
--- a/tests/codegen/vec_pop_push_noop.rs
+++ b/tests/codegen/vec_pop_push_noop.rs
@@ -12,9 +12,6 @@ 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|[}]}}
     if let Some(x) = v.pop() {
diff --git a/tests/debuginfo/strings-and-strs.rs b/tests/debuginfo/strings-and-strs.rs
index 3d6589db34b..543e74e1744 100644
--- a/tests/debuginfo/strings-and-strs.rs
+++ b/tests/debuginfo/strings-and-strs.rs
@@ -7,7 +7,7 @@
 // gdb-command:run
 
 // gdb-command:print plain_string
-// gdb-check:$1 = alloc::string::String {vec: alloc::vec::Vec<u8, alloc::alloc::Global> {buf: alloc::raw_vec::RawVec<u8, alloc::alloc::Global> {inner: alloc::raw_vec::RawVecInner<alloc::alloc::Global> {ptr: core::ptr::unique::Unique<u8> {pointer: core::ptr::non_null::NonNull<u8> {pointer: 0x[...]}, _marker: core::marker::PhantomData<u8>}, cap: alloc::raw_vec::Cap (5), alloc: alloc::alloc::Global}, _marker: core::marker::PhantomData<u8>}, len: 5}}
+// gdb-check:$1 = alloc::string::String {vec: alloc::vec::Vec<u8, alloc::alloc::Global> {buf: alloc::raw_vec::RawVec<u8, alloc::alloc::Global> {inner: alloc::raw_vec::RawVecInner<alloc::alloc::Global> {ptr: core::ptr::unique::Unique<u8> {pointer: core::ptr::non_null::NonNull<u8> {pointer: 0x[...]}, _marker: core::marker::PhantomData<u8>}, cap: core::num::niche_types::UsizeNoHighBit (5), alloc: alloc::alloc::Global}, _marker: core::marker::PhantomData<u8>}, len: 5}}
 
 // gdb-command:print plain_str
 // gdb-check:$2 = "Hello"
diff --git a/tests/ui/lint/invalid_value.stderr b/tests/ui/lint/invalid_value.stderr
index b4e7421829f..cc6a2a1c8e5 100644
--- a/tests/ui/lint/invalid_value.stderr
+++ b/tests/ui/lint/invalid_value.stderr
@@ -323,7 +323,7 @@ LL |         let _val: (NonZero<u32>, i32) = mem::zeroed();
    |                                         ^^^^^^^^^^^^^ this code causes undefined behavior when executed
    |
    = note: `std::num::NonZero<u32>` must be non-null
-   = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null
+   = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null
 
 error: the type `(NonZero<u32>, i32)` does not permit being left uninitialized
   --> $DIR/invalid_value.rs:95:41
@@ -332,7 +332,7 @@ LL |         let _val: (NonZero<u32>, i32) = mem::uninitialized();
    |                                         ^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
    |
    = note: `std::num::NonZero<u32>` must be non-null
-   = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null
+   = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null
    = note: integers must be initialized
 
 error: the type `*const dyn Send` does not permit zero-initialization
@@ -415,7 +415,7 @@ note: because `std::num::NonZero<u32>` must be non-null (in this field of the on
    |
 LL |     Banana(NonZero<u32>),
    |            ^^^^^^^^^^^^
-   = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null
+   = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null
 
 error: the type `OneFruitNonZero` does not permit being left uninitialized
   --> $DIR/invalid_value.rs:107:37
@@ -429,7 +429,7 @@ note: because `std::num::NonZero<u32>` must be non-null (in this field of the on
    |
 LL |     Banana(NonZero<u32>),
    |            ^^^^^^^^^^^^
-   = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null
+   = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null
    = note: integers must be initialized
 
 error: the type `bool` does not permit being left uninitialized
@@ -602,7 +602,7 @@ LL |         let _val: NonZero<u32> = mem::transmute(0);
    |                                  ^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
    |
    = note: `std::num::NonZero<u32>` must be non-null
-   = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null
+   = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null
 
 error: the type `NonNull<i32>` does not permit zero-initialization
   --> $DIR/invalid_value.rs:156:34
diff --git a/tests/ui/print_type_sizes/niche-filling.stdout b/tests/ui/print_type_sizes/niche-filling.stdout
index eeb5de53241..70612490a47 100644
--- a/tests/ui/print_type_sizes/niche-filling.stdout
+++ b/tests/ui/print_type_sizes/niche-filling.stdout
@@ -68,7 +68,7 @@ print-type-size type: `Union2<std::num::NonZero<u32>, u32>`: 4 bytes, alignment:
 print-type-size     variant `Union2`: 4 bytes
 print-type-size         field `.a`: 4 bytes
 print-type-size         field `.b`: 4 bytes, offset: 0 bytes, alignment: 4 bytes
-print-type-size type: `core::num::nonzero::private::NonZeroU32Inner`: 4 bytes, alignment: 4 bytes
+print-type-size type: `core::num::niche_types::NonZeroU32Inner`: 4 bytes, alignment: 4 bytes
 print-type-size     field `.0`: 4 bytes
 print-type-size type: `std::num::NonZero<u32>`: 4 bytes, alignment: 4 bytes
 print-type-size     field `.0`: 4 bytes