Skip to content

Commit 809cbb1

Browse files
committed
Add an intrinsic for ptr::metadata
1 parent fb89862 commit 809cbb1

25 files changed

+410
-43
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2398,11 +2398,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
23982398
}
23992399
}
24002400
}
2401-
CastKind::Transmute => {
2401+
CastKind::PtrToMetadata | CastKind::Transmute => {
24022402
span_mirbug!(
24032403
self,
24042404
rvalue,
2405-
"Unexpected CastKind::Transmute, which is not permitted in Analysis MIR",
2405+
"Unexpected CastKind::{cast_kind:?}, which is not permitted in Analysis MIR",
24062406
);
24072407
}
24082408
}

compiler/rustc_codegen_cranelift/src/base.rs

+11
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,17 @@ fn codegen_stmt<'tcx>(
698698
lval.write_cvalue(fx, CValue::by_val(res, dest_layout));
699699
}
700700
}
701+
Rvalue::Cast(CastKind::PtrToMetadata, ref operand, _to_ty) => {
702+
let operand = codegen_operand(fx, operand);
703+
let meta = match operand.layout().abi {
704+
Abi::Scalar(_) => CValue::zst(dest_layout),
705+
Abi::ScalarPair(_, _) => {
706+
CValue::by_val(operand.load_scalar_pair(fx).1, dest_layout)
707+
}
708+
_ => bug!("Unexpected `PtrToMetadata` operand: {operand:?}"),
709+
};
710+
lval.write_cvalue(fx, meta);
711+
}
701712
Rvalue::Cast(
702713
CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)),
703714
ref operand,

compiler/rustc_codegen_cranelift/src/constant.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub(crate) fn codegen_const_value<'tcx>(
100100
assert!(layout.is_sized(), "unsized const value");
101101

102102
if layout.is_zst() {
103-
return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout);
103+
return CValue::zst(layout);
104104
}
105105

106106
match const_val {

compiler/rustc_codegen_cranelift/src/value_and_place.rs

+8
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ impl<'tcx> CValue<'tcx> {
111111
CValue(inner, layout)
112112
}
113113

114+
/// Create an instance of a ZST
115+
///
116+
/// The is represented by a dangling pointer of suitable alignment.
117+
pub(crate) fn zst(layout: TyAndLayout<'tcx>) -> CValue<'tcx> {
118+
assert!(layout.is_zst());
119+
CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout)
120+
}
121+
114122
pub(crate) fn layout(&self) -> TyAndLayout<'tcx> {
115123
self.1
116124
}

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+7
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
565565
};
566566
OperandValue::Immediate(newval)
567567
}
568+
mir::CastKind::PtrToMetadata => {
569+
match operand.val {
570+
OperandValue::Immediate(_) => OperandValue::ZeroSized,
571+
OperandValue::Pair(_, meta) => OperandValue::Immediate(meta),
572+
_ => bug!("Unexpected operand to `PtrToMetadata`: {operand:?}"),
573+
}
574+
}
568575
mir::CastKind::Transmute => {
569576
self.codegen_transmute_operand(bx, operand, cast).unwrap_or_else(|| {
570577
bug!("Unsupported transmute-as-operand of {operand:?} to {cast:?}");

compiler/rustc_const_eval/src/interpret/cast.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
6464
self.write_immediate(*res, dest)?;
6565
}
6666

67+
CastKind::PtrToMetadata => {
68+
let src = self.read_immediate(src)?;
69+
let res = self.ptr_to_metadata(&src, cast_layout)?;
70+
self.write_immediate(*res, dest)?;
71+
}
72+
6773
CastKind::PointerCoercion(
6874
PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer,
6975
) => {
@@ -204,7 +210,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
204210
assert!(cast_to.ty.is_unsafe_ptr());
205211
// Handle casting any ptr to raw ptr (might be a fat ptr).
206212
if cast_to.size == src.layout.size {
207-
// Thin or fat pointer that just hast the ptr kind of target type changed.
213+
// Thin or fat pointer that just has the ptr kind of target type changed.
208214
return Ok(ImmTy::from_immediate(**src, cast_to));
209215
} else {
210216
// Casting the metadata away from a fat ptr.
@@ -225,6 +231,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
225231
}
226232
}
227233

234+
fn ptr_to_metadata(
235+
&self,
236+
src: &ImmTy<'tcx, M::Provenance>,
237+
cast_to: TyAndLayout<'tcx>,
238+
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
239+
assert!(src.layout.ty.is_unsafe_ptr());
240+
return Ok(match **src {
241+
Immediate::Scalar(_) => {
242+
assert!(cast_to.is_zst());
243+
ImmTy::uninit(cast_to)
244+
}
245+
Immediate::ScalarPair(_, meta) => ImmTy::from_scalar(meta, cast_to),
246+
Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
247+
});
248+
}
249+
228250
pub fn pointer_expose_provenance_cast(
229251
&mut self,
230252
src: &ImmTy<'tcx, M::Provenance>,

compiler/rustc_const_eval/src/transform/validate.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
11351135
self.fail(location, "Can't cast {op_ty} into 'Ptr'");
11361136
}
11371137
}
1138+
CastKind::PtrToMetadata => {
1139+
if !op_ty.is_unsafe_ptr() {
1140+
self.fail(location, "Can't get ptr metadata from {op_ty}");
1141+
}
1142+
let pointee_ty = op_ty.builtin_deref(true).unwrap().ty;
1143+
1144+
// FIXME: Check metadata more generally
1145+
if pointee_ty.is_slice() {
1146+
if !self.mir_assign_valid_types(*target_type, self.tcx.types.usize) {
1147+
self.fail(location, "slice metadata must be usize");
1148+
}
1149+
} else if pointee_ty.is_sized(self.tcx, self.param_env) {
1150+
if *target_type != self.tcx.types.unit {
1151+
self.fail(location, "metadata for pointer-to-thin must be unit");
1152+
}
1153+
}
1154+
}
11381155
CastKind::FloatToFloat | CastKind::FloatToInt => {
11391156
if !op_ty.is_floating_point() || !target_type.is_numeric() {
11401157
self.fail(

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
129129
| sym::is_val_statically_known
130130
| sym::ptr_mask
131131
| sym::aggregate_raw_ptr
132+
| sym::ptr_metadata
132133
| sym::ub_checks
133134
| sym::fadd_algebraic
134135
| sym::fsub_algebraic
@@ -578,6 +579,7 @@ pub fn check_intrinsic_type(
578579
// This type check is not particularly useful, but the `where` bounds
579580
// on the definition in `core` do the heavy lifting for checking it.
580581
sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)),
582+
sym::ptr_metadata => (2, 1, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),
581583

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

compiler/rustc_middle/src/mir/statement.rs

+1
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ impl<'tcx> Rvalue<'tcx> {
425425
| CastKind::IntToFloat
426426
| CastKind::FnPtrToPtr
427427
| CastKind::PtrToPtr
428+
| CastKind::PtrToMetadata
428429
| CastKind::PointerCoercion(_)
429430
| CastKind::PointerWithExposedProvenance
430431
| CastKind::DynStar

compiler/rustc_middle/src/mir/syntax.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,17 @@ pub enum CastKind {
13251325
IntToFloat,
13261326
PtrToPtr,
13271327
FnPtrToPtr,
1328+
/// Convert a raw pointer to an `impl Pointee<Metadata = M>` into `M`.
1329+
///
1330+
/// For example, this will give a `()` from `*const i32`, a `usize` from
1331+
/// `*mut [u8]`, or a vtable from a `*const dyn Foo`.
1332+
///
1333+
/// There's only one legal cast type based on the input type, but calculating
1334+
/// that type is expensive, and thus it's convenient for this to be a `Cast`
1335+
/// instead of an `UnOp`.
1336+
///
1337+
/// Allowed only in [`MirPhase::Runtime`]; Earlier it's an intrinsic.
1338+
PtrToMetadata,
13281339
/// Reinterpret the bits of the input as a different type.
13291340
///
13301341
/// MIR is well-formed if the input and output types have different sizes,

compiler/rustc_mir_transform/src/lower_intrinsics.rs

+18
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,24 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
315315

316316
terminator.kind = TerminatorKind::Goto { target };
317317
}
318+
sym::ptr_metadata => {
319+
let Ok([ptr]) = <[_; 1]>::try_from(std::mem::take(args)) else {
320+
span_bug!(
321+
terminator.source_info.span,
322+
"Wrong number of arguments for ptr_metadata intrinsic",
323+
);
324+
};
325+
let target = target.unwrap();
326+
let metadata_ty = generic_args.type_at(1);
327+
block.statements.push(Statement {
328+
source_info: terminator.source_info,
329+
kind: StatementKind::Assign(Box::new((
330+
*destination,
331+
Rvalue::Cast(CastKind::PtrToMetadata, ptr.node, metadata_ty),
332+
))),
333+
});
334+
terminator.kind = TerminatorKind::Goto { target };
335+
}
318336
_ => {}
319337
}
320338
}

compiler/rustc_smir/src/rustc_smir/convert/mir.rs

+1
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ impl<'tcx> Stable<'tcx> for mir::CastKind {
276276
FloatToFloat => stable_mir::mir::CastKind::FloatToFloat,
277277
IntToFloat => stable_mir::mir::CastKind::IntToFloat,
278278
PtrToPtr => stable_mir::mir::CastKind::PtrToPtr,
279+
PtrToMetadata => stable_mir::mir::CastKind::PtrToMetadata,
279280
FnPtrToPtr => stable_mir::mir::CastKind::FnPtrToPtr,
280281
Transmute => stable_mir::mir::CastKind::Transmute,
281282
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,7 @@ symbols! {
14171417
ptr_guaranteed_cmp,
14181418
ptr_is_null,
14191419
ptr_mask,
1420+
ptr_metadata,
14201421
ptr_null,
14211422
ptr_null_mut,
14221423
ptr_offset_from,

compiler/stable_mir/src/mir/body.rs

+1
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,7 @@ pub enum CastKind {
949949
FloatToFloat,
950950
IntToFloat,
951951
PtrToPtr,
952+
PtrToMetadata,
952953
FnPtrToPtr,
953954
Transmute,
954955
}

library/core/src/intrinsics.rs

+15
Original file line numberDiff line numberDiff line change
@@ -2807,6 +2807,21 @@ impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
28072807
type Metadata = <P as ptr::Pointee>::Metadata;
28082808
}
28092809

2810+
/// Lowers in MIR to `Rvalue::Cast` with `CastKind::PtrToMetadata`.
2811+
///
2812+
/// This is used to implement functions like `ptr::metadata`.
2813+
#[rustc_nounwind]
2814+
#[unstable(feature = "core_intrinsics", issue = "none")]
2815+
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
2816+
#[rustc_intrinsic]
2817+
#[rustc_intrinsic_must_be_overridden]
2818+
#[cfg(not(bootstrap))]
2819+
pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *const P) -> M {
2820+
// To implement a fallback we'd have to assume the layout of the pointer,
2821+
// but the whole point of this intrinsic is that we shouldn't do that.
2822+
unreachable!()
2823+
}
2824+
28102825
// Some functions are defined here because they accidentally got made
28112826
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
28122827
// (`transmute` also falls into this category, but it cannot be wrapped due to the

library/core/src/ptr/metadata.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::fmt;
44
use crate::hash::{Hash, Hasher};
55
#[cfg(not(bootstrap))]
6-
use crate::intrinsics::aggregate_raw_ptr;
6+
use crate::intrinsics::{aggregate_raw_ptr, ptr_metadata};
77
use crate::marker::Freeze;
88

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

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

155162
#[repr(C)]
163+
#[cfg(bootstrap)]
156164
union PtrRepr<T: ?Sized> {
157165
const_ptr: *const T,
158166
mut_ptr: *mut T,
159167
components: PtrComponents<T>,
160168
}
161169

162170
#[repr(C)]
171+
#[cfg(bootstrap)]
163172
struct PtrComponents<T: ?Sized> {
164173
data_pointer: *const (),
165174
metadata: <T as Pointee>::Metadata,
166175
}
167176

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

171181
// Manual impl needed to avoid `T: Clone` bound.
182+
#[cfg(bootstrap)]
172183
impl<T: ?Sized> Clone for PtrComponents<T> {
173184
fn clone(&self) -> Self {
174185
*self

library/core/tests/ptr.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1171,3 +1171,11 @@ fn test_ptr_from_raw_parts_in_const() {
11711171
assert_eq!(EMPTY_SLICE_PTR.addr(), 123);
11721172
assert_eq!(EMPTY_SLICE_PTR.len(), 456);
11731173
}
1174+
1175+
#[test]
1176+
fn test_ptr_metadata_in_const() {
1177+
const ARRAY_META: () = std::ptr::metadata::<[u16; 3]>(&[1, 2, 3]);
1178+
const SLICE_META: usize = std::ptr::metadata::<[u16]>(&[1, 2, 3]);
1179+
assert_eq!(ARRAY_META, ());
1180+
assert_eq!(SLICE_META, 3);
1181+
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//@ compile-flags: -O -C no-prepopulate-passes -Z inline-mir
2+
//@ only-64bit (so I don't need to worry about usize)
3+
4+
#![crate_type = "lib"]
5+
#![feature(core_intrinsics)]
6+
7+
use std::intrinsics::ptr_metadata;
8+
9+
// CHECK-LABEL: @thin_metadata(
10+
#[no_mangle]
11+
pub fn thin_metadata(p: *const ()) {
12+
// CHECK: start
13+
// CHECK-NEXT: ret void
14+
ptr_metadata(p)
15+
}
16+
17+
// CHECK-LABEL: @slice_metadata(
18+
#[no_mangle]
19+
pub fn slice_metadata(p: *const [u8]) -> usize {
20+
// CHECK: start
21+
// CHECK-NEXT: ret i64 %p.1
22+
ptr_metadata(p)
23+
}
24+
25+
// CHECK-LABEL: @dyn_byte_offset(
26+
#[no_mangle]
27+
pub unsafe fn dyn_byte_offset(
28+
p: *const dyn std::fmt::Debug,
29+
n: usize,
30+
) -> *const dyn std::fmt::Debug {
31+
// CHECK: %[[Q:.+]] = getelementptr inbounds i8, ptr %p.0, i64 %n
32+
// CHECK: %[[TEMP1:.+]] = insertvalue { ptr, ptr } poison, ptr %[[Q]], 0
33+
// CHECK: %[[TEMP2:.+]] = insertvalue { ptr, ptr } %[[TEMP1]], ptr %p.1, 1
34+
// CHECK: ret { ptr, ptr } %[[TEMP2]]
35+
p.byte_add(n)
36+
}

0 commit comments

Comments
 (0)