From 0905bad65d398aee3dde546e31542c1d5d4dd432 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 24 Feb 2023 18:32:52 -0800 Subject: [PATCH 1/4] Add `CastKind::Transmute` to MIR Nothing actually produces it in this commit. --- compiler/rustc_borrowck/src/type_check/mod.rs | 7 +++ compiler/rustc_codegen_cranelift/src/base.rs | 4 ++ compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 10 ++++ .../rustc_const_eval/src/interpret/cast.rs | 4 ++ .../src/transform/validate.rs | 19 ++++++++ compiler/rustc_middle/src/mir/mod.rs | 3 +- compiler/rustc_middle/src/mir/syntax.rs | 2 + .../rustc_mir_transform/src/check_unsafety.rs | 6 +++ .../src/lower_intrinsics.rs | 25 ++++++++++ .../clippy_utils/src/qualify_min_const_fn.rs | 3 ++ .../miri/tests/fail/never_transmute_humans.rs | 2 +- .../tests/fail/never_transmute_humans.stderr | 4 +- ...e_75439.foo.MatchBranchSimplification.diff | 48 +++++++------------ ...wer_intrinsics.assume.LowerIntrinsics.diff | 2 +- ...trinsics.discriminant.LowerIntrinsics.diff | 14 +++--- ...f_copy_nonoverlapping.LowerIntrinsics.diff | 2 +- tests/mir-opt/lower_intrinsics.rs | 12 +++++ ...s.transmute_inhabited.LowerIntrinsics.diff | 27 +++++++++++ ...transmute_uninhabited.LowerIntrinsics.diff | 21 ++++++++ ...rinsics.with_overflow.LowerIntrinsics.diff | 6 +-- .../ui/consts/const-eval/ub-enum.32bit.stderr | 4 +- .../ui/consts/const-eval/ub-enum.64bit.stderr | 4 +- .../validate_uninhabited_zsts.32bit.stderr | 2 +- .../validate_uninhabited_zsts.64bit.stderr | 2 +- tests/ui/statics/uninhabited-static.stderr | 4 +- 26 files changed, 184 insertions(+), 55 deletions(-) create mode 100644 tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff create mode 100644 tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index c4e7e1f8ffa30..3d3f253ef0e5f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2242,6 +2242,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } } + CastKind::Transmute => { + span_mirbug!( + self, + rvalue, + "Unexpected CastKind::Transmute, should only appear after lowering_intrinsics", + ); + } } } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 7f857528c7c5c..20644ad6018a5 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -720,6 +720,10 @@ fn codegen_stmt<'tcx>( let operand = codegen_operand(fx, operand); operand.coerce_dyn_star(fx, lval); } + Rvalue::Cast(CastKind::Transmute, ref operand, _to_ty) => { + let operand = codegen_operand(fx, operand); + lval.write_cvalue_transmute(fx, operand); + } Rvalue::Discriminant(place) => { let place = codegen_place(fx, place); let value = place.to_cvalue(fx); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index cdc3e1dc237b2..4d512ebcf7e74 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1789,7 +1789,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - fn codegen_transmute_into( + pub(crate) fn codegen_transmute_into( &mut self, bx: &mut Bx, src: &mir::Operand<'tcx>, diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 41cd1c09a4e70..caa34c3672ff3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -135,6 +135,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { dest.codegen_set_discr(bx, variant_index); } + mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, _ty) => { + self.codegen_transmute_into(bx, operand, dest); + } + _ => { assert!(self.rvalue_creates_operand(rvalue, DUMMY_SP)); let temp = self.codegen_rvalue_operand(bx, rvalue); @@ -344,6 +348,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; OperandValue::Immediate(newval) } + mir::CastKind::Transmute => { + bug!("Transmute operand {:?} in `codegen_rvalue_operand`", operand); + } }; OperandRef { val, layout: cast } } @@ -684,6 +691,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool { match *rvalue { + mir::Rvalue::Cast(mir::CastKind::Transmute, ..) => + // sometimes this could, but for aggregates it can't + false, mir::Rvalue::Ref(..) | mir::Rvalue::CopyForDeref(..) | mir::Rvalue::AddressOf(..) | diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 2be5ed896ec80..e07a00d4cba1b 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -133,6 +133,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { bug!() } } + + Transmute => { + self.copy_op(src, dest, /*allow_transmute*/ true)?; + } } Ok(()) } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 3af2d0c4e668a..84e0e24f99a47 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -619,6 +619,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } + CastKind::Transmute => { + let src_layout = self.tcx.layout_of(self.param_env.and(op_ty)); + let dst_layout = self.tcx.layout_of(self.param_env.and(*target_type)); + if let Err(e) = src_layout { + self.fail( + location, + format!("Unable to compute layout for source type {op_ty:?}: {e}"), + ); + } + if let Err(e) = dst_layout { + self.fail(location, format!("Unable to compute layout for destination type {target_type:?}: {e}")); + } + + if let (Ok(src_layout), Ok(dst_layout)) = (src_layout, dst_layout) { + if src_layout.layout.size() != dst_layout.layout.size() { + self.fail(location, format!("Source and destination layouts have different sizes: {src_layout:?} vs {dst_layout:?}")); + } + } + } } } Rvalue::Repeat(_, _) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index ccd8a33386693..2326d31c4c07d 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1963,7 +1963,8 @@ impl<'tcx> Rvalue<'tcx> { | CastKind::PtrToPtr | CastKind::Pointer(_) | CastKind::PointerFromExposedAddress - | CastKind::DynStar, + | CastKind::DynStar + | CastKind::Transmute, _, _, ) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index ae09562a85e98..d1234d3a38974 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1189,6 +1189,8 @@ pub enum CastKind { IntToFloat, PtrToPtr, FnPtrToPtr, + /// Reinterpret the bits of the input as a different type. + Transmute, } #[derive(Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index d00ee1f4babe8..110d0581e49a7 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -132,6 +132,12 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> { self.register_violations(violations, used_unsafe_blocks.iter().copied()); } }, + Rvalue::Cast(CastKind::Transmute, ..) => { + self.require_unsafe( + UnsafetyViolationKind::General, + UnsafetyViolationDetails::CallToUnsafeFunction, + ); + } _ => {} } self.super_rvalue(rvalue, location); diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index f596cc1808fa2..aad5be1f44c6f 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -2,6 +2,7 @@ use crate::MirPass; use rustc_middle::mir::*; +use rustc_middle::ty::inhabitedness::inhabited_predicate::InhabitedPredicate; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; @@ -162,6 +163,30 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Goto { target }; } } + sym::transmute => { + let dst_ty = destination.ty(local_decls, tcx).ty; + if let Some(target) = *target { + let mut args = args.drain(..); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::Cast(CastKind::Transmute, args.next().unwrap(), dst_ty), + ))), + }); + assert_eq!(args.next(), None, "Extra argument for transmute intrinsic"); + drop(args); + terminator.kind = TerminatorKind::Goto { target }; + } else { + debug_assert!(!matches!( + dst_ty.inhabited_predicate(tcx), + InhabitedPredicate::True + )); + // `transmute::<_, !>(x)` is UB for anything inhabited, + // and must be unreachable if `x` is uninhabited. + terminator.kind = TerminatorKind::Unreachable; + } + } _ if intrinsic_name.as_str().starts_with("simd_shuffle") => { validate_simd_shuffle(tcx, args, terminator.source_info.span); } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 1a35fe05067fc..0697a9eafb338 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -176,6 +176,9 @@ fn check_rvalue<'tcx>( // FIXME(dyn-star) unimplemented!() }, + Rvalue::Cast(CastKind::Transmute, _, _) => { + Err((span, "transmute can attempt to turn pointers into integers, so is unstable in const fn".into())) + }, // binops are fine on integers Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { check_operand(tcx, lhs, span, body)?; diff --git a/src/tools/miri/tests/fail/never_transmute_humans.rs b/src/tools/miri/tests/fail/never_transmute_humans.rs index de723433dc283..cba3cc0ccf17b 100644 --- a/src/tools/miri/tests/fail/never_transmute_humans.rs +++ b/src/tools/miri/tests/fail/never_transmute_humans.rs @@ -7,6 +7,6 @@ struct Human; fn main() { let _x: ! = unsafe { - std::mem::transmute::(Human) //~ ERROR: transmuting to uninhabited + std::mem::transmute::(Human) //~ ERROR: entering unreachable code }; } diff --git a/src/tools/miri/tests/fail/never_transmute_humans.stderr b/src/tools/miri/tests/fail/never_transmute_humans.stderr index e8df4739f9bcb..a51ca7fe7e767 100644 --- a/src/tools/miri/tests/fail/never_transmute_humans.stderr +++ b/src/tools/miri/tests/fail/never_transmute_humans.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: transmuting to uninhabited type +error: Undefined Behavior: entering unreachable code --> $DIR/never_transmute_humans.rs:LL:CC | LL | std::mem::transmute::(Human) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/tests/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff b/tests/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff index bcda128804566..8e6e6fc0ec2a3 100644 --- a/tests/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff +++ b/tests/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff @@ -24,61 +24,49 @@ StorageLive(_2); // scope 0 at $DIR/issue_75439.rs:+2:9: +2:15 StorageLive(_3); // scope 2 at $DIR/issue_75439.rs:+2:47: +2:52 _3 = _1; // scope 2 at $DIR/issue_75439.rs:+2:47: +2:52 - _2 = transmute::<[u8; 16], [u32; 4]>(move _3) -> bb1; // scope 2 at $DIR/issue_75439.rs:+2:37: +2:53 - // mir::Constant - // + span: $DIR/issue_75439.rs:8:37: 8:46 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn([u8; 16]) -> [u32; 4] {transmute::<[u8; 16], [u32; 4]>}, val: Value() } + _2 = move _3 as [u32; 4] (Transmute); // scope 2 at $DIR/issue_75439.rs:+2:37: +2:53 + StorageDead(_3); // scope 2 at $DIR/issue_75439.rs:+2:52: +2:53 + switchInt(_2[0 of 4]) -> [0: bb1, otherwise: bb6]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 } bb1: { - StorageDead(_3); // scope 2 at $DIR/issue_75439.rs:+2:52: +2:53 - switchInt(_2[0 of 4]) -> [0: bb2, otherwise: bb8]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 + switchInt(_2[1 of 4]) -> [0: bb2, otherwise: bb6]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 } bb2: { - switchInt(_2[1 of 4]) -> [0: bb3, otherwise: bb8]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 + switchInt(_2[2 of 4]) -> [0: bb4, 4294901760: bb5, otherwise: bb6]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 } bb3: { - switchInt(_2[2 of 4]) -> [0: bb5, 4294901760: bb6, otherwise: bb8]; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 - } - - bb4: { StorageLive(_5); // scope 3 at $DIR/issue_75439.rs:+5:14: +5:38 StorageLive(_6); // scope 4 at $DIR/issue_75439.rs:+5:33: +5:35 _6 = _4; // scope 4 at $DIR/issue_75439.rs:+5:33: +5:35 - _5 = transmute::(move _6) -> bb7; // scope 4 at $DIR/issue_75439.rs:+5:23: +5:36 - // mir::Constant - // + span: $DIR/issue_75439.rs:11:23: 11:32 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) -> [u8; 4] {transmute::}, val: Value() } + _5 = move _6 as [u8; 4] (Transmute); // scope 4 at $DIR/issue_75439.rs:+5:23: +5:36 + StorageDead(_6); // scope 4 at $DIR/issue_75439.rs:+5:35: +5:36 + _0 = Option::<[u8; 4]>::Some(move _5); // scope 3 at $DIR/issue_75439.rs:+5:9: +5:39 + StorageDead(_5); // scope 3 at $DIR/issue_75439.rs:+5:38: +5:39 + StorageDead(_4); // scope 1 at $DIR/issue_75439.rs:+6:5: +6:6 + goto -> bb7; // scope 1 at $DIR/issue_75439.rs:+4:5: +8:6 } - bb5: { + bb4: { StorageLive(_4); // scope 3 at $DIR/issue_75439.rs:+4:27: +4:29 _4 = _2[3 of 4]; // scope 3 at $DIR/issue_75439.rs:+4:27: +4:29 - goto -> bb4; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 + goto -> bb3; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 } - bb6: { + bb5: { StorageLive(_4); // scope 3 at $DIR/issue_75439.rs:+4:27: +4:29 _4 = _2[3 of 4]; // scope 3 at $DIR/issue_75439.rs:+4:27: +4:29 - goto -> bb4; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 + goto -> bb3; // scope 3 at $DIR/issue_75439.rs:+4:12: +4:30 } - bb7: { - StorageDead(_6); // scope 4 at $DIR/issue_75439.rs:+5:35: +5:36 - _0 = Option::<[u8; 4]>::Some(move _5); // scope 3 at $DIR/issue_75439.rs:+5:9: +5:39 - StorageDead(_5); // scope 3 at $DIR/issue_75439.rs:+5:38: +5:39 - StorageDead(_4); // scope 1 at $DIR/issue_75439.rs:+6:5: +6:6 - goto -> bb9; // scope 1 at $DIR/issue_75439.rs:+4:5: +8:6 - } - - bb8: { + bb6: { _0 = Option::<[u8; 4]>::None; // scope 1 at $DIR/issue_75439.rs:+7:9: +7:13 - goto -> bb9; // scope 1 at $DIR/issue_75439.rs:+4:5: +8:6 + goto -> bb7; // scope 1 at $DIR/issue_75439.rs:+4:5: +8:6 } - bb9: { + bb7: { StorageDead(_2); // scope 0 at $DIR/issue_75439.rs:+9:1: +9:2 return; // scope 0 at $DIR/issue_75439.rs:+9:2: +9:2 } diff --git a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff index d9898d8e0f015..546ccde6d36b4 100644 --- a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff @@ -11,7 +11,7 @@ StorageLive(_1); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 - _1 = std::intrinsics::assume(const true) -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:72:9: 72:32 +- // + span: $DIR/lower_intrinsics.rs:84:9: 84:32 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(bool) {std::intrinsics::assume}, val: Value() } + assume(const true); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 diff --git a/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff index d962ef8cb12dc..d42b8741175c0 100644 --- a/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff @@ -31,7 +31,7 @@ _3 = &(*_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:42: +1:44 - _2 = discriminant_value::(move _3) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:49:5: 49:41 +- // + span: $DIR/lower_intrinsics.rs:61:5: 61:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a T) -> ::Discriminant {discriminant_value::}, val: Value() } + _2 = discriminant((*_3)); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45 @@ -46,13 +46,13 @@ StorageLive(_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 _19 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:50:42: 50:44 + // + span: $DIR/lower_intrinsics.rs:62:42: 62:44 // + literal: Const { ty: &i32, val: Unevaluated(discriminant, [T], Some(promoted[2])) } _7 = &(*_19); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 _6 = &(*_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 - _5 = discriminant_value::(move _6) -> bb2; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:50:5: 50:41 +- // + span: $DIR/lower_intrinsics.rs:62:5: 62:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a i32) -> ::Discriminant {discriminant_value::}, val: Value() } + _5 = discriminant((*_6)); // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45 + goto -> bb2; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45 @@ -67,13 +67,13 @@ StorageLive(_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 _18 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:51:42: 51:45 + // + span: $DIR/lower_intrinsics.rs:63:42: 63:45 // + literal: Const { ty: &(), val: Unevaluated(discriminant, [T], Some(promoted[1])) } _11 = &(*_18); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 _10 = &(*_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 - _9 = discriminant_value::<()>(move _10) -> bb3; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:51:5: 51:41 +- // + span: $DIR/lower_intrinsics.rs:63:5: 63:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a ()) -> <() as DiscriminantKind>::Discriminant {discriminant_value::<()>}, val: Value() } + _9 = discriminant((*_10)); // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46 + goto -> bb3; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46 @@ -88,13 +88,13 @@ StorageLive(_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 _17 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:52:42: 52:47 + // + span: $DIR/lower_intrinsics.rs:64:42: 64:47 // + literal: Const { ty: &E, val: Unevaluated(discriminant, [T], Some(promoted[0])) } _15 = &(*_17); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 _14 = &(*_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 - _13 = discriminant_value::(move _14) -> bb4; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:52:5: 52:41 +- // + span: $DIR/lower_intrinsics.rs:64:5: 64:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a E) -> ::Discriminant {discriminant_value::}, val: Value() } + _13 = discriminant((*_14)); // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48 + goto -> bb4; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48 diff --git a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff index 5c972a00e464d..0cb173fa9655c 100644 --- a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff @@ -49,7 +49,7 @@ StorageDead(_9); // scope 3 at $DIR/lower_intrinsics.rs:+4:90: +4:91 - _3 = copy_nonoverlapping::(move _4, move _8, const 0_usize) -> bb1; // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:65:9: 65:28 +- // + span: $DIR/lower_intrinsics.rs:77:9: 77:28 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const i32, *mut i32, usize) {copy_nonoverlapping::}, val: Value() } + copy_nonoverlapping(dst = move _8, src = move _4, count = const 0_usize); // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95 + goto -> bb1; // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95 diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 7147be43ca5e3..56a7ef8bc9cf3 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -38,6 +38,18 @@ pub fn non_const() -> usize { size_of_t() } +// EMIT_MIR lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff +pub fn transmute_inhabited(c: std::cmp::Ordering) -> i8 { + unsafe { std::mem::transmute(c) } +} + +pub enum Never {} + +// EMIT_MIR lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff +pub unsafe fn transmute_uninhabited(u: ()) -> Never { + unsafe { std::mem::transmute::<(), Never>(u) } +} + pub enum E { A, B, diff --git a/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff new file mode 100644 index 0000000000000..814368ec021e7 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff @@ -0,0 +1,27 @@ +- // MIR for `transmute_inhabited` before LowerIntrinsics ++ // MIR for `transmute_inhabited` after LowerIntrinsics + + fn transmute_inhabited(_1: std::cmp::Ordering) -> i8 { + debug c => _1; // in scope 0 at $DIR/lower_intrinsics.rs:+0:28: +0:29 + let mut _0: i8; // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:54: +0:56 + let mut _2: std::cmp::Ordering; // in scope 0 at $DIR/lower_intrinsics.rs:+1:34: +1:35 + scope 1 { + } + + bb0: { + StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+1:34: +1:35 + _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+1:34: +1:35 +- _0 = transmute::(move _2) -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:43:14: 43:33 +- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(std::cmp::Ordering) -> i8 {transmute::}, val: Value() } ++ _0 = move _2 as i8 (Transmute); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 ++ goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 + } + + bb1: { + StorageDead(_2); // scope 1 at $DIR/lower_intrinsics.rs:+1:35: +1:36 + return; // scope 0 at $DIR/lower_intrinsics.rs:+2:2: +2:2 + } + } + diff --git a/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff new file mode 100644 index 0000000000000..709735c3beafc --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff @@ -0,0 +1,21 @@ +- // MIR for `transmute_uninhabited` before LowerIntrinsics ++ // MIR for `transmute_uninhabited` after LowerIntrinsics + + fn transmute_uninhabited(_1: ()) -> Never { + debug u => _1; // in scope 0 at $DIR/lower_intrinsics.rs:+0:37: +0:38 + let mut _0: Never; // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:47: +0:52 + let mut _2: (); // in scope 0 at $DIR/lower_intrinsics.rs:+1:47: +1:48 + scope 1 { + } + + bb0: { + StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+1:47: +1:48 + _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+1:47: +1:48 +- _0 = transmute::<(), Never>(move _2); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:49 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:50:14: 50:46 +- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Never {transmute::<(), Never>}, val: Value() } ++ unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:49 + } + } + diff --git a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff index 9870a70dec5ee..aea5a8035d331 100644 --- a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff @@ -32,7 +32,7 @@ _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 - _3 = add_with_overflow::(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:78:14: 78:49 +- // + span: $DIR/lower_intrinsics.rs:90:14: 90:49 - // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {add_with_overflow::}, val: Value() } + _3 = CheckedAdd(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 @@ -48,7 +48,7 @@ _8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:+2:53: +2:54 - _6 = sub_with_overflow::(move _7, move _8) -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:79:14: 79:49 +- // + span: $DIR/lower_intrinsics.rs:91:14: 91:49 - // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {sub_with_overflow::}, val: Value() } + _6 = CheckedSub(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 + goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 @@ -64,7 +64,7 @@ _11 = _2; // scope 2 at $DIR/lower_intrinsics.rs:+3:53: +3:54 - _9 = mul_with_overflow::(move _10, move _11) -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:80:14: 80:49 +- // + span: $DIR/lower_intrinsics.rs:92:14: 92:49 - // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {mul_with_overflow::}, val: Value() } + _9 = CheckedMul(move _10, move _11); // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 + goto -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 diff --git a/tests/ui/consts/const-eval/ub-enum.32bit.stderr b/tests/ui/consts/const-eval/ub-enum.32bit.stderr index 2d86bd88f1c88..e978ea7e3c460 100644 --- a/tests/ui/consts/const-eval/ub-enum.32bit.stderr +++ b/tests/ui/consts/const-eval/ub-enum.32bit.stderr @@ -108,13 +108,13 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-enum.rs:96:77 | LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type + | ^^^^^^^^^^^^^^^^^^^^ entering unreachable code error[E0080]: evaluation of constant value failed --> $DIR/ub-enum.rs:98:77 | LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type + | ^^^^^^^^^^^^^^^^^^^^ entering unreachable code error: aborting due to 13 previous errors diff --git a/tests/ui/consts/const-eval/ub-enum.64bit.stderr b/tests/ui/consts/const-eval/ub-enum.64bit.stderr index a89d7ec5f6d44..5e20eba0b6c0b 100644 --- a/tests/ui/consts/const-eval/ub-enum.64bit.stderr +++ b/tests/ui/consts/const-eval/ub-enum.64bit.stderr @@ -108,13 +108,13 @@ error[E0080]: evaluation of constant value failed --> $DIR/ub-enum.rs:96:77 | LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type + | ^^^^^^^^^^^^^^^^^^^^ entering unreachable code error[E0080]: evaluation of constant value failed --> $DIR/ub-enum.rs:98:77 | LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type + | ^^^^^^^^^^^^^^^^^^^^ entering unreachable code error: aborting due to 13 previous errors diff --git a/tests/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr b/tests/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr index 69fb1a59d4f39..9230a5a5e4508 100644 --- a/tests/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr +++ b/tests/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr @@ -11,7 +11,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/validate_uninhabited_zsts.rs:4:14 | LL | unsafe { std::mem::transmute(()) } - | ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type + | ^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code | note: inside `foo` --> $DIR/validate_uninhabited_zsts.rs:4:14 diff --git a/tests/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr b/tests/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr index 69fb1a59d4f39..9230a5a5e4508 100644 --- a/tests/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr +++ b/tests/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr @@ -11,7 +11,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/validate_uninhabited_zsts.rs:4:14 | LL | unsafe { std::mem::transmute(()) } - | ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type + | ^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code | note: inside `foo` --> $DIR/validate_uninhabited_zsts.rs:4:14 diff --git a/tests/ui/statics/uninhabited-static.stderr b/tests/ui/statics/uninhabited-static.stderr index 437053a4476e1..5286d4955ae3a 100644 --- a/tests/ui/statics/uninhabited-static.stderr +++ b/tests/ui/statics/uninhabited-static.stderr @@ -47,7 +47,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/uninhabited-static.rs:12:31 | LL | static VOID2: Void = unsafe { std::mem::transmute(()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type + | ^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code warning: the type `Void` does not permit zero-initialization --> $DIR/uninhabited-static.rs:12:31 @@ -66,7 +66,7 @@ error[E0080]: could not evaluate static initializer --> $DIR/uninhabited-static.rs:16:32 | LL | static NEVER2: Void = unsafe { std::mem::transmute(()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type + | ^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code warning: the type `Void` does not permit zero-initialization --> $DIR/uninhabited-static.rs:16:32 From 2405ac418acd20478dfc11f67532201f2ede356f Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 26 Feb 2023 13:03:24 -0800 Subject: [PATCH 2/4] Remove transmute intrinsic code from interpret and codegen_ssa --- compiler/rustc_codegen_ssa/src/mir/block.rs | 50 ++----------------- .../src/interpret/intrinsics.rs | 8 +-- 2 files changed, 9 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 4d512ebcf7e74..ff28f499ba3dd 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -16,7 +16,7 @@ use rustc_index::vec::Idx; use rustc_middle::mir::{self, AssertKind, SwitchTargets}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; -use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, Instance, Ty}; use rustc_session::config::OptLevel; use rustc_span::source_map::Span; use rustc_span::{sym, Symbol}; @@ -782,6 +782,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => None, }; + if let Some(intrinsic @ (sym::copy_nonoverlapping | sym::transmute)) = intrinsic { + bug!("Intrinsic {intrinsic} should have been lowered before codegen"); + } + let extra_args = &args[sig.inputs().skip_binder().len()..]; let extra_args = bx.tcx().mk_type_list(extra_args.iter().map(|op_arg| { let op_ty = op_arg.ty(self.mir, bx.tcx()); @@ -793,23 +797,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None => bx.fn_abi_of_fn_ptr(sig, extra_args), }; - if intrinsic == Some(sym::transmute) { - return if let Some(target) = target { - self.codegen_transmute(bx, &args[0], destination); - helper.funclet_br(self, bx, target, mergeable_succ) - } else { - // If we are trying to transmute to an uninhabited type, - // it is likely there is no allotted destination. In fact, - // transmuting to an uninhabited type is UB, which means - // we can do what we like. Here, we declare that transmuting - // into an uninhabited type is impossible, so anything following - // it must be unreachable. - assert_eq!(fn_abi.ret.layout.abi, abi::Abi::Uninhabited); - bx.unreachable(); - MergingSucc::False - }; - } - if let Some(merging_succ) = self.codegen_panic_intrinsic( &helper, bx, @@ -852,7 +839,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match intrinsic { None | Some(sym::drop_in_place) => {} - Some(sym::copy_nonoverlapping) => unreachable!(), Some(intrinsic) => { let dest = match ret_dest { _ if fn_abi.ret.is_indirect() => llargs[0], @@ -1763,32 +1749,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - fn codegen_transmute(&mut self, bx: &mut Bx, src: &mir::Operand<'tcx>, dst: mir::Place<'tcx>) { - if let Some(index) = dst.as_local() { - match self.locals[index] { - LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place), - LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"), - LocalRef::Operand(None) => { - let dst_layout = bx.layout_of(self.monomorphized_place_ty(dst.as_ref())); - assert!(!dst_layout.ty.has_erasable_regions()); - let place = PlaceRef::alloca(bx, dst_layout); - place.storage_live(bx); - self.codegen_transmute_into(bx, src, place); - let op = bx.load_operand(place); - place.storage_dead(bx); - self.locals[index] = LocalRef::Operand(Some(op)); - self.debug_introduce_local(bx, index); - } - LocalRef::Operand(Some(op)) => { - assert!(op.layout.is_zst(), "assigning to initialized SSAtemp"); - } - } - } else { - let dst = self.codegen_place(bx, dst.as_ref()); - self.codegen_transmute_into(bx, src, dst); - } - } - pub(crate) fn codegen_transmute_into( &mut self, bx: &mut Bx, diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c5d558aeb6ccd..8533d2c3f3e1f 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -124,10 +124,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let substs = instance.substs; let intrinsic_name = self.tcx.item_name(instance.def_id()); + if let sym::copy_nonoverlapping | sym::transmute = intrinsic_name { + bug!("Intrinsic {intrinsic_name} should have been lowered before interpreting MIR"); + } + // First handle intrinsics without return place. let ret = match ret { None => match intrinsic_name { - sym::transmute => throw_ub_format!("transmuting to uninhabited type"), sym::abort => M::abort(self, "the program aborted execution".to_owned())?, // Unsupported diverging intrinsic. _ => return Ok(false), @@ -411,9 +414,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.exact_div(&val, &size, dest)?; } - sym::transmute => { - self.copy_op(&args[0], dest, /*allow_transmute*/ true)?; - } sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { From 3fbdbcc3461f44f1181827c79f324c16c685fea7 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 26 Feb 2023 20:32:19 -0800 Subject: [PATCH 3/4] `CastKind::Transmute` is only allowed in `MirPhase::Runtime` --- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- .../src/transform/validate.rs | 44 +++++++++++++------ compiler/rustc_middle/src/mir/syntax.rs | 5 +++ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 3d3f253ef0e5f..b175d964b584b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2246,7 +2246,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!( self, rvalue, - "Unexpected CastKind::Transmute, should only appear after lowering_intrinsics", + "Unexpected CastKind::Transmute, which is not permitted in Analysis MIR", ); } } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 84e0e24f99a47..32d134dca6da9 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -620,23 +620,41 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } CastKind::Transmute => { - let src_layout = self.tcx.layout_of(self.param_env.and(op_ty)); - let dst_layout = self.tcx.layout_of(self.param_env.and(*target_type)); - if let Err(e) = src_layout { + if let MirPhase::Runtime(..) = self.mir_phase { + // `mem::transmute` currently requires types concrete enough + // to *know* that their sizes are the same, and we repeat + // that check here. This restriction may be lifted in future, + // should some optimizations need to it be more general. + // (It might just end up being UB if they don't match, say.) + + let src_layout = self.tcx.layout_of(self.param_env.and(op_ty)); + let dst_layout = self.tcx.layout_of(self.param_env.and(*target_type)); + if let Err(e) = src_layout { + self.fail( + location, + format!( + "Unable to compute layout for source type {op_ty:?}: {e}" + ), + ); + } + if let Err(e) = dst_layout { + self.fail(location, format!("Unable to compute layout for destination type {target_type:?}: {e}")); + } + + if let (Ok(src_layout), Ok(dst_layout)) = (src_layout, dst_layout) { + if src_layout.layout.size() != dst_layout.layout.size() { + self.fail(location, format!("Source and destination layouts have different sizes: {src_layout:?} vs {dst_layout:?}")); + } + } + } else { self.fail( location, - format!("Unable to compute layout for source type {op_ty:?}: {e}"), + format!( + "Transmute is not supported in non-runtime phase {:?}.", + self.mir_phase + ), ); } - if let Err(e) = dst_layout { - self.fail(location, format!("Unable to compute layout for destination type {target_type:?}: {e}")); - } - - if let (Ok(src_layout), Ok(dst_layout)) = (src_layout, dst_layout) { - if src_layout.layout.size() != dst_layout.layout.size() { - self.fail(location, format!("Source and destination layouts have different sizes: {src_layout:?} vs {dst_layout:?}")); - } - } } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index d1234d3a38974..0ff76963ddbe7 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -83,6 +83,9 @@ pub enum MirPhase { /// access to. This occurs in generator bodies. Such locals do not behave like other locals, /// because they eg may be aliased in surprising ways. Runtime MIR has no such special locals - /// all generator bodies are lowered and so all places that look like locals really are locals. + /// - Intrinsics: In analysis MIR, intrinsics are typically represented as [`TerminatorKind::Call`]s + /// to their `extern "rust-intrinsic"` declarations. In runtime MIR, some intrinsics are lowered + /// to MIR constructs not used earlier, such as `mem::transmute` → [`CastKind::Transmute`]. /// /// Also note that the lint pass which reports eg `200_u8 + 200_u8` as an error is run as a part /// of analysis to runtime MIR lowering. To ensure lints are reported reliably, this means that @@ -1190,6 +1193,8 @@ pub enum CastKind { PtrToPtr, FnPtrToPtr, /// Reinterpret the bits of the input as a different type. + /// + /// Allowed only in [`MirPhase::Runtime`]; Earlier it's a [`TerminatorKind::Call`]. Transmute, } From 6f49f85d95c0288d1b408bba46d12b7f9cf64881 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 26 Feb 2023 20:40:52 -0800 Subject: [PATCH 4/4] Oh, right, tidy got even more persnickety --- compiler/rustc_const_eval/src/transform/validate.rs | 2 +- compiler/rustc_middle/src/mir/syntax.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 32d134dca6da9..4a45ea641cdf8 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -623,7 +623,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if let MirPhase::Runtime(..) = self.mir_phase { // `mem::transmute` currently requires types concrete enough // to *know* that their sizes are the same, and we repeat - // that check here. This restriction may be lifted in future, + // that check here. This restriction may be lifted in future, // should some optimizations need to it be more general. // (It might just end up being UB if they don't match, say.) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 0ff76963ddbe7..72efa1e27e47a 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -84,7 +84,7 @@ pub enum MirPhase { /// because they eg may be aliased in surprising ways. Runtime MIR has no such special locals - /// all generator bodies are lowered and so all places that look like locals really are locals. /// - Intrinsics: In analysis MIR, intrinsics are typically represented as [`TerminatorKind::Call`]s - /// to their `extern "rust-intrinsic"` declarations. In runtime MIR, some intrinsics are lowered + /// to their `extern "rust-intrinsic"` declarations. In runtime MIR, some intrinsics are lowered /// to MIR constructs not used earlier, such as `mem::transmute` → [`CastKind::Transmute`]. /// /// Also note that the lint pass which reports eg `200_u8 + 200_u8` as an error is run as a part