Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move generic NonZero rustc_layout_scalar_valid_range_start attribute to inner type. #121885

Merged
merged 14 commits into from
Mar 17, 2024
Merged
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
2 changes: 2 additions & 0 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2468,6 +2468,8 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
ty: Ty<'tcx>,
init: InitKind,
) -> Option<InitError> {
let ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);

use rustc_type_ir::TyKind::*;
match ty.kind() {
// Primitive types that don't like 0 as a value.
Expand Down
44 changes: 27 additions & 17 deletions compiler/rustc_lint/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,14 @@ pub fn transparent_newtype_field<'a, 'tcx>(
}

/// Is type known to be non-null?
fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
fn ty_is_known_nonnull<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
mode: CItemKind,
) -> bool {
let ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty);

match ty.kind() {
ty::FnPtr(_) => true,
ty::Ref(..) => true,
Expand All @@ -1004,15 +1011,21 @@ fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -
def.variants()
.iter()
.filter_map(|variant| transparent_newtype_field(tcx, variant))
.any(|field| ty_is_known_nonnull(tcx, field.ty(tcx, args), mode))
.any(|field| ty_is_known_nonnull(tcx, param_env, field.ty(tcx, args), mode))
}
_ => false,
}
}

/// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
/// If the type passed in was not scalar, returns None.
fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
fn get_nullable_type<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
let ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty);

Some(match *ty.kind() {
ty::Adt(field_def, field_args) => {
let inner_field_ty = {
Expand All @@ -1028,22 +1041,19 @@ fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>>
.expect("No non-zst fields in transparent type.")
.ty(tcx, field_args)
};
return get_nullable_type(tcx, inner_field_ty);
return get_nullable_type(tcx, param_env, inner_field_ty);
}
ty::Int(ty) => Ty::new_int(tcx, ty),
ty::Uint(ty) => Ty::new_uint(tcx, ty),
ty::RawPtr(ty_mut) => Ty::new_ptr(tcx, ty_mut),
// As these types are always non-null, the nullable equivalent of
// Option<T> of these types are their raw pointer counterparts.
// `Option<T>` of these types are their raw pointer counterparts.
ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl }),
ty::FnPtr(..) => {
// There is no nullable equivalent for Rust's function pointers -- you
// must use an Option<fn(..) -> _> to represent it.
ty
}

// We should only ever reach this case if ty_is_known_nonnull is extended
// to other types.
// There is no nullable equivalent for Rust's function pointers,
// you must use an `Option<fn(..) -> _>` to represent it.
ty::FnPtr(..) => ty,
// We should only ever reach this case if `ty_is_known_nonnull` is
// extended to other types.
ref unhandled => {
debug!(
"get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
Expand All @@ -1056,7 +1066,7 @@ fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>>

/// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
/// can, return the type that `ty` can be safely converted to, otherwise return `None`.
/// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`,
/// Currently restricted to function pointers, boxes, references, `core::num::NonZero`,
/// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
/// FIXME: This duplicates code in codegen.
pub(crate) fn repr_nullable_ptr<'tcx>(
Expand All @@ -1075,7 +1085,7 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
_ => return None,
};

if !ty_is_known_nonnull(tcx, field_ty, ckind) {
if !ty_is_known_nonnull(tcx, param_env, field_ty, ckind) {
return None;
}

Expand All @@ -1099,10 +1109,10 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
WrappingRange { start: 0, end }
if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
{
return Some(get_nullable_type(tcx, field_ty).unwrap());
return Some(get_nullable_type(tcx, param_env, field_ty).unwrap());
}
WrappingRange { start: 1, .. } => {
return Some(get_nullable_type(tcx, field_ty).unwrap());
return Some(get_nullable_type(tcx, param_env, field_ty).unwrap());
}
WrappingRange { start, end } => {
unreachable!("Unhandled start and end range: ({}, {})", start, end)
Expand Down
149 changes: 98 additions & 51 deletions library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,15 @@ use crate::cmp::Ordering;
use crate::fmt;
use crate::hash::{Hash, Hasher};
use crate::intrinsics;
use crate::marker::StructuralPartialEq;
use crate::marker::{Freeze, StructuralPartialEq};
use crate::ops::{BitOr, BitOrAssign, Div, Neg, Rem};
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::ptr;
use crate::str::FromStr;

use super::from_str_radix;
use super::{IntErrorKind, ParseIntError};

mod private {
#[unstable(
feature = "nonzero_internals",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "none"
)]
#[const_trait]
pub trait Sealed {}
}

/// A marker trait for primitive types which can be zero.
///
/// This is an implementation detail for <code>[NonZero]\<T></code> which may disappear or be replaced at any time.
Expand All @@ -34,38 +26,70 @@ mod private {
issue = "none"
)]
#[const_trait]
pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed {}
pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed {
#[doc(hidden)]
type NonZeroInner: Sized + Copy;
}

macro_rules! impl_zeroable_primitive {
($primitive:ty) => {
#[unstable(
feature = "nonzero_internals",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "none"
)]
impl const private::Sealed for $primitive {}

#[unstable(
feature = "nonzero_internals",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "none"
)]
unsafe impl const ZeroablePrimitive for $primitive {}
($($NonZeroInner:ident ( $primitive:ty )),+ $(,)?) => {
mod private {
#[unstable(
feature = "nonzero_internals",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "none"
)]
#[const_trait]
pub trait Sealed {}

$(
#[derive(Debug, Clone, Copy, PartialEq)]
#[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);
)+
}

$(
#[unstable(
feature = "nonzero_internals",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "none"
)]
impl const private::Sealed for $primitive {}

#[unstable(
feature = "nonzero_internals",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "none"
)]
unsafe impl const ZeroablePrimitive for $primitive {
type NonZeroInner = private::$NonZeroInner;
}
)+
};
}

impl_zeroable_primitive!(u8);
impl_zeroable_primitive!(u16);
impl_zeroable_primitive!(u32);
impl_zeroable_primitive!(u64);
impl_zeroable_primitive!(u128);
impl_zeroable_primitive!(usize);
impl_zeroable_primitive!(i8);
impl_zeroable_primitive!(i16);
impl_zeroable_primitive!(i32);
impl_zeroable_primitive!(i64);
impl_zeroable_primitive!(i128);
impl_zeroable_primitive!(isize);
impl_zeroable_primitive!(
NonZeroU8Inner(u8),
NonZeroU16Inner(u16),
NonZeroU32Inner(u32),
NonZeroU64Inner(u64),
NonZeroU128Inner(u128),
NonZeroUsizeInner(usize),
NonZeroI8Inner(i8),
NonZeroI16Inner(i16),
NonZeroI32Inner(i32),
NonZeroI64Inner(i64),
NonZeroI128Inner(i128),
NonZeroIsizeInner(isize),
);

/// A value that is known not to equal zero.
///
Expand All @@ -80,10 +104,9 @@ impl_zeroable_primitive!(isize);
/// ```
#[unstable(feature = "generic_nonzero", issue = "120257")]
#[repr(transparent)]
#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
reitermarkus marked this conversation as resolved.
Show resolved Hide resolved
#[rustc_diagnostic_item = "NonZero"]
pub struct NonZero<T: ZeroablePrimitive>(T);
pub struct NonZero<T: ZeroablePrimitive>(T::NonZeroInner);

macro_rules! impl_nonzero_fmt {
($Trait:ident) => {
Expand All @@ -107,15 +130,34 @@ impl_nonzero_fmt!(Octal);
impl_nonzero_fmt!(LowerHex);
impl_nonzero_fmt!(UpperHex);

macro_rules! impl_nonzero_auto_trait {
(unsafe $Trait:ident) => {
#[stable(feature = "nonzero", since = "1.28.0")]
unsafe impl<T> $Trait for NonZero<T> where T: ZeroablePrimitive + $Trait {}
};
($Trait:ident) => {
#[stable(feature = "nonzero", since = "1.28.0")]
impl<T> $Trait for NonZero<T> where T: ZeroablePrimitive + $Trait {}
};
}

// Implement auto-traits manually based on `T` to avoid docs exposing
// the `ZeroablePrimitive::NonZeroInner` implementation detail.
impl_nonzero_auto_trait!(unsafe Freeze);
impl_nonzero_auto_trait!(RefUnwindSafe);
impl_nonzero_auto_trait!(unsafe Send);
impl_nonzero_auto_trait!(unsafe Sync);
impl_nonzero_auto_trait!(Unpin);
impl_nonzero_auto_trait!(UnwindSafe);

#[stable(feature = "nonzero", since = "1.28.0")]
impl<T> Clone for NonZero<T>
where
T: ZeroablePrimitive,
{
#[inline]
fn clone(&self) -> Self {
// SAFETY: The contained value is non-zero.
unsafe { Self(self.0) }
Self(self.0)
}
}

Expand Down Expand Up @@ -188,19 +230,19 @@ where
#[inline]
fn max(self, other: Self) -> Self {
// SAFETY: The maximum of two non-zero values is still non-zero.
unsafe { Self(self.get().max(other.get())) }
unsafe { Self::new_unchecked(self.get().max(other.get())) }
}

#[inline]
fn min(self, other: Self) -> Self {
// SAFETY: The minimum of two non-zero values is still non-zero.
unsafe { Self(self.get().min(other.get())) }
unsafe { Self::new_unchecked(self.get().min(other.get())) }
}

#[inline]
fn clamp(self, min: Self, max: Self) -> Self {
// SAFETY: A non-zero value clamped between two non-zero values is still non-zero.
unsafe { Self(self.get().clamp(min.get(), max.get())) }
unsafe { Self::new_unchecked(self.get().clamp(min.get(), max.get())) }
}
}

Expand Down Expand Up @@ -240,7 +282,7 @@ where
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
// SAFETY: Bitwise OR of two non-zero values is still non-zero.
unsafe { Self(self.get() | rhs.get()) }
unsafe { Self::new_unchecked(self.get() | rhs.get()) }
}
}

Expand All @@ -254,7 +296,7 @@ where
#[inline]
fn bitor(self, rhs: T) -> Self::Output {
// SAFETY: Bitwise OR of a non-zero value with anything is still non-zero.
unsafe { Self(self.get() | rhs) }
unsafe { Self::new_unchecked(self.get() | rhs) }
}
}

Expand All @@ -268,7 +310,7 @@ where
#[inline]
fn bitor(self, rhs: NonZero<T>) -> Self::Output {
// SAFETY: Bitwise OR of anything with a non-zero value is still non-zero.
unsafe { NonZero(self | rhs.get()) }
unsafe { NonZero::new_unchecked(self | rhs.get()) }
}
}

Expand Down Expand Up @@ -346,7 +388,7 @@ where
pub fn from_mut(n: &mut T) -> Option<&mut Self> {
// SAFETY: Memory layout optimization guarantees that `Option<NonZero<T>>` has
// the same layout and size as `T`, with `0` representing `None`.
let opt_n = unsafe { &mut *(n as *mut T as *mut Option<Self>) };
let opt_n = unsafe { &mut *(ptr::from_mut(n).cast::<Option<Self>>()) };

opt_n.as_mut()
}
Expand Down Expand Up @@ -390,12 +432,17 @@ where
// memory somewhere. If the value of `self` was from by-value argument
// of some not-inlined function, LLVM don't have range metadata
// to understand that the value cannot be zero.
match Self::new(self.0) {
Some(Self(n)) => n,
//
// SAFETY: `Self` is guaranteed to have the same layout as `Option<Self>`.
match unsafe { intrinsics::transmute_unchecked(self) } {
None => {
// SAFETY: `NonZero` is guaranteed to only contain non-zero values, so this is unreachable.
unsafe { intrinsics::unreachable() }
}
Some(Self(inner)) => {
// SAFETY: `T::NonZeroInner` is guaranteed to have the same layout as `T`.
unsafe { intrinsics::transmute_unchecked(inner) }
}
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/etc/gdb_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,14 @@ def __init__(self, valobj):
fields = valobj.type.fields()
assert len(fields) == 1
field = list(fields)[0]
self._value = str(valobj[field.name])

inner_valobj = valobj[field.name]

inner_fields = inner_valobj.type.fields()
assert len(inner_fields) == 1
inner_field = list(inner_fields)[0]

self._value = str(inner_valobj[inner_field.name])

def to_string(self):
return self._value
Expand Down
2 changes: 1 addition & 1 deletion src/etc/lldb_commands
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)C
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)Ref<.+>$" --category Rust
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust
type summary add -F lldb_lookup.summary_lookup -e -x -h "^core::num::([a-z_]+::)*NonZero.+$" --category Rust
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust
type category enable Rust
Loading
Loading