about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs2
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/array/iter.rs18
-rw-r--r--library/core/src/intrinsics.rs22
-rw-r--r--library/core/src/mem/maybe_uninit.rs10
-rw-r--r--tests/codegen/intrinsics/transmute.rs42
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!{