Skip to content

Commit

Permalink
Manually implement derived NonZero traits.
Browse files Browse the repository at this point in the history
  • Loading branch information
reitermarkus committed Jan 20, 2024
1 parent 227abac commit 9aa4830
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 8 deletions.
182 changes: 174 additions & 8 deletions library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Definitions of integer that is known not to equal zero.

use crate::cmp::Ordering;
use crate::fmt;
use crate::hash::{Hash, Hasher};
use crate::marker::StructuralPartialEq;
use crate::ops::{BitOr, BitOrAssign, Div, Neg, Rem};
use crate::str::FromStr;

Expand Down Expand Up @@ -31,13 +34,6 @@ pub trait ZeroablePrimitive: Sized + Copy + private::Sealed {
type NonZero;
}

#[unstable(
feature = "nonzero_internals",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "none"
)]
pub(crate) type NonZero<T> = <T as ZeroablePrimitive>::NonZero;

macro_rules! impl_zeroable_primitive {
($NonZero:ident ( $primitive:ty )) => {
#[unstable(
Expand Down Expand Up @@ -71,6 +67,174 @@ impl_zeroable_primitive!(NonZeroI64(i64));
impl_zeroable_primitive!(NonZeroI128(i128));
impl_zeroable_primitive!(NonZeroIsize(isize));

/// A value that is known not to equal zero.
///
/// This enables some memory layout optimization.
/// For example, `Option<NonZero<u32>>` is the same size as `u32`:
///
/// ```rust
/// #![feature(generic_nonzero)]
///
/// use core::mem::size_of;
/// assert_eq!(size_of::<Option<core::num::NonZero<u32>>>(), size_of::<u32>());
/// ```
#[unstable(feature = "generic_nonzero", issue = "82363")]
#[derive(Clone, Copy)]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
#[rustc_diagnostic_item = "NonZero"]
pub struct NonZero2<T: ZeroablePrimitive>(T);

#[unstable(
feature = "nonzero_internals",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "none"
)]
pub(crate) type NonZero<T> = <T as ZeroablePrimitive>::NonZero;

macro_rules! impl_nonzero_traits {
(#[$stability:meta] $Ty:ty) => {
#[$stability]
impl Clone for $Ty {
#[inline]
fn clone(&self) -> Self {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
unsafe {
Self(self.0)
}
}
}

#[$stability]
impl PartialEq for $Ty {
#[inline]
fn eq(&self, other: &Self) -> bool {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
unsafe {
self.0 == other.0
}
}

#[inline]
fn ne(&self, other: &Self) -> bool {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
unsafe {
self.0 != other.0
}
}
}

#[$stability]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl StructuralPartialEq for $Ty {}

#[$stability]
impl PartialOrd for $Ty {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
unsafe {
self.0.partial_cmp(&other.0)
}
}

#[inline]
fn lt(&self, other: &Self) -> bool {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
unsafe {
self.0 < other.0
}
}

#[inline]
fn le(&self, other: &Self) -> bool {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
unsafe {
self.0 <= other.0
}
}

#[inline]
fn gt(&self, other: &Self) -> bool {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
unsafe {
self.0 > other.0
}
}

#[inline]
fn ge(&self, other: &Self) -> bool {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
unsafe {
self.0 >= other.0
}
}
}

#[$stability]
impl Ord for $Ty {
fn cmp(&self, other: &Self) -> Ordering {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
unsafe {
self.0.cmp(&other.0)
}
}

fn max(self, other: Self) -> Self {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values,
// and the maximum of two non-zero values is still non-zero.
unsafe {
Self(self.0.max(other.0))
}
}

fn min(self, other: Self) -> Self {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values,
// and the minimum of two non-zero values is still non-zero.
unsafe {
Self(self.0.min(other.0))
}
}

fn clamp(self, min: Self, max: Self) -> Self {
#[allow(unused_unsafe)]
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values, and
// a non-zero value clamped between two non-zero values is still non-zero.
unsafe {
Self(self.0.clamp(min.0, max.0))
}
}
}

#[$stability]
impl Hash for $Ty {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
// SAFETY: A `NonZero` is guaranteed to only contain primitive non-zero values.
#[allow(unused_unsafe)]
unsafe {
self.0.hash(state)
}
}
}
};
}

macro_rules! impl_nonzero_fmt {
( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => {
$(
Expand Down Expand Up @@ -128,13 +292,15 @@ macro_rules! nonzero_integer {
///
/// [null pointer optimization]: crate::option#representation
#[$stability]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Copy, Eq)]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
#[rustc_diagnostic_item = stringify!($Ty)]
pub struct $Ty($Int);

impl_nonzero_traits!(#[$stability] $Ty);

impl $Ty {
/// Creates a non-zero without checking whether the value is non-zero.
/// This results in undefined behaviour if the value is zero.
Expand Down
1 change: 1 addition & 0 deletions tests/ui/unsafe/ranged_ints3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::cell::Cell;
#[rustc_layout_scalar_valid_range_start(1)]
#[repr(transparent)]
pub(crate) struct NonZero<T>(pub(crate) T);

fn main() {
let mut x = unsafe { NonZero(Cell::new(1)) };
let y = &x.0; //~ ERROR borrow of layout constrained field with interior mutability
Expand Down
1 change: 1 addition & 0 deletions tests/ui/unsafe/ranged_ints3_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::cell::Cell;
#[rustc_layout_scalar_valid_range_start(1)]
#[repr(transparent)]
pub(crate) struct NonZero<T>(pub(crate) T);

fn main() {
let mut x = unsafe { NonZero(Cell::new(1)) };
match x {
Expand Down

0 comments on commit 9aa4830

Please sign in to comment.