Skip to content

Commit 3ecae29

Browse files
authored
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 <#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 #108442 (comment), where `CastKind::Transmute` was added having exactly these semantics before the lang meeting (which I wasn't in) independently expressed interest.
2 parents 775682d + 1de2257 commit 3ecae29

File tree

7 files changed

+43
-54
lines changed

7 files changed

+43
-54
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
198198
| sym::assert_zero_valid
199199
| sym::assert_mem_uninitialized_valid => (1, Vec::new(), tcx.mk_unit()),
200200
sym::forget => (1, vec![param(0)], tcx.mk_unit()),
201-
sym::transmute => (2, vec![param(0)], param(1)),
201+
sym::transmute | sym::transmute_unchecked => (2, vec![param(0)], param(1)),
202202
sym::prefetch_read_data
203203
| sym::prefetch_write_data
204204
| sym::prefetch_read_instruction

compiler/rustc_mir_transform/src/lower_intrinsics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
221221
terminator.kind = TerminatorKind::Goto { target };
222222
}
223223
}
224-
sym::transmute => {
224+
sym::transmute | sym::transmute_unchecked => {
225225
let dst_ty = destination.ty(local_decls, tcx).ty;
226226
let Ok([arg]) = <[_; 1]>::try_from(std::mem::take(args)) else {
227227
span_bug!(

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,7 @@ symbols! {
15051505
transmute_generic_consts,
15061506
transmute_opts,
15071507
transmute_trait,
1508+
transmute_unchecked,
15081509
transparent,
15091510
transparent_enums,
15101511
transparent_unions,

library/core/src/array/iter.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
use crate::num::NonZeroUsize;
44
use crate::{
55
fmt,
6+
intrinsics::transmute_unchecked,
67
iter::{self, ExactSizeIterator, FusedIterator, TrustedLen},
7-
mem::{self, MaybeUninit},
8+
mem::MaybeUninit,
89
ops::{IndexRange, Range},
910
ptr,
1011
};
@@ -63,18 +64,11 @@ impl<T, const N: usize> IntoIterator for [T; N] {
6364
// an array of `T`.
6465
//
6566
// With that, this initialization satisfies the invariants.
66-
67-
// FIXME(LukasKalbertodt): actually use `mem::transmute` here, once it
68-
// works with const generics:
69-
// `mem::transmute::<[T; N], [MaybeUninit<T>; N]>(array)`
7067
//
71-
// Until then, we can use `mem::transmute_copy` to create a bitwise copy
72-
// as a different type, then forget `array` so that it is not dropped.
73-
unsafe {
74-
let iter = IntoIter { data: mem::transmute_copy(&self), alive: IndexRange::zero_to(N) };
75-
mem::forget(self);
76-
iter
77-
}
68+
// FIXME: If normal `transmute` ever gets smart enough to allow this
69+
// directly, use it instead of `transmute_unchecked`.
70+
let data: [MaybeUninit<T>; N] = unsafe { transmute_unchecked(self) };
71+
IntoIter { data, alive: IndexRange::zero_to(N) }
7872
}
7973
}
8074

library/core/src/intrinsics.rs

+22
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,20 @@ extern "rust-intrinsic" {
13761376
#[rustc_nounwind]
13771377
pub fn transmute<Src, Dst>(src: Src) -> Dst;
13781378

1379+
/// Like [`transmute`], but even less checked at compile-time: rather than
1380+
/// giving an error for `size_of::<Src>() != size_of::<Dst>()`, it's
1381+
/// **Undefined Behaviour** at runtime.
1382+
///
1383+
/// Prefer normal `transmute` where possible, for the extra checking, since
1384+
/// both do exactly the same thing at runtime, if they both compile.
1385+
///
1386+
/// This is not expected to ever be exposed directly to users, rather it
1387+
/// may eventually be exposed through some more-constrained API.
1388+
#[cfg(not(bootstrap))]
1389+
#[rustc_const_stable(feature = "const_transmute", since = "1.56.0")]
1390+
#[rustc_nounwind]
1391+
pub fn transmute_unchecked<Src, Dst>(src: Src) -> Dst;
1392+
13791393
/// Returns `true` if the actual type given as `T` requires drop
13801394
/// glue; returns `false` if the actual type provided for `T`
13811395
/// implements `Copy`.
@@ -2798,3 +2812,11 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
27982812
write_bytes(dst, val, count)
27992813
}
28002814
}
2815+
2816+
/// Polyfill for bootstrap
2817+
#[cfg(bootstrap)]
2818+
pub const unsafe fn transmute_unchecked<Src, Dst>(src: Src) -> Dst {
2819+
use crate::mem::*;
2820+
// SAFETY: It's a transmute -- the caller promised it's fine.
2821+
unsafe { transmute_copy(&ManuallyDrop::new(src)) }
2822+
}

library/core/src/mem/maybe_uninit.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -945,14 +945,10 @@ impl<T> MaybeUninit<T> {
945945
// * `MaybeUninit<T>` and T are guaranteed to have the same layout
946946
// * `MaybeUninit` does not drop, so there are no double-frees
947947
// And thus the conversion is safe
948-
let ret = unsafe {
948+
unsafe {
949949
intrinsics::assert_inhabited::<[T; N]>();
950-
(&array as *const _ as *const [T; N]).read()
951-
};
952-
953-
// FIXME: required to avoid `~const Destruct` bound
954-
super::forget(array);
955-
ret
950+
intrinsics::transmute_unchecked(array)
951+
}
956952
}
957953

958954
/// Assuming all the elements are initialized, get a slice to them.

tests/codegen/intrinsics/transmute.rs

+9-33
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
#![feature(inline_const)]
99
#![allow(unreachable_code)]
1010

11-
use std::mem::{transmute, MaybeUninit};
11+
use std::mem::MaybeUninit;
12+
use std::intrinsics::{transmute, transmute_unchecked};
1213

13-
// Some of the cases here are statically rejected by `mem::transmute`, so
14-
// we need to generate custom MIR for those cases to get to codegen.
14+
// Some of these need custom MIR to not get removed by MIR optimizations.
1515
use std::intrinsics::mir::*;
1616

1717
enum Never {}
@@ -30,59 +30,35 @@ pub struct Aggregate8(u8);
3030

3131
// CHECK-LABEL: @check_bigger_size(
3232
#[no_mangle]
33-
#[custom_mir(dialect = "runtime", phase = "initial")]
3433
pub unsafe fn check_bigger_size(x: u16) -> u32 {
3534
// CHECK: call void @llvm.trap
36-
mir!{
37-
{
38-
RET = CastTransmute(x);
39-
Return()
40-
}
41-
}
35+
transmute_unchecked(x)
4236
}
4337

4438
// CHECK-LABEL: @check_smaller_size(
4539
#[no_mangle]
46-
#[custom_mir(dialect = "runtime", phase = "initial")]
4740
pub unsafe fn check_smaller_size(x: u32) -> u16 {
4841
// CHECK: call void @llvm.trap
49-
mir!{
50-
{
51-
RET = CastTransmute(x);
52-
Return()
53-
}
54-
}
42+
transmute_unchecked(x)
5543
}
5644

5745
// CHECK-LABEL: @check_smaller_array(
5846
#[no_mangle]
59-
#[custom_mir(dialect = "runtime", phase = "initial")]
6047
pub unsafe fn check_smaller_array(x: [u32; 7]) -> [u32; 3] {
6148
// CHECK: call void @llvm.trap
62-
mir!{
63-
{
64-
RET = CastTransmute(x);
65-
Return()
66-
}
67-
}
49+
transmute_unchecked(x)
6850
}
6951

7052
// CHECK-LABEL: @check_bigger_array(
7153
#[no_mangle]
72-
#[custom_mir(dialect = "runtime", phase = "initial")]
7354
pub unsafe fn check_bigger_array(x: [u32; 3]) -> [u32; 7] {
7455
// CHECK: call void @llvm.trap
75-
mir!{
76-
{
77-
RET = CastTransmute(x);
78-
Return()
79-
}
80-
}
56+
transmute_unchecked(x)
8157
}
8258

8359
// CHECK-LABEL: @check_to_uninhabited(
8460
#[no_mangle]
85-
#[custom_mir(dialect = "runtime", phase = "initial")]
61+
#[custom_mir(dialect = "runtime", phase = "optimized")]
8662
pub unsafe fn check_to_uninhabited(x: u16) -> BigNever {
8763
// CHECK: call void @llvm.trap
8864
mir!{
@@ -95,7 +71,7 @@ pub unsafe fn check_to_uninhabited(x: u16) -> BigNever {
9571

9672
// CHECK-LABEL: @check_from_uninhabited(
9773
#[no_mangle]
98-
#[custom_mir(dialect = "runtime", phase = "initial")]
74+
#[custom_mir(dialect = "runtime", phase = "optimized")]
9975
pub unsafe fn check_from_uninhabited(x: BigNever) -> u16 {
10076
// CHECK: ret i16 poison
10177
mir!{

0 commit comments

Comments
 (0)