diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index d9ebdadf3b79c..f6dfae29a9d76 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -330,7 +330,7 @@ pub(crate) mod rustc { .fold(Tree::unit(), |tree, elt| tree.then(elt))) } - ty::Adt(adt_def, _args_ref) if !ty.is_box() => { + ty::Adt(adt_def, args_ref) if !ty.is_box() => { let (lo, hi) = cx.tcx().layout_scalar_valid_range(adt_def.did()); use core::ops::Bound::*; @@ -339,14 +339,43 @@ pub(crate) mod rustc { (AdtKind::Struct, Unbounded, Unbounded) => { Self::from_struct((ty, layout), *adt_def, cx) } - (AdtKind::Struct, Included(1), Included(_hi)) if is_transparent => { - // FIXME(@joshlf): Support `NonZero` types: - // - Check to make sure that the first field is - // numerical - // - Check to make sure that the upper bound is the - // maximum value for the field's type - // - Construct `Self::nonzero` - Err(Err::NotYetSupported) + (AdtKind::Struct, Included(1), hi_val) if is_transparent => { + let variant = adt_def.non_enum_variant(); + // For now, only support `repr(transparent)` types + // with a single field. Technically + // `repr(transparent)` also works with types with + // any number of zero-sized fields (in addition to + // their single non-zero-sized field), but no such + // types exist in the standard library which also + // use + // `#[rustc_layout_scalar_valid_range_(start|end)]`. + let [field] = &variant.fields.as_slice().raw else { + return Err(Err::NotYetSupported); + }; + + let field_ty = field.ty(cx.tcx(), args_ref); + + let field_layout = layout_of(cx, field_ty)?; + let field_size = field_layout.size; + + let max_value = (1u128 << field_size.bits()) - 1; + let hi_val = match hi_val { + Included(hi_val) => hi_val, + Unbounded => max_value, + Excluded(_) => return Err(Err::NotYetSupported), + }; + + if hi_val == max_value + && let ty::Uint(_) = *field_ty.kind() + { + Ok(Self::seq([ + Self::def(Def::Adt(adt_def.clone())), + Self::def(Def::Field(field)), + Self::nonzero(field_size.bytes()), + ])) + } else { + Err(Err::NotYetSupported) + } } (AdtKind::Enum, Unbounded, Unbounded) => { Self::from_enum((ty, layout), *adt_def, cx) diff --git a/tests/ui/transmutability/nonzero.rs b/tests/ui/transmutability/nonzero.rs new file mode 100644 index 0000000000000..1a43c0fe90c71 --- /dev/null +++ b/tests/ui/transmutability/nonzero.rs @@ -0,0 +1,43 @@ +#![feature(transmutability)] +#![feature(rustc_attrs)] +#![allow(dead_code)] + +mod assert { + use std::mem::{Assume, TransmuteFrom}; + + pub fn is_transmutable_assume_nothing() + where + Dst: TransmuteFrom, + { + } + + pub fn is_transmutable_assume_safety() + where + Dst: TransmuteFrom, + { + } +} + +fn main() { + // FIXME: Replace this with `core::num::NonZeroU8` once we support it. + #[rustc_layout_scalar_valid_range_start(1)] + #[repr(transparent)] + struct NonZeroU8(u8); + + // FIXME: Replace this with `core::num::NonZeroU16` once we support it. + #[rustc_layout_scalar_valid_range_start(1)] + #[repr(transparent)] + struct NonZeroU16(u16); + + assert::is_transmutable_assume_safety::(); //~ ERROR: cannot be safely transmuted + assert::is_transmutable_assume_safety::(); + + assert::is_transmutable_assume_nothing::(); //~ ERROR: cannot be safely transmuted + assert::is_transmutable_assume_safety::(); + + assert::is_transmutable_assume_safety::(); //~ ERROR: cannot be safely transmuted + assert::is_transmutable_assume_safety::(); + + assert::is_transmutable_assume_nothing::(); //~ ERROR: cannot be safely transmuted + assert::is_transmutable_assume_safety::(); +} diff --git a/tests/ui/transmutability/nonzero.stderr b/tests/ui/transmutability/nonzero.stderr new file mode 100644 index 0000000000000..227d31b43137d --- /dev/null +++ b/tests/ui/transmutability/nonzero.stderr @@ -0,0 +1,63 @@ +error[E0277]: `u8` cannot be safely transmuted into `NonZeroU8` + --> $DIR/nonzero.rs:32:49 + | +LL | assert::is_transmutable_assume_safety::(); + | ^^^^^^^^^ at least one value of `u8` isn't a bit-valid value of `NonZeroU8` + | +note: required by a bound in `is_transmutable_assume_safety` + --> $DIR/nonzero.rs:16:14 + | +LL | pub fn is_transmutable_assume_safety() + | ----------------------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable_assume_safety` + +error[E0277]: `NonZeroU8` cannot be safely transmuted into `NonZeroU8` + --> $DIR/nonzero.rs:35:57 + | +LL | assert::is_transmutable_assume_nothing::(); + | ^^^^^^^^^ `NonZeroU8` may carry safety invariants + | +note: required by a bound in `is_transmutable_assume_nothing` + --> $DIR/nonzero.rs:10:14 + | +LL | pub fn is_transmutable_assume_nothing() + | ------------------------------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable_assume_nothing` + +error[E0277]: `u16` cannot be safely transmuted into `NonZeroU16` + --> $DIR/nonzero.rs:38:50 + | +LL | assert::is_transmutable_assume_safety::(); + | ^^^^^^^^^^ at least one value of `u16` isn't a bit-valid value of `NonZeroU16` + | +note: required by a bound in `is_transmutable_assume_safety` + --> $DIR/nonzero.rs:16:14 + | +LL | pub fn is_transmutable_assume_safety() + | ----------------------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable_assume_safety` + +error[E0277]: `NonZeroU16` cannot be safely transmuted into `NonZeroU16` + --> $DIR/nonzero.rs:41:58 + | +LL | assert::is_transmutable_assume_nothing::(); + | ^^^^^^^^^^ `NonZeroU16` may carry safety invariants + | +note: required by a bound in `is_transmutable_assume_nothing` + --> $DIR/nonzero.rs:10:14 + | +LL | pub fn is_transmutable_assume_nothing() + | ------------------------------ required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable_assume_nothing` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`.