diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 84203c5caefea..5454a7bef67a7 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -1,8 +1,10 @@ use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_hir::Safety; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty; +use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::mir_cast_kind; use rustc_span::{Span, Spanned}; @@ -205,6 +207,97 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { ) } + fn parse_cast_fn_ptr_safety(&self, expr_id: ExprId) -> PResult { + parse_by_kind!(self, expr_id, _, "function pointer safety", + @variant(mir_cast_fn_ptr_safety, Safe) => { + Ok(Safety::Safe) + }, + @variant(mir_cast_fn_ptr_safety, Unsafe) => { + Ok(Safety::Unsafe) + }, + ) + } + + fn parse_cast_pointer_coercion(&self, expr_id: ExprId) -> PResult { + parse_by_kind!(self, expr_id, expr, "pointer coercion kind", + @variant(mir_cast_ptr_coercion, ReifyFnPointer) => { + let ExprKind::Adt(AdtExpr { fields, .. }) = &expr.kind else { + unreachable!("already matched") + }; + Ok(PointerCoercion::ReifyFnPointer( + self.parse_cast_fn_ptr_safety(fields[0].expr)?, + )) + }, + @variant(mir_cast_ptr_coercion, UnsafeFnPointer) => { + Ok(PointerCoercion::UnsafeFnPointer) + }, + @variant(mir_cast_ptr_coercion, ClosureFnPointer) => { + let ExprKind::Adt(AdtExpr { fields, .. }) = &expr.kind else { + unreachable!("already matched") + }; + Ok(PointerCoercion::ClosureFnPointer( + self.parse_cast_fn_ptr_safety(fields[0].expr)?, + )) + }, + @variant(mir_cast_ptr_coercion, MutToConstPointer) => { + Ok(PointerCoercion::MutToConstPointer) + }, + @variant(mir_cast_ptr_coercion, ArrayToPointer) => { + Ok(PointerCoercion::ArrayToPointer) + }, + @variant(mir_cast_ptr_coercion, UnsizePointee) => { + Ok(PointerCoercion::Unsize) + }, + ) + } + + fn parse_cast_kind(&self, expr_id: ExprId) -> PResult { + parse_by_kind!(self, expr_id, expr, "cast kind", + @variant(mir_cast_kind, PointerExposeProvenance) => { + Ok(CastKind::PointerExposeProvenance) + }, + @variant(mir_cast_kind, PointerWithExposedProvenance) => { + Ok(CastKind::PointerWithExposedProvenance) + }, + @variant(mir_cast_kind, IntToInt) => { + Ok(CastKind::IntToInt) + }, + @variant(mir_cast_kind, FloatToInt) => { + Ok(CastKind::FloatToInt) + }, + @variant(mir_cast_kind, FloatToFloat) => { + Ok(CastKind::FloatToFloat) + }, + @variant(mir_cast_kind, IntToFloat) => { + Ok(CastKind::IntToFloat) + }, + @variant(mir_cast_kind, PtrToPtr) => { + Ok(CastKind::PtrToPtr) + }, + @variant(mir_cast_kind, FnPtrToPtr) => { + Ok(CastKind::FnPtrToPtr) + }, + @variant(mir_cast_kind, Transmute) => { + Ok(CastKind::Transmute) + }, + @variant(mir_cast_kind, BoxDerefTransmute) => { + Ok(CastKind::BoxDerefTransmute) + }, + @variant(mir_cast_kind, Subtype) => { + Ok(CastKind::Subtype) + }, + @variant(mir_cast_kind, PointerCoercion) => { + let ExprKind::Adt(AdtExpr { fields, .. }) = &expr.kind else { + unreachable!("already matched") + }; + Ok(CastKind::PointerCoercion( + self.parse_cast_pointer_coercion(fields[0].expr)?, + CoercionSource::AsCast, + )) + }, + ) + } + fn parse_rvalue(&self, expr_id: ExprId) -> PResult> { parse_by_kind!(self, expr_id, expr, "rvalue", @call(mir_discriminant, args) => self.parse_place(args[0]).map(Rvalue::Discriminant), @@ -221,6 +314,10 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let kind = CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, CoercionSource::AsCast); Ok(Rvalue::Cast(kind, source, expr.ty)) }, + @call(mir_cast, args) => { + let source = self.parse_operand(args[0])?; + Ok(Rvalue::Cast(self.parse_cast_kind(args[1])?, source, expr.ty)) + }, @call(mir_checked, args) => { parse_by_kind!(self, args[0], _, "binary op", ExprKind::Binary { op, lhs, rhs } => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 17ffb52f4b333..871d5aa97e94e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -163,6 +163,7 @@ symbols! { Arc, ArcWeak, Array, + ArrayToPointer, AsMut, AsRef, AssertParamIsClone, @@ -176,6 +177,7 @@ symbols! { Bool, Borrow, BorrowMut, + BoxDerefTransmute, Break, BuildHasher, CStr, @@ -187,6 +189,7 @@ symbols! { Cleanup, Client, Clone, + ClosureFnPointer, CoercePointee, CoercePointeeValidated, CoerceShared, @@ -219,11 +222,14 @@ symbols! { ExternC, ExternRust, Float, + FloatToFloat, + FloatToInt, FmtArgumentsNew, Fn, FnMut, FnOnce, FnPtr, + FnPtrToPtr, Formatter, Forward, Found, @@ -239,6 +245,8 @@ symbols! { IndexOutput, Input, Int, + IntToFloat, + IntToInt, Into, IntoAsyncIterator, IntoFuture, @@ -255,6 +263,7 @@ symbols! { Lifetime, LintPass, LocalKey, + MutToConstPointer, Mutex, MutexGuard, Named, @@ -274,7 +283,11 @@ symbols! { PinDerefMutHelper, PinMacroHelper, Pointer, + PointerCoercion, + PointerExposeProvenance, + PointerWithExposedProvenance, Poll, + PtrToPtr, Range, RangeCopy, RangeFrom, @@ -294,6 +307,7 @@ symbols! { Reborrow, RefCell, Reference, + ReifyFnPointer, Relaxed, Release, Result, @@ -306,6 +320,8 @@ symbols! { RwLock, RwLockReadGuard, RwLockWriteGuard, + Safe, + Safety, SelfTy, Send, SeqCst, @@ -320,12 +336,14 @@ symbols! { String, Struct, StructuralPartialEq, + Subtype, SymbolIntern, Sync, SyncUnsafeCell, Target, This, TokenStream, + Transmute, TrivialClone, Try, TryCaptureGeneric, @@ -339,7 +357,10 @@ symbols! { Type, Union, Unresolved, + Unsafe, + UnsafeFnPointer, Unsize, + UnsizePointee, Vec, Wrapper, _DECLS, @@ -1321,6 +1342,10 @@ symbols! { mir_assume, mir_basic_block, mir_call, + mir_cast, + mir_cast_fn_ptr_safety, + mir_cast_kind, + mir_cast_ptr_coercion, mir_cast_ptr_to_ptr, mir_cast_transmute, mir_cast_unsize, diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index dce7bf681a7af..929e56872674d 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -508,6 +508,41 @@ define!( fn __debuginfo(name: &'static str, s: T) ); +#[rustc_diagnostic_item = "mir_cast_fn_ptr_safety"] +pub enum Safety { + Safe, + Unsafe, +} +#[rustc_diagnostic_item = "mir_cast_ptr_coercion"] +pub enum PointerCoercion { + ReifyFnPointer(Safety), + UnsafeFnPointer, + ClosureFnPointer(Safety), + MutToConstPointer, + ArrayToPointer, + UnsizePointee, +} +#[rustc_diagnostic_item = "mir_cast_kind"] +pub enum CastKind { + PointerExposeProvenance, + PointerWithExposedProvenance, + IntToInt, + FloatToInt, + FloatToFloat, + IntToFloat, + PtrToPtr, + FnPtrToPtr, + Transmute, + BoxDerefTransmute, + Subtype, + PointerCoercion(PointerCoercion), +} +define!( + "mir_cast", + /// Emits a cast of the specified kind. + fn Cast(operand: T, kind: CastKind) -> U +); + /// Macro for generating custom MIR. /// /// See the module documentation for syntax details. This macro is not magic - it only transforms diff --git a/tests/mir-opt/building/custom/arbitrary_cast.rs b/tests/mir-opt/building/custom/arbitrary_cast.rs new file mode 100644 index 0000000000000..56e6373756b53 --- /dev/null +++ b/tests/mir-opt/building/custom/arbitrary_cast.rs @@ -0,0 +1,80 @@ +//@ skip-filecheck +#![feature(custom_mir, core_intrinsics)] + +extern crate core; +use core::intrinsics::mir::*; + +fn f(x: i32) -> i32 { + x +} + +#[custom_mir(dialect = "built")] +fn reify_fn_ptr() -> fn(i32) -> i32 { + mir! { + { + RET = Cast( + f, + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(Safety::Safe)), + ); + Return() + } + } +} + +#[custom_mir(dialect = "built")] +fn fn_ptr_to_unsafe(f: fn()) -> unsafe fn() { + mir! { + { + RET = Cast( + f, + CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer), + ); + Return() + } + } +} + +#[custom_mir(dialect = "runtime")] +fn subtype_fn_ptr(f: fn(&i32)) -> fn(&'static i32) { + mir! { + { + RET = Cast::(f, CastKind::Subtype); + Return() + } + } +} + +#[custom_mir(dialect = "built")] +fn expose_ptr(p: *const i32) -> usize { + mir! { + { + RET = Cast(p, CastKind::PointerExposeProvenance); + Return() + } + } +} + +#[custom_mir(dialect = "built")] +fn ptr_from_exposed(p: usize) -> *const i32 { + mir! { + { + RET = Cast(p, CastKind::PointerWithExposedProvenance); + Return() + } + } +} + +fn main() { + assert_eq!(reify_fn_ptr(), f as fn(i32) -> i32); + + let fn_ptr: fn() = || {}; + assert_eq!(fn_ptr as unsafe fn(), fn_ptr_to_unsafe(fn_ptr)); + + let fn_ptr: fn(&i32) = |_| {}; + assert_eq!(fn_ptr as fn(&'static i32), subtype_fn_ptr(fn_ptr)); + + let p = &1; + assert_eq!(p as *const i32 as usize, expose_ptr(p)); + + assert_eq!(ptr_from_exposed(1), 1 as *const i32); +} diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout index 689453326c0b5..c75cc7b2179d3 100644 --- a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout +++ b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout @@ -15,8 +15,8 @@ macro lifetime_hygiene /* 0#0 */ { - ($f /* 0#0 */:ident /* 0#0 */<$a /* 0#0 */:lifetime /* 0#0 */>) - => + ($f /* 0#0 */:ident /* 0#0 */<$a /* 0#0 */:lifetime /* 0#0 + */>) => { fn /* 0#0 */ $f /* 0#0 */<$a /* 0#0 */, 'a /* 0#0 */>() {} } } fn f /* 0#0 */<'a /* 0#0 */, 'a /* 0#1 */>() {}