Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -205,6 +207,97 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
)
}

fn parse_cast_fn_ptr_safety(&self, expr_id: ExprId) -> PResult<Safety> {
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<PointerCoercion> {
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<CastKind> {
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<Rvalue<'tcx>> {
parse_by_kind!(self, expr_id, expr, "rvalue",
@call(mir_discriminant, args) => self.parse_place(args[0]).map(Rvalue::Discriminant),
Expand All @@ -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 } => {
Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ symbols! {
Arc,
ArcWeak,
Array,
ArrayToPointer,
AsMut,
AsRef,
AssertParamIsClone,
Expand All @@ -176,6 +177,7 @@ symbols! {
Bool,
Borrow,
BorrowMut,
BoxDerefTransmute,
Break,
BuildHasher,
CStr,
Expand All @@ -187,6 +189,7 @@ symbols! {
Cleanup,
Client,
Clone,
ClosureFnPointer,
CoercePointee,
CoercePointeeValidated,
CoerceShared,
Expand Down Expand Up @@ -219,11 +222,14 @@ symbols! {
ExternC,
ExternRust,
Float,
FloatToFloat,
FloatToInt,
FmtArgumentsNew,
Fn,
FnMut,
FnOnce,
FnPtr,
FnPtrToPtr,
Formatter,
Forward,
Found,
Expand All @@ -239,6 +245,8 @@ symbols! {
IndexOutput,
Input,
Int,
IntToFloat,
IntToInt,
Into,
IntoAsyncIterator,
IntoFuture,
Expand All @@ -255,6 +263,7 @@ symbols! {
Lifetime,
LintPass,
LocalKey,
MutToConstPointer,
Mutex,
MutexGuard,
Named,
Expand All @@ -274,7 +283,11 @@ symbols! {
PinDerefMutHelper,
PinMacroHelper,
Pointer,
PointerCoercion,
PointerExposeProvenance,
PointerWithExposedProvenance,
Poll,
PtrToPtr,
Range,
RangeCopy,
RangeFrom,
Expand All @@ -294,6 +307,7 @@ symbols! {
Reborrow,
RefCell,
Reference,
ReifyFnPointer,
Relaxed,
Release,
Result,
Expand All @@ -306,6 +320,8 @@ symbols! {
RwLock,
RwLockReadGuard,
RwLockWriteGuard,
Safe,
Safety,
SelfTy,
Send,
SeqCst,
Expand All @@ -320,12 +336,14 @@ symbols! {
String,
Struct,
StructuralPartialEq,
Subtype,
SymbolIntern,
Sync,
SyncUnsafeCell,
Target,
This,
TokenStream,
Transmute,
TrivialClone,
Try,
TryCaptureGeneric,
Expand All @@ -339,7 +357,10 @@ symbols! {
Type,
Union,
Unresolved,
Unsafe,
UnsafeFnPointer,
Unsize,
UnsizePointee,
Vec,
Wrapper,
_DECLS,
Expand Down Expand Up @@ -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,
Expand Down
35 changes: 35 additions & 0 deletions library/core/src/intrinsics/mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,41 @@ define!(
fn __debuginfo<T>(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,

@maxdexh maxdexh Aug 18, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: If this is named Unsize, then std::marker::Unsized doesn't get abbreviated to Unsize in diagnostics anymore.

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then std::marker::Unsized doesn't get abbreviated to Unsize in diagnostics anymore.

Did you mean "abbreviated to Unsized"?

Please file an issue for this... it is strange how adding unstable items changes diagnostics for stable code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to my knowledge we already do this correctly, it affects nightly code, but a stable compiler will not consider unstable items in the diagnostics

}
#[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<T, U>(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
Expand Down
80 changes: 80 additions & 0 deletions tests/mir-opt/building/custom/arbitrary_cast.rs
Original file line number Diff line number Diff line change
@@ -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::<fn(&i32), fn(&'static i32)>(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);
}
4 changes: 2 additions & 2 deletions tests/ui/hygiene/unpretty-debug-lifetimes.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -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 */>() {}
Expand Down
Loading