diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2023-04-24 07:53:25 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-24 07:53:25 +0200 |
| commit | 3ecae2932ccba4744a70da52dfb0930bcf8e902a (patch) | |
| tree | bcab72d307cf760d83a5be21f62612866bcbad4f | |
| parent | 775682dc5e63fec7a062e84a32aa740ca5c45f0b (diff) | |
| parent | 1de2257c3f6579028f2b8d97908ba12896abca61 (diff) | |
| download | rust-3ecae2932ccba4744a70da52dfb0930bcf8e902a.tar.gz | |
Rollup merge of #110706 - scottmcm:transmute_unchecked, r=oli-obk
Add `intrinsics::transmute_unchecked`
This takes a whole 3 lines in `compiler/` since it lowers to `CastKind::Transmute` in MIR *exactly* the same as the existing `intrinsics::transmute` does, it just doesn't have the fancy checking in `hir_typeck`.
Added to enable experimenting with the request in <https://github.com/rust-lang/rust/pull/106281#issuecomment-1496648190> and because the portable-simd folks might be interested for dependently-sized array-vector conversions.
It also simplifies a couple places in `core`.
See also https://github.com/rust-lang/rust/pull/108442#issuecomment-1474777273, where `CastKind::Transmute` was added having exactly these semantics before the lang meeting (which I wasn't in) independently expressed interest.
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/intrinsic.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/lower_intrinsics.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_span/src/symbol.rs | 1 | ||||
| -rw-r--r-- | library/core/src/array/iter.rs | 18 | ||||
| -rw-r--r-- | library/core/src/intrinsics.rs | 22 | ||||
| -rw-r--r-- | library/core/src/mem/maybe_uninit.rs | 10 | ||||
| -rw-r--r-- | tests/codegen/intrinsics/transmute.rs | 42 |
7 files changed, 43 insertions, 54 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 854974d1605..0fcbaa2efab 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -198,7 +198,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => (1, Vec::new(), tcx.mk_unit()), sym::forget => (1, vec![param(0)], tcx.mk_unit()), - sym::transmute => (2, vec![param(0)], param(1)), + sym::transmute | sym::transmute_unchecked => (2, vec![param(0)], param(1)), sym::prefetch_read_data | sym::prefetch_write_data | sym::prefetch_read_instruction diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index c136642dff2..c7d3f6c9f04 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -221,7 +221,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Goto { target }; } } - sym::transmute => { + sym::transmute | sym::transmute_unchecked => { let dst_ty = destination.ty(local_decls, tcx).ty; let Ok([arg]) = <[_; 1]>::try_from(std::mem::take(args)) else { span_bug!( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9891915d076..70b9088de50 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1505,6 +1505,7 @@ symbols! { transmute_generic_consts, transmute_opts, transmute_trait, + transmute_unchecked, transparent, transparent_enums, transparent_unions, diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 73e2c2cfbbe..587877dff55 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -3,8 +3,9 @@ use crate::num::NonZeroUsize; use crate::{ fmt, + intrinsics::transmute_unchecked, iter::{self, ExactSizeIterator, FusedIterator, TrustedLen}, - mem::{self, MaybeUninit}, + mem::MaybeUninit, ops::{IndexRange, Range}, ptr, }; @@ -63,18 +64,11 @@ impl<T, const N: usize> IntoIterator for [T; N] { // an array of `T`. // // With that, this initialization satisfies the invariants. - - // FIXME(LukasKalbertodt): actually use `mem::transmute` here, once it - // works with const generics: - // `mem::transmute::<[T; N], [MaybeUninit<T>; N]>(array)` // - // Until then, we can use `mem::transmute_copy` to create a bitwise copy - // as a different type, then forget `array` so that it is not dropped. - unsafe { - let iter = IntoIter { data: mem::transmute_copy(&self), alive: IndexRange::zero_to(N) }; - mem::forget(self); - iter - } + // FIXME: If normal `transmute` ever gets smart enough to allow this + // directly, use it instead of `transmute_unchecked`. + let data: [MaybeUninit<T>; N] = unsafe { transmute_unchecked(self) }; + IntoIter { data, alive: IndexRange::zero_to(N) } } } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index ba03da411e3..39edfd8265b 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1376,6 +1376,20 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn transmute<Src, Dst>(src: Src) -> Dst; + /// Like [`transmute`], but even less checked at compile-time: rather than + /// giving an error for `size_of::<Src>() != size_of::<Dst>()`, it's + /// **Undefined Behaviour** at runtime. + /// + /// Prefer normal `transmute` where possible, for the extra checking, since + /// both do exactly the same thing at runtime, if they both compile. + /// + /// This is not expected to ever be exposed directly to users, rather it + /// may eventually be exposed through some more-constrained API. + #[cfg(not(bootstrap))] + #[rustc_const_stable(feature = "const_transmute", since = "1.56.0")] + #[rustc_nounwind] + pub fn transmute_unchecked<Src, Dst>(src: Src) -> Dst; + /// Returns `true` if the actual type given as `T` requires drop /// glue; returns `false` if the actual type provided for `T` /// implements `Copy`. @@ -2798,3 +2812,11 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) { write_bytes(dst, val, count) } } + +/// Polyfill for bootstrap +#[cfg(bootstrap)] +pub const unsafe fn transmute_unchecked<Src, Dst>(src: Src) -> Dst { + use crate::mem::*; + // SAFETY: It's a transmute -- the caller promised it's fine. + unsafe { transmute_copy(&ManuallyDrop::new(src)) } +} diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 9c6d48675a6..2588b7d2bef 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -945,14 +945,10 @@ impl<T> MaybeUninit<T> { // * `MaybeUninit<T>` and T are guaranteed to have the same layout // * `MaybeUninit` does not drop, so there are no double-frees // And thus the conversion is safe - let ret = unsafe { + unsafe { intrinsics::assert_inhabited::<[T; N]>(); - (&array as *const _ as *const [T; N]).read() - }; - - // FIXME: required to avoid `~const Destruct` bound - super::forget(array); - ret + intrinsics::transmute_unchecked(array) + } } /// Assuming all the elements are initialized, get a slice to them. diff --git a/tests/codegen/intrinsics/transmute.rs b/tests/codegen/intrinsics/transmute.rs index 51c000b82ea..664e697c2a5 100644 --- a/tests/codegen/intrinsics/transmute.rs +++ b/tests/codegen/intrinsics/transmute.rs @@ -8,10 +8,10 @@ #![feature(inline_const)] #![allow(unreachable_code)] -use std::mem::{transmute, MaybeUninit}; +use std::mem::MaybeUninit; +use std::intrinsics::{transmute, transmute_unchecked}; -// Some of the cases here are statically rejected by `mem::transmute`, so -// we need to generate custom MIR for those cases to get to codegen. +// Some of these need custom MIR to not get removed by MIR optimizations. use std::intrinsics::mir::*; enum Never {} @@ -30,59 +30,35 @@ pub struct Aggregate8(u8); // CHECK-LABEL: @check_bigger_size( #[no_mangle] -#[custom_mir(dialect = "runtime", phase = "initial")] pub unsafe fn check_bigger_size(x: u16) -> u32 { // CHECK: call void @llvm.trap - mir!{ - { - RET = CastTransmute(x); - Return() - } - } + transmute_unchecked(x) } // CHECK-LABEL: @check_smaller_size( #[no_mangle] -#[custom_mir(dialect = "runtime", phase = "initial")] pub unsafe fn check_smaller_size(x: u32) -> u16 { // CHECK: call void @llvm.trap - mir!{ - { - RET = CastTransmute(x); - Return() - } - } + transmute_unchecked(x) } // CHECK-LABEL: @check_smaller_array( #[no_mangle] -#[custom_mir(dialect = "runtime", phase = "initial")] pub unsafe fn check_smaller_array(x: [u32; 7]) -> [u32; 3] { // CHECK: call void @llvm.trap - mir!{ - { - RET = CastTransmute(x); - Return() - } - } + transmute_unchecked(x) } // CHECK-LABEL: @check_bigger_array( #[no_mangle] -#[custom_mir(dialect = "runtime", phase = "initial")] pub unsafe fn check_bigger_array(x: [u32; 3]) -> [u32; 7] { // CHECK: call void @llvm.trap - mir!{ - { - RET = CastTransmute(x); - Return() - } - } + transmute_unchecked(x) } // CHECK-LABEL: @check_to_uninhabited( #[no_mangle] -#[custom_mir(dialect = "runtime", phase = "initial")] +#[custom_mir(dialect = "runtime", phase = "optimized")] pub unsafe fn check_to_uninhabited(x: u16) -> BigNever { // CHECK: call void @llvm.trap mir!{ @@ -95,7 +71,7 @@ pub unsafe fn check_to_uninhabited(x: u16) -> BigNever { // CHECK-LABEL: @check_from_uninhabited( #[no_mangle] -#[custom_mir(dialect = "runtime", phase = "initial")] +#[custom_mir(dialect = "runtime", phase = "optimized")] pub unsafe fn check_from_uninhabited(x: BigNever) -> u16 { // CHECK: ret i16 poison mir!{ |
