Skip to content

Commit 951426b

Browse files
authored
Rollup merge of #108442 - scottmcm:mir-transmute, r=oli-obk
Add `CastKind::Transmute` to MIR ~~Nothing actually produces it in this commit, so I don't know how to test it, but it also means it shouldn't be possible for it to break anything.~~ Includes lowering `transmute` calls to it, so it's used. Zulip Conversation: <https://rust-lang.zulipchat.com/#narrow/stream/189540-t-compiler.2Fwg-mir-opt/topic/Good.20first.20isssue/near/321849610>
2 parents f33dbe9 + 6f49f85 commit 951426b

27 files changed

+216
-104
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,6 +2242,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
22422242
}
22432243
}
22442244
}
2245+
CastKind::Transmute => {
2246+
span_mirbug!(
2247+
self,
2248+
rvalue,
2249+
"Unexpected CastKind::Transmute, which is not permitted in Analysis MIR",
2250+
);
2251+
}
22452252
}
22462253
}
22472254

compiler/rustc_codegen_cranelift/src/base.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,10 @@ fn codegen_stmt<'tcx>(
720720
let operand = codegen_operand(fx, operand);
721721
operand.coerce_dyn_star(fx, lval);
722722
}
723+
Rvalue::Cast(CastKind::Transmute, ref operand, _to_ty) => {
724+
let operand = codegen_operand(fx, operand);
725+
lval.write_cvalue_transmute(fx, operand);
726+
}
723727
Rvalue::Discriminant(place) => {
724728
let place = codegen_place(fx, place);
725729
let value = place.to_cvalue(fx);

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_index::vec::Idx;
1616
use rustc_middle::mir::{self, AssertKind, SwitchTargets};
1717
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
1818
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
19-
use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt};
19+
use rustc_middle::ty::{self, Instance, Ty};
2020
use rustc_session::config::OptLevel;
2121
use rustc_span::source_map::Span;
2222
use rustc_span::{sym, Symbol};
@@ -765,6 +765,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
765765
_ => None,
766766
};
767767

768+
if let Some(intrinsic @ (sym::copy_nonoverlapping | sym::transmute)) = intrinsic {
769+
bug!("Intrinsic {intrinsic} should have been lowered before codegen");
770+
}
771+
768772
let extra_args = &args[sig.inputs().skip_binder().len()..];
769773
let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| {
770774
let op_ty = op_arg.ty(self.mir, bx.tcx());
@@ -776,23 +780,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
776780
None => bx.fn_abi_of_fn_ptr(sig, extra_args),
777781
};
778782

779-
if intrinsic == Some(sym::transmute) {
780-
return if let Some(target) = target {
781-
self.codegen_transmute(bx, &args[0], destination);
782-
helper.funclet_br(self, bx, target, mergeable_succ)
783-
} else {
784-
// If we are trying to transmute to an uninhabited type,
785-
// it is likely there is no allotted destination. In fact,
786-
// transmuting to an uninhabited type is UB, which means
787-
// we can do what we like. Here, we declare that transmuting
788-
// into an uninhabited type is impossible, so anything following
789-
// it must be unreachable.
790-
assert_eq!(fn_abi.ret.layout.abi, abi::Abi::Uninhabited);
791-
bx.unreachable();
792-
MergingSucc::False
793-
};
794-
}
795-
796783
if let Some(merging_succ) = self.codegen_panic_intrinsic(
797784
&helper,
798785
bx,
@@ -835,7 +822,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
835822

836823
match intrinsic {
837824
None | Some(sym::drop_in_place) => {}
838-
Some(sym::copy_nonoverlapping) => unreachable!(),
839825
Some(intrinsic) => {
840826
let dest = match ret_dest {
841827
_ if fn_abi.ret.is_indirect() => llargs[0],
@@ -1746,33 +1732,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
17461732
}
17471733
}
17481734

1749-
fn codegen_transmute(&mut self, bx: &mut Bx, src: &mir::Operand<'tcx>, dst: mir::Place<'tcx>) {
1750-
if let Some(index) = dst.as_local() {
1751-
match self.locals[index] {
1752-
LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place),
1753-
LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"),
1754-
LocalRef::Operand(None) => {
1755-
let dst_layout = bx.layout_of(self.monomorphized_place_ty(dst.as_ref()));
1756-
assert!(!dst_layout.ty.has_erasable_regions());
1757-
let place = PlaceRef::alloca(bx, dst_layout);
1758-
place.storage_live(bx);
1759-
self.codegen_transmute_into(bx, src, place);
1760-
let op = bx.load_operand(place);
1761-
place.storage_dead(bx);
1762-
self.locals[index] = LocalRef::Operand(Some(op));
1763-
self.debug_introduce_local(bx, index);
1764-
}
1765-
LocalRef::Operand(Some(op)) => {
1766-
assert!(op.layout.is_zst(), "assigning to initialized SSAtemp");
1767-
}
1768-
}
1769-
} else {
1770-
let dst = self.codegen_place(bx, dst.as_ref());
1771-
self.codegen_transmute_into(bx, src, dst);
1772-
}
1773-
}
1774-
1775-
fn codegen_transmute_into(
1735+
pub(crate) fn codegen_transmute_into(
17761736
&mut self,
17771737
bx: &mut Bx,
17781738
src: &mir::Operand<'tcx>,

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
135135
dest.codegen_set_discr(bx, variant_index);
136136
}
137137

138+
mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, _ty) => {
139+
self.codegen_transmute_into(bx, operand, dest);
140+
}
141+
138142
_ => {
139143
assert!(self.rvalue_creates_operand(rvalue, DUMMY_SP));
140144
let temp = self.codegen_rvalue_operand(bx, rvalue);
@@ -344,6 +348,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
344348
};
345349
OperandValue::Immediate(newval)
346350
}
351+
mir::CastKind::Transmute => {
352+
bug!("Transmute operand {:?} in `codegen_rvalue_operand`", operand);
353+
}
347354
};
348355
OperandRef { val, layout: cast }
349356
}
@@ -684,6 +691,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
684691
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
685692
pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool {
686693
match *rvalue {
694+
mir::Rvalue::Cast(mir::CastKind::Transmute, ..) =>
695+
// sometimes this could, but for aggregates it can't
696+
false,
687697
mir::Rvalue::Ref(..) |
688698
mir::Rvalue::CopyForDeref(..) |
689699
mir::Rvalue::AddressOf(..) |

compiler/rustc_const_eval/src/interpret/cast.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
133133
bug!()
134134
}
135135
}
136+
137+
Transmute => {
138+
self.copy_op(src, dest, /*allow_transmute*/ true)?;
139+
}
136140
}
137141
Ok(())
138142
}

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
124124
let substs = instance.substs;
125125
let intrinsic_name = self.tcx.item_name(instance.def_id());
126126

127+
if let sym::copy_nonoverlapping | sym::transmute = intrinsic_name {
128+
bug!("Intrinsic {intrinsic_name} should have been lowered before interpreting MIR");
129+
}
130+
127131
// First handle intrinsics without return place.
128132
let ret = match ret {
129133
None => match intrinsic_name {
130-
sym::transmute => throw_ub_format!("transmuting to uninhabited type"),
131134
sym::abort => M::abort(self, "the program aborted execution".to_owned())?,
132135
// Unsupported diverging intrinsic.
133136
_ => return Ok(false),
@@ -411,9 +414,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
411414
self.exact_div(&val, &size, dest)?;
412415
}
413416

414-
sym::transmute => {
415-
self.copy_op(&args[0], dest, /*allow_transmute*/ true)?;
416-
}
417417
sym::assert_inhabited
418418
| sym::assert_zero_valid
419419
| sym::assert_mem_uninitialized_valid => {

compiler/rustc_const_eval/src/transform/validate.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,43 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
619619
);
620620
}
621621
}
622+
CastKind::Transmute => {
623+
if let MirPhase::Runtime(..) = self.mir_phase {
624+
// `mem::transmute` currently requires types concrete enough
625+
// to *know* that their sizes are the same, and we repeat
626+
// that check here. This restriction may be lifted in future,
627+
// should some optimizations need to it be more general.
628+
// (It might just end up being UB if they don't match, say.)
629+
630+
let src_layout = self.tcx.layout_of(self.param_env.and(op_ty));
631+
let dst_layout = self.tcx.layout_of(self.param_env.and(*target_type));
632+
if let Err(e) = src_layout {
633+
self.fail(
634+
location,
635+
format!(
636+
"Unable to compute layout for source type {op_ty:?}: {e}"
637+
),
638+
);
639+
}
640+
if let Err(e) = dst_layout {
641+
self.fail(location, format!("Unable to compute layout for destination type {target_type:?}: {e}"));
642+
}
643+
644+
if let (Ok(src_layout), Ok(dst_layout)) = (src_layout, dst_layout) {
645+
if src_layout.layout.size() != dst_layout.layout.size() {
646+
self.fail(location, format!("Source and destination layouts have different sizes: {src_layout:?} vs {dst_layout:?}"));
647+
}
648+
}
649+
} else {
650+
self.fail(
651+
location,
652+
format!(
653+
"Transmute is not supported in non-runtime phase {:?}.",
654+
self.mir_phase
655+
),
656+
);
657+
}
658+
}
622659
}
623660
}
624661
Rvalue::Repeat(_, _)

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1963,7 +1963,8 @@ impl<'tcx> Rvalue<'tcx> {
19631963
| CastKind::PtrToPtr
19641964
| CastKind::Pointer(_)
19651965
| CastKind::PointerFromExposedAddress
1966-
| CastKind::DynStar,
1966+
| CastKind::DynStar
1967+
| CastKind::Transmute,
19671968
_,
19681969
_,
19691970
)

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ pub enum MirPhase {
8383
/// access to. This occurs in generator bodies. Such locals do not behave like other locals,
8484
/// because they eg may be aliased in surprising ways. Runtime MIR has no such special locals -
8585
/// all generator bodies are lowered and so all places that look like locals really are locals.
86+
/// - Intrinsics: In analysis MIR, intrinsics are typically represented as [`TerminatorKind::Call`]s
87+
/// to their `extern "rust-intrinsic"` declarations. In runtime MIR, some intrinsics are lowered
88+
/// to MIR constructs not used earlier, such as `mem::transmute` → [`CastKind::Transmute`].
8689
///
8790
/// Also note that the lint pass which reports eg `200_u8 + 200_u8` as an error is run as a part
8891
/// of analysis to runtime MIR lowering. To ensure lints are reported reliably, this means that
@@ -1189,6 +1192,10 @@ pub enum CastKind {
11891192
IntToFloat,
11901193
PtrToPtr,
11911194
FnPtrToPtr,
1195+
/// Reinterpret the bits of the input as a different type.
1196+
///
1197+
/// Allowed only in [`MirPhase::Runtime`]; Earlier it's a [`TerminatorKind::Call`].
1198+
Transmute,
11921199
}
11931200

11941201
#[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]

compiler/rustc_mir_transform/src/check_unsafety.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
132132
self.register_violations(violations, used_unsafe_blocks.iter().copied());
133133
}
134134
},
135+
Rvalue::Cast(CastKind::Transmute, ..) => {
136+
self.require_unsafe(
137+
UnsafetyViolationKind::General,
138+
UnsafetyViolationDetails::CallToUnsafeFunction,
139+
);
140+
}
135141
_ => {}
136142
}
137143
self.super_rvalue(rvalue, location);

compiler/rustc_mir_transform/src/lower_intrinsics.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use crate::MirPass;
44
use rustc_middle::mir::*;
5+
use rustc_middle::ty::inhabitedness::inhabited_predicate::InhabitedPredicate;
56
use rustc_middle::ty::subst::SubstsRef;
67
use rustc_middle::ty::{self, Ty, TyCtxt};
78
use rustc_span::symbol::{sym, Symbol};
@@ -162,6 +163,30 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
162163
terminator.kind = TerminatorKind::Goto { target };
163164
}
164165
}
166+
sym::transmute => {
167+
let dst_ty = destination.ty(local_decls, tcx).ty;
168+
if let Some(target) = *target {
169+
let mut args = args.drain(..);
170+
block.statements.push(Statement {
171+
source_info: terminator.source_info,
172+
kind: StatementKind::Assign(Box::new((
173+
*destination,
174+
Rvalue::Cast(CastKind::Transmute, args.next().unwrap(), dst_ty),
175+
))),
176+
});
177+
assert_eq!(args.next(), None, "Extra argument for transmute intrinsic");
178+
drop(args);
179+
terminator.kind = TerminatorKind::Goto { target };
180+
} else {
181+
debug_assert!(!matches!(
182+
dst_ty.inhabited_predicate(tcx),
183+
InhabitedPredicate::True
184+
));
185+
// `transmute::<_, !>(x)` is UB for anything inhabited,
186+
// and must be unreachable if `x` is uninhabited.
187+
terminator.kind = TerminatorKind::Unreachable;
188+
}
189+
}
165190
_ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
166191
validate_simd_shuffle(tcx, args, terminator.source_info.span);
167192
}

src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ fn check_rvalue<'tcx>(
176176
// FIXME(dyn-star)
177177
unimplemented!()
178178
},
179+
Rvalue::Cast(CastKind::Transmute, _, _) => {
180+
Err((span, "transmute can attempt to turn pointers into integers, so is unstable in const fn".into()))
181+
},
179182
// binops are fine on integers
180183
Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
181184
check_operand(tcx, lhs, span, body)?;

src/tools/miri/tests/fail/never_transmute_humans.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ struct Human;
77

88
fn main() {
99
let _x: ! = unsafe {
10-
std::mem::transmute::<Human, !>(Human) //~ ERROR: transmuting to uninhabited
10+
std::mem::transmute::<Human, !>(Human) //~ ERROR: entering unreachable code
1111
};
1212
}

src/tools/miri/tests/fail/never_transmute_humans.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: transmuting to uninhabited type
1+
error: Undefined Behavior: entering unreachable code
22
--> $DIR/never_transmute_humans.rs:LL:CC
33
|
44
LL | std::mem::transmute::<Human, !>(Human)
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

0 commit comments

Comments
 (0)