Skip to content

Commit

Permalink
Add an intrinsic for ptr::metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
scottmcm committed Apr 22, 2024
1 parent fb89862 commit 809cbb1
Show file tree
Hide file tree
Showing 25 changed files with 410 additions and 43 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2398,11 +2398,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
}
CastKind::Transmute => {
CastKind::PtrToMetadata | CastKind::Transmute => {
span_mirbug!(
self,
rvalue,
"Unexpected CastKind::Transmute, which is not permitted in Analysis MIR",
"Unexpected CastKind::{cast_kind:?}, which is not permitted in Analysis MIR",
);
}
}
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,17 @@ fn codegen_stmt<'tcx>(
lval.write_cvalue(fx, CValue::by_val(res, dest_layout));
}
}
Rvalue::Cast(CastKind::PtrToMetadata, ref operand, _to_ty) => {
let operand = codegen_operand(fx, operand);
let meta = match operand.layout().abi {
Abi::Scalar(_) => CValue::zst(dest_layout),
Abi::ScalarPair(_, _) => {
CValue::by_val(operand.load_scalar_pair(fx).1, dest_layout)
}
_ => bug!("Unexpected `PtrToMetadata` operand: {operand:?}"),
};
lval.write_cvalue(fx, meta);
}
Rvalue::Cast(
CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)),
ref operand,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ pub(crate) fn codegen_const_value<'tcx>(
assert!(layout.is_sized(), "unsized const value");

if layout.is_zst() {
return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout);
return CValue::zst(layout);
}

match const_val {
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_codegen_cranelift/src/value_and_place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ impl<'tcx> CValue<'tcx> {
CValue(inner, layout)
}

/// Create an instance of a ZST
///
/// The is represented by a dangling pointer of suitable alignment.
pub(crate) fn zst(layout: TyAndLayout<'tcx>) -> CValue<'tcx> {
assert!(layout.is_zst());
CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout)
}

pub(crate) fn layout(&self) -> TyAndLayout<'tcx> {
self.1
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
};
OperandValue::Immediate(newval)
}
mir::CastKind::PtrToMetadata => {
match operand.val {
OperandValue::Immediate(_) => OperandValue::ZeroSized,
OperandValue::Pair(_, meta) => OperandValue::Immediate(meta),
_ => bug!("Unexpected operand to `PtrToMetadata`: {operand:?}"),
}
}
mir::CastKind::Transmute => {
self.codegen_transmute_operand(bx, operand, cast).unwrap_or_else(|| {
bug!("Unsupported transmute-as-operand of {operand:?} to {cast:?}");
Expand Down
24 changes: 23 additions & 1 deletion compiler/rustc_const_eval/src/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.write_immediate(*res, dest)?;
}

CastKind::PtrToMetadata => {
let src = self.read_immediate(src)?;
let res = self.ptr_to_metadata(&src, cast_layout)?;
self.write_immediate(*res, dest)?;
}

CastKind::PointerCoercion(
PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer,
) => {
Expand Down Expand Up @@ -204,7 +210,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
assert!(cast_to.ty.is_unsafe_ptr());
// Handle casting any ptr to raw ptr (might be a fat ptr).
if cast_to.size == src.layout.size {
// Thin or fat pointer that just hast the ptr kind of target type changed.
// Thin or fat pointer that just has the ptr kind of target type changed.
return Ok(ImmTy::from_immediate(**src, cast_to));
} else {
// Casting the metadata away from a fat ptr.
Expand All @@ -225,6 +231,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}

fn ptr_to_metadata(
&self,
src: &ImmTy<'tcx, M::Provenance>,
cast_to: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
assert!(src.layout.ty.is_unsafe_ptr());
return Ok(match **src {
Immediate::Scalar(_) => {
assert!(cast_to.is_zst());
ImmTy::uninit(cast_to)
}
Immediate::ScalarPair(_, meta) => ImmTy::from_scalar(meta, cast_to),
Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
});
}

pub fn pointer_expose_provenance_cast(
&mut self,
src: &ImmTy<'tcx, M::Provenance>,
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(location, "Can't cast {op_ty} into 'Ptr'");
}
}
CastKind::PtrToMetadata => {
if !op_ty.is_unsafe_ptr() {
self.fail(location, "Can't get ptr metadata from {op_ty}");
}
let pointee_ty = op_ty.builtin_deref(true).unwrap().ty;

// FIXME: Check metadata more generally
if pointee_ty.is_slice() {
if !self.mir_assign_valid_types(*target_type, self.tcx.types.usize) {
self.fail(location, "slice metadata must be usize");
}
} else if pointee_ty.is_sized(self.tcx, self.param_env) {
if *target_type != self.tcx.types.unit {
self.fail(location, "metadata for pointer-to-thin must be unit");
}
}
}
CastKind::FloatToFloat | CastKind::FloatToInt => {
if !op_ty.is_floating_point() || !target_type.is_numeric() {
self.fail(
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
| sym::is_val_statically_known
| sym::ptr_mask
| sym::aggregate_raw_ptr
| sym::ptr_metadata
| sym::ub_checks
| sym::fadd_algebraic
| sym::fsub_algebraic
Expand Down Expand Up @@ -578,6 +579,7 @@ pub fn check_intrinsic_type(
// This type check is not particularly useful, but the `where` bounds
// on the definition in `core` do the heavy lifting for checking it.
sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)),
sym::ptr_metadata => (2, 1, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),

sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool),

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/mir/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ impl<'tcx> Rvalue<'tcx> {
| CastKind::IntToFloat
| CastKind::FnPtrToPtr
| CastKind::PtrToPtr
| CastKind::PtrToMetadata
| CastKind::PointerCoercion(_)
| CastKind::PointerWithExposedProvenance
| CastKind::DynStar
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1325,6 +1325,17 @@ pub enum CastKind {
IntToFloat,
PtrToPtr,
FnPtrToPtr,
/// Convert a raw pointer to an `impl Pointee<Metadata = M>` into `M`.
///
/// For example, this will give a `()` from `*const i32`, a `usize` from
/// `*mut [u8]`, or a vtable from a `*const dyn Foo`.
///
/// There's only one legal cast type based on the input type, but calculating
/// that type is expensive, and thus it's convenient for this to be a `Cast`
/// instead of an `UnOp`.
///
/// Allowed only in [`MirPhase::Runtime`]; Earlier it's an intrinsic.
PtrToMetadata,
/// Reinterpret the bits of the input as a different type.
///
/// MIR is well-formed if the input and output types have different sizes,
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_mir_transform/src/lower_intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,24 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {

terminator.kind = TerminatorKind::Goto { target };
}
sym::ptr_metadata => {
let Ok([ptr]) = <[_; 1]>::try_from(std::mem::take(args)) else {
span_bug!(
terminator.source_info.span,
"Wrong number of arguments for ptr_metadata intrinsic",
);
};
let target = target.unwrap();
let metadata_ty = generic_args.type_at(1);
block.statements.push(Statement {
source_info: terminator.source_info,
kind: StatementKind::Assign(Box::new((
*destination,
Rvalue::Cast(CastKind::PtrToMetadata, ptr.node, metadata_ty),
))),
});
terminator.kind = TerminatorKind::Goto { target };
}
_ => {}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_smir/src/rustc_smir/convert/mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ impl<'tcx> Stable<'tcx> for mir::CastKind {
FloatToFloat => stable_mir::mir::CastKind::FloatToFloat,
IntToFloat => stable_mir::mir::CastKind::IntToFloat,
PtrToPtr => stable_mir::mir::CastKind::PtrToPtr,
PtrToMetadata => stable_mir::mir::CastKind::PtrToMetadata,
FnPtrToPtr => stable_mir::mir::CastKind::FnPtrToPtr,
Transmute => stable_mir::mir::CastKind::Transmute,
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1417,6 +1417,7 @@ symbols! {
ptr_guaranteed_cmp,
ptr_is_null,
ptr_mask,
ptr_metadata,
ptr_null,
ptr_null_mut,
ptr_offset_from,
Expand Down
1 change: 1 addition & 0 deletions compiler/stable_mir/src/mir/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,7 @@ pub enum CastKind {
FloatToFloat,
IntToFloat,
PtrToPtr,
PtrToMetadata,
FnPtrToPtr,
Transmute,
}
Expand Down
15 changes: 15 additions & 0 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2807,6 +2807,21 @@ impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
type Metadata = <P as ptr::Pointee>::Metadata;
}

/// Lowers in MIR to `Rvalue::Cast` with `CastKind::PtrToMetadata`.
///
/// This is used to implement functions like `ptr::metadata`.
#[rustc_nounwind]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[cfg(not(bootstrap))]
pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *const P) -> M {
// To implement a fallback we'd have to assume the layout of the pointer,
// but the whole point of this intrinsic is that we shouldn't do that.
unreachable!()
}

// Some functions are defined here because they accidentally got made
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
// (`transmute` also falls into this category, but it cannot be wrapped due to the
Expand Down
21 changes: 16 additions & 5 deletions library/core/src/ptr/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::fmt;
use crate::hash::{Hash, Hasher};
#[cfg(not(bootstrap))]
use crate::intrinsics::aggregate_raw_ptr;
use crate::intrinsics::{aggregate_raw_ptr, ptr_metadata};
use crate::marker::Freeze;

/// Provides the pointer metadata type of any pointed-to type.
Expand Down Expand Up @@ -95,10 +95,17 @@ pub trait Thin = Pointee<Metadata = ()>;
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[inline]
pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
#[cfg(bootstrap)]
{
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
}
#[cfg(not(bootstrap))]
{
ptr_metadata(ptr)
}
}

/// Forms a (possibly-wide) raw pointer from a data pointer and metadata.
Expand Down Expand Up @@ -153,22 +160,26 @@ pub const fn from_raw_parts_mut<T: ?Sized>(
}

#[repr(C)]
#[cfg(bootstrap)]
union PtrRepr<T: ?Sized> {
const_ptr: *const T,
mut_ptr: *mut T,
components: PtrComponents<T>,
}

#[repr(C)]
#[cfg(bootstrap)]
struct PtrComponents<T: ?Sized> {
data_pointer: *const (),
metadata: <T as Pointee>::Metadata,
}

// Manual impl needed to avoid `T: Copy` bound.
#[cfg(bootstrap)]
impl<T: ?Sized> Copy for PtrComponents<T> {}

// Manual impl needed to avoid `T: Clone` bound.
#[cfg(bootstrap)]
impl<T: ?Sized> Clone for PtrComponents<T> {
fn clone(&self) -> Self {
*self
Expand Down
8 changes: 8 additions & 0 deletions library/core/tests/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1171,3 +1171,11 @@ fn test_ptr_from_raw_parts_in_const() {
assert_eq!(EMPTY_SLICE_PTR.addr(), 123);
assert_eq!(EMPTY_SLICE_PTR.len(), 456);
}

#[test]
fn test_ptr_metadata_in_const() {
const ARRAY_META: () = std::ptr::metadata::<[u16; 3]>(&[1, 2, 3]);
const SLICE_META: usize = std::ptr::metadata::<[u16]>(&[1, 2, 3]);
assert_eq!(ARRAY_META, ());
assert_eq!(SLICE_META, 3);
}
36 changes: 36 additions & 0 deletions tests/codegen/intrinsics/ptr_metadata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//@ compile-flags: -O -C no-prepopulate-passes -Z inline-mir
//@ only-64bit (so I don't need to worry about usize)

#![crate_type = "lib"]
#![feature(core_intrinsics)]

use std::intrinsics::ptr_metadata;

// CHECK-LABEL: @thin_metadata(
#[no_mangle]
pub fn thin_metadata(p: *const ()) {
// CHECK: start
// CHECK-NEXT: ret void
ptr_metadata(p)
}

// CHECK-LABEL: @slice_metadata(
#[no_mangle]
pub fn slice_metadata(p: *const [u8]) -> usize {
// CHECK: start
// CHECK-NEXT: ret i64 %p.1
ptr_metadata(p)
}

// CHECK-LABEL: @dyn_byte_offset(
#[no_mangle]
pub unsafe fn dyn_byte_offset(
p: *const dyn std::fmt::Debug,
n: usize,
) -> *const dyn std::fmt::Debug {
// CHECK: %[[Q:.+]] = getelementptr inbounds i8, ptr %p.0, i64 %n
// CHECK: %[[TEMP1:.+]] = insertvalue { ptr, ptr } poison, ptr %[[Q]], 0
// CHECK: %[[TEMP2:.+]] = insertvalue { ptr, ptr } %[[TEMP1]], ptr %p.1, 1
// CHECK: ret { ptr, ptr } %[[TEMP2]]
p.byte_add(n)
}
Loading

0 comments on commit 809cbb1

Please sign in to comment.