diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 28a44b09de2b1..eadac22e6526f 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1856,6 +1856,29 @@ pub enum SizeSkeleton<'tcx> { /// depending on one, with regions erased. tail: Ty<'tcx>, }, + + /// A value whose size would be statically known after monomorphization, but may not yet be + /// knowable. The size must either be exactly one instance of a type, or an array of instances + /// of a type, because this variant isn't aware of other layout constraints such as padding. + /// + /// This can be useful for comparing two types which can be known to be the same size because, + /// though individually their sizes aren't known, they can be easily proven to have the same + /// size. For instance, checking whether two arrays of the same type parameter have the same + /// size (even though the type parameter's size may not yet be known), or for comparing the + /// size of a value of a type parameter with a repr(transaprent) type which contains a value of + /// the same type parameter. + /// + /// This is, however, currently very limited and conservative. For instance, two arrays of the + /// same type, where: + /// * one's size is a const generic parameter + /// * the other's is an const value which happens to be equal to that const generic parameter + /// will not be considered equal. + RepeatedTy { + /// The type of which this size is a known multiple (e.g. in an array). + ty: Ty<'tcx>, + /// How many copies of that type are stored. + repetitions: &'tcx ty::Const<'tcx>, + }, } impl<'tcx> SizeSkeleton<'tcx> { @@ -1906,7 +1929,7 @@ impl<'tcx> SizeSkeleton<'tcx> { .fields .iter() .map(|field| SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env)); - let mut ptr = None; + let mut ptr_or_transparent = None; for field in fields { let field = field?; match field { @@ -1916,33 +1939,54 @@ impl<'tcx> SizeSkeleton<'tcx> { } } SizeSkeleton::Pointer { .. } => { - if ptr.is_some() { + if ptr_or_transparent.is_some() { + return Err(err); + } + ptr_or_transparent = Some(field); + } + SizeSkeleton::RepeatedTy { .. } => { + if ptr_or_transparent.is_some() { + return Err(err); + } else if def.repr.transparent() { + ptr_or_transparent = Some(field); + } else { return Err(err); } - ptr = Some(field); } } } - Ok(ptr) + Ok(ptr_or_transparent) }; let v0 = zero_or_ptr_variant(0)?; // Newtype. if def.variants.len() == 1 { - if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 { - return Ok(SizeSkeleton::Pointer { - non_zero: non_zero - || match tcx.layout_scalar_valid_range(def.did) { - (Bound::Included(start), Bound::Unbounded) => start > 0, - (Bound::Included(start), Bound::Included(end)) => { - 0 < start && start < end - } - _ => false, - }, - tail, - }); - } else { - return Err(err); + match v0 { + Some(SizeSkeleton::Pointer { non_zero, tail }) => { + return Ok(SizeSkeleton::Pointer { + non_zero: non_zero + || match tcx.layout_scalar_valid_range(def.did) { + (Bound::Included(start), Bound::Unbounded) => start > 0, + (Bound::Included(start), Bound::Included(end)) => { + 0 < start && start < end + } + _ => false, + }, + tail, + }); + } + Some(SizeSkeleton::RepeatedTy { + ty: related_ty, + repetitions: multiple, + }) => { + return Ok(SizeSkeleton::RepeatedTy { + ty: related_ty, + repetitions: multiple, + }); + } + _ => { + return Err(err); + } } } @@ -1966,6 +2010,26 @@ impl<'tcx> SizeSkeleton<'tcx> { } } + ty::Array(array_ty, size) => { + if array_ty.is_sized(tcx.at(DUMMY_SP), param_env) { + Ok(SizeSkeleton::RepeatedTy { ty: array_ty, repetitions: size }) + } else { + Err(err) + } + } + + ty::Param(param_ty) => { + let param_ty = param_ty.to_ty(tcx); + if param_ty.is_sized(tcx.at(DUMMY_SP), param_env) { + Ok(SizeSkeleton::RepeatedTy { + ty: param_ty, + repetitions: ty::Const::from_usize(tcx, 1), + }) + } else { + Err(err) + } + } + _ => Err(err), } } @@ -1976,6 +2040,10 @@ impl<'tcx> SizeSkeleton<'tcx> { (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => { a == b } + ( + SizeSkeleton::RepeatedTy { ty: related_ty, repetitions: multiple }, + SizeSkeleton::RepeatedTy { ty: other_related_ty, repetitions: other_multiple }, + ) => related_ty == other_related_ty && multiple == other_multiple, _ => false, } } diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index 012d97ef106c7..a48bab7d0fdc9 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -97,6 +97,9 @@ impl ExprVisitor<'tcx> { let skeleton_string = |ty: Ty<'tcx>, sk| match sk { Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()), Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{}`", tail), + Ok(SizeSkeleton::RepeatedTy { ty: related_ty, repetitions: _ }) => { + format!("size can vary because of {}", related_ty) + } Err(LayoutError::Unknown(bad)) => { if bad == ty { "this type does not have a fixed size".to_owned() diff --git a/src/test/ui/transmute/transmute-related-type-parameters-with-arrays.rs b/src/test/ui/transmute/transmute-related-type-parameters-with-arrays.rs new file mode 100644 index 0000000000000..81812eb7d6928 --- /dev/null +++ b/src/test/ui/transmute/transmute-related-type-parameters-with-arrays.rs @@ -0,0 +1,51 @@ +// Tests that `transmute` can be called in simple cases on types using type parameters with arrays. + +// run-pass + +use std::mem::transmute; + +#[repr(transparent)] +struct Wrapper([T; 10]); + +#[repr(transparent)] +struct OtherWrapper([T; 10]); + +#[repr(transparent)] +struct ArbitrarySizedWrapper([T; N]); + +fn wrap(unwrapped: [T; 10]) -> Wrapper { + unsafe { + transmute(unwrapped) + } +} + +fn rewrap(wrapped: Wrapper) -> OtherWrapper { + unsafe { + transmute(wrapped) + } +} + +fn unwrap(wrapped: OtherWrapper) -> [T; 10] { + unsafe { + transmute(wrapped) + } +} + +fn wrap_arbitrary_size(arr: [T; N]) -> ArbitrarySizedWrapper { + unsafe { transmute(arr) } +} + +fn main() { + let unwrapped = [5_u64; 10]; + let wrapped = wrap(unwrapped); + assert_eq!([5_u64; 10], wrapped.0); + + let rewrapped = rewrap(wrapped); + assert_eq!([5_u64; 10], rewrapped.0); + + let unwrapped = unwrap(rewrapped); + assert_eq!([5_u64; 10], unwrapped); + + let arbitrary_sized_wrapper = wrap_arbitrary_size(unwrapped); + assert_eq!([5_u64; 10], arbitrary_sized_wrapper.0); +} diff --git a/src/test/ui/transmute/transmute-related-type-parameters.rs b/src/test/ui/transmute/transmute-related-type-parameters.rs new file mode 100644 index 0000000000000..c32e2fdf060a3 --- /dev/null +++ b/src/test/ui/transmute/transmute-related-type-parameters.rs @@ -0,0 +1,29 @@ +// Tests that `transmute` can be called in simple cases on types using type parameters. + +// run-pass + +use std::mem::transmute; + +#[repr(transparent)] +struct Wrapper(T); + +fn wrap(unwrapped: T) -> Wrapper { + unsafe { + transmute(unwrapped) + } +} + +fn unwrap(wrapped: Wrapper) -> T { + unsafe { + transmute(wrapped) + } +} + +fn main() { + let unwrapped = 5_u64; + let wrapped = wrap(unwrapped); + assert_eq!(5_u64, wrapped.0); + + let unwrapped = unwrap(wrapped); + assert_eq!(5_u64, unwrapped); +} diff --git a/src/test/ui/transmute/transmute-type-parameters.rs b/src/test/ui/transmute/transmute-type-parameters.rs index 5f44b2d0f0163..78a0a59cf8178 100644 --- a/src/test/ui/transmute/transmute-type-parameters.rs +++ b/src/test/ui/transmute/transmute-type-parameters.rs @@ -1,4 +1,4 @@ -// Tests that `transmute` cannot be called on type parameters. +// Tests that `transmute` cannot be called on arbitrary type parameters. use std::mem::transmute; diff --git a/src/test/ui/transmute/transmute-type-parameters.stderr b/src/test/ui/transmute/transmute-type-parameters.stderr index 220b929d4fd2e..8a99bb558eba7 100644 --- a/src/test/ui/transmute/transmute-type-parameters.stderr +++ b/src/test/ui/transmute/transmute-type-parameters.stderr @@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | let _: i32 = transmute(x); | ^^^^^^^^^ | - = note: source type: `T` (this type does not have a fixed size) + = note: source type: `T` (size can vary because of T) = note: target type: `i32` (32 bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types