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`.
This commit is contained in:
parent
4396ceca05
commit
1de2257c3f
@ -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
|
||||
|
@ -221,7 +221,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
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!(
|
||||
|
@ -1505,6 +1505,7 @@
|
||||
transmute_generic_consts,
|
||||
transmute_opts,
|
||||
transmute_trait,
|
||||
transmute_unchecked,
|
||||
transparent,
|
||||
transparent_enums,
|
||||
transparent_unions,
|
||||
|
@ -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 @@ fn into_iter(self) -> Self::IntoIter {
|
||||
// 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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1376,6 +1376,20 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
|
||||
#[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(crate) fn is_nonoverlapping<T>(src: *const T, dst: *const T, 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)) }
|
||||
}
|
||||
|
@ -945,14 +945,10 @@ pub unsafe fn assume_init_drop(&mut self) {
|
||||
// * `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.
|
||||
|
@ -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 @@ enum Never {}
|
||||
|
||||
// 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!{
|
||||
|
Loading…
Reference in New Issue
Block a user