Skip to content

Commit

Permalink
Rollup merge of rust-lang#110706 - scottmcm:transmute_unchecked, r=ol…
Browse files Browse the repository at this point in the history
…i-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 <rust-lang#106281 (comment)> 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 rust-lang#108442 (comment), where `CastKind::Transmute` was added having exactly these semantics before the lang meeting (which I wasn't in) independently expressed interest.
  • Loading branch information
matthiaskrgr committed Apr 24, 2023
2 parents 775682d + 1de2257 commit 3ecae29
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 54 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/lower_intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1505,6 +1505,7 @@ symbols! {
transmute_generic_consts,
transmute_opts,
transmute_trait,
transmute_unchecked,
transparent,
transparent_enums,
transparent_unions,
Expand Down
18 changes: 6 additions & 12 deletions library/core/src/array/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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) }
}
}

Expand Down
22 changes: 22 additions & 0 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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)) }
}
10 changes: 3 additions & 7 deletions library/core/src/mem/maybe_uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
42 changes: 9 additions & 33 deletions tests/codegen/intrinsics/transmute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand All @@ -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!{
Expand All @@ -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!{
Expand Down

0 comments on commit 3ecae29

Please sign in to comment.