Skip to content

Commit b3b68f1

Browse files
committed
Add an intrinsic for ptr::metadata
1 parent 7b06810 commit b3b68f1

File tree

32 files changed

+413
-59
lines changed

32 files changed

+413
-59
lines changed

compiler/rustc_codegen_cranelift/src/base.rs

+25-13
Original file line numberDiff line numberDiff line change
@@ -622,22 +622,34 @@ fn codegen_stmt<'tcx>(
622622
Rvalue::UnaryOp(un_op, ref operand) => {
623623
let operand = codegen_operand(fx, operand);
624624
let layout = operand.layout();
625-
let val = operand.load_scalar(fx);
626625
let res = match un_op {
627-
UnOp::Not => match layout.ty.kind() {
628-
ty::Bool => {
629-
let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0);
630-
CValue::by_val(res, layout)
626+
UnOp::Not => {
627+
let val = operand.load_scalar(fx);
628+
match layout.ty.kind() {
629+
ty::Bool => {
630+
let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0);
631+
CValue::by_val(res, layout)
632+
}
633+
ty::Uint(_) | ty::Int(_) => {
634+
CValue::by_val(fx.bcx.ins().bnot(val), layout)
635+
}
636+
_ => unreachable!("un op Not for {:?}", layout.ty),
631637
}
632-
ty::Uint(_) | ty::Int(_) => {
633-
CValue::by_val(fx.bcx.ins().bnot(val), layout)
638+
}
639+
UnOp::Neg => {
640+
let val = operand.load_scalar(fx);
641+
match layout.ty.kind() {
642+
ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout),
643+
ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout),
644+
_ => unreachable!("un op Neg for {:?}", layout.ty),
634645
}
635-
_ => unreachable!("un op Not for {:?}", layout.ty),
636-
},
637-
UnOp::Neg => match layout.ty.kind() {
638-
ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout),
639-
ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout),
640-
_ => unreachable!("un op Neg for {:?}", layout.ty),
646+
}
647+
UnOp::PtrMetadata => match layout.abi {
648+
Abi::Scalar(_) => CValue::zst(dest_layout),
649+
Abi::ScalarPair(_, _) => {
650+
CValue::by_val(operand.load_scalar_pair(fx).1, dest_layout)
651+
}
652+
_ => bug!("Unexpected `PtrToMetadata` operand: {operand:?}"),
641653
},
642654
};
643655
lval.write_cvalue(fx, res);

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
@@ -95,6 +95,14 @@ impl<'tcx> CValue<'tcx> {
9595
CValue(CValueInner::ByValPair(value, extra), layout)
9696
}
9797

98+
/// Create an instance of a ZST
99+
///
100+
/// The is represented by a dangling pointer of suitable alignment.
101+
pub(crate) fn zst(layout: TyAndLayout<'tcx>) -> CValue<'tcx> {
102+
assert!(layout.is_zst());
103+
CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout)
104+
}
105+
98106
pub(crate) fn layout(&self) -> TyAndLayout<'tcx> {
99107
self.1
100108
}

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -617,19 +617,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
617617

618618
mir::Rvalue::UnaryOp(op, ref operand) => {
619619
let operand = self.codegen_operand(bx, operand);
620-
let lloperand = operand.immediate();
621620
let is_float = operand.layout.ty.is_floating_point();
622-
let llval = match op {
623-
mir::UnOp::Not => bx.not(lloperand),
621+
let (val, layout) = match op {
622+
mir::UnOp::Not => {
623+
let llval = bx.not(operand.immediate());
624+
(OperandValue::Immediate(llval), operand.layout)
625+
}
624626
mir::UnOp::Neg => {
625-
if is_float {
626-
bx.fneg(lloperand)
627+
let llval = if is_float {
628+
bx.fneg(operand.immediate())
629+
} else {
630+
bx.neg(operand.immediate())
631+
};
632+
(OperandValue::Immediate(llval), operand.layout)
633+
}
634+
mir::UnOp::PtrMetadata => {
635+
debug_assert!(operand.layout.ty.is_unsafe_ptr());
636+
let (_, meta) = operand.val.pointer_parts();
637+
assert_eq!(operand.layout.fields.count() > 1, meta.is_some());
638+
if let Some(meta) = meta {
639+
(OperandValue::Immediate(meta), operand.layout.field(self.cx, 1))
627640
} else {
628-
bx.neg(lloperand)
641+
(OperandValue::ZeroSized, bx.cx().layout_of(bx.tcx().types.unit))
629642
}
630643
}
631644
};
632-
OperandRef { val: OperandValue::Immediate(llval), layout: operand.layout }
645+
debug_assert!(
646+
val.is_expected_variant_for_type(self.cx, layout),
647+
"Made wrong variant {val:?} for type {layout:?}",
648+
);
649+
OperandRef { val, layout }
633650
}
634651

635652
mir::Rvalue::Discriminant(ref place) => {
@@ -712,8 +729,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
712729
let values = op.val.immediates_or_place().left_or_else(|p| {
713730
bug!("Field {field_idx:?} is {p:?} making {layout:?}");
714731
});
715-
inputs.extend(values);
716732
let scalars = self.value_kind(op.layout).scalars().unwrap();
733+
debug_assert_eq!(values.len(), scalars.len());
734+
inputs.extend(values);
717735
input_scalars.extend(scalars);
718736
}
719737

compiler/rustc_const_eval/src/const_eval/machine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
722722
// (Do nothing on `None` provenance, that cannot store immutability anyway.)
723723
if let ty::Ref(_, ty, mutbl) = val.layout.ty.kind()
724724
&& *mutbl == Mutability::Not
725-
&& val.to_scalar_and_meta().0.to_pointer(ecx)?.provenance.is_some_and(|p| !p.immutable())
725+
&& val.to_scalar_and_meta()?.0.to_pointer(ecx)?.provenance.is_some_and(|p| !p.immutable())
726726
// That next check is expensive, that's why we have all the guards above.
727727
&& ty.is_freeze(*ecx.tcx, ecx.param_env)
728728
{

compiler/rustc_const_eval/src/interpret/cast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
206206
assert!(cast_to.ty.is_unsafe_ptr());
207207
// Handle casting any ptr to raw ptr (might be a fat ptr).
208208
if cast_to.size == src.layout.size {
209-
// Thin or fat pointer that just hast the ptr kind of target type changed.
209+
// Thin or fat pointer that just has the ptr kind of target type changed.
210210
return Ok(ImmTy::from_immediate(**src, cast_to));
211211
} else {
212212
// Casting the metadata away from a fat ptr.

compiler/rustc_const_eval/src/interpret/operand.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
4545
}
4646
}
4747

48-
impl<Prov: Provenance> Immediate<Prov> {
48+
impl<'tcx, Prov: Provenance> Immediate<Prov> {
4949
pub fn new_pointer_with_meta(
5050
ptr: Pointer<Option<Prov>>,
5151
meta: MemPlaceMeta<Prov>,
@@ -99,12 +99,12 @@ impl<Prov: Provenance> Immediate<Prov> {
9999
/// Returns the scalar from the first component and optionally the 2nd component as metadata.
100100
#[inline]
101101
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
102-
pub fn to_scalar_and_meta(self) -> (Scalar<Prov>, MemPlaceMeta<Prov>) {
103-
match self {
102+
pub fn to_scalar_and_meta(self) -> InterpResult<'tcx, (Scalar<Prov>, MemPlaceMeta<Prov>)> {
103+
Ok(match self {
104104
Immediate::ScalarPair(val1, val2) => (val1, MemPlaceMeta::Meta(val2)),
105105
Immediate::Scalar(val) => (val, MemPlaceMeta::None),
106-
Immediate::Uninit => bug!("Got uninit where a scalar or scalar pair was expected"),
107-
}
106+
Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)),
107+
})
108108
}
109109
}
110110

compiler/rustc_const_eval/src/interpret/operator.rs

+29-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use rustc_middle::{bug, span_bug};
77
use rustc_span::symbol::sym;
88
use rustc_target::abi::Abi;
99

10-
use super::{err_ub, throw_ub, throw_ub_custom, ImmTy, Immediate, InterpCx, Machine, PlaceTy};
10+
use super::{
11+
err_ub, throw_ub, throw_ub_custom, ImmTy, Immediate, InterpCx, Machine, MemPlaceMeta, PlaceTy,
12+
};
1113

1214
use crate::fluent_generated as fluent;
1315

@@ -479,11 +481,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
479481
use rustc_middle::mir::UnOp::*;
480482

481483
let layout = val.layout;
482-
let val = val.to_scalar();
483484
trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty);
484485

485486
match layout.ty.kind() {
486487
ty::Bool => {
488+
let val = val.to_scalar();
487489
let val = val.to_bool()?;
488490
let res = match un_op {
489491
Not => !val,
@@ -492,6 +494,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
492494
Ok((ImmTy::from_bool(res, *self.tcx), false))
493495
}
494496
ty::Float(fty) => {
497+
let val = val.to_scalar();
495498
// No NaN adjustment here, `-` is a bitwise operation!
496499
let res = match (un_op, fty) {
497500
(Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
@@ -500,8 +503,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
500503
};
501504
Ok((ImmTy::from_scalar(res, layout), false))
502505
}
503-
_ => {
504-
assert!(layout.ty.is_integral());
506+
_ if layout.ty.is_integral() => {
507+
let val = val.to_scalar();
505508
let val = val.to_bits(layout.size)?;
506509
let (res, overflow) = match un_op {
507510
Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate
@@ -516,9 +519,31 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
516519
let truncated = self.truncate(res, layout);
517520
(truncated, overflow || self.sign_extend(truncated, layout) != res)
518521
}
522+
_ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op),
519523
};
520524
Ok((ImmTy::from_uint(res, layout), overflow))
521525
}
526+
ty::RawPtr(..) => {
527+
assert_eq!(un_op, PtrMetadata);
528+
let (_, meta) = val.to_scalar_and_meta()?;
529+
Ok((
530+
match meta {
531+
MemPlaceMeta::Meta(scalar) => {
532+
let ty = un_op.ty(*self.tcx, val.layout.ty);
533+
let layout = self.layout_of(ty)?;
534+
ImmTy::from_scalar(scalar, layout)
535+
}
536+
MemPlaceMeta::None => {
537+
let unit_layout = self.layout_of(self.tcx.types.unit)?;
538+
ImmTy::uninit(unit_layout)
539+
}
540+
},
541+
false,
542+
))
543+
}
544+
_ => {
545+
bug!("Unexpected unary op argument {val:?}")
546+
}
522547
}
523548
}
524549

compiler/rustc_const_eval/src/interpret/place.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ where
419419
let pointee_type =
420420
val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type");
421421
let layout = self.layout_of(pointee_type)?;
422-
let (ptr, meta) = val.to_scalar_and_meta();
422+
let (ptr, meta) = val.to_scalar_and_meta()?;
423423

424424
// `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced;
425425
// we hence can't call `size_and_align_of` since that asserts more validity than we want.

compiler/rustc_const_eval/src/transform/validate.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
11051105
ty::Int(..) | ty::Uint(..) | ty::Bool
11061106
);
11071107
}
1108+
UnOp::PtrMetadata => {
1109+
check_kinds!(a, "Cannot PtrMetadata non-pointer type {:?}", ty::RawPtr(..));
1110+
}
11081111
}
11091112
}
11101113
Rvalue::ShallowInitBox(operand, _) => {

compiler/rustc_hir_analysis/src/check/intrinsic.rs

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

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

compiler/rustc_middle/src/mir/syntax.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,13 @@ pub enum UnOp {
14401440
Not,
14411441
/// The `-` operator for negation
14421442
Neg,
1443+
/// Get the metadata `M` from a `*const/mut impl Pointee<Metadata = M>`.
1444+
///
1445+
/// For example, this will give a `()` from `*const i32`, a `usize` from
1446+
/// `*mut [u8]`, or a pointer to a vtable from a `*const dyn Foo`.
1447+
///
1448+
/// Allowed only in [`MirPhase::Runtime`]; earlier it's an intrinsic.
1449+
PtrMetadata,
14431450
}
14441451

14451452
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]

compiler/rustc_middle/src/mir/tcx.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,10 @@ impl<'tcx> Rvalue<'tcx> {
185185
let ty = op.ty(tcx, lhs_ty, rhs_ty);
186186
Ty::new_tup(tcx, &[ty, tcx.types.bool])
187187
}
188-
Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx),
188+
Rvalue::UnaryOp(op, ref operand) => {
189+
let arg_ty = operand.ty(local_decls, tcx);
190+
op.ty(tcx, arg_ty)
191+
}
189192
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
190193
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
191194
tcx.types.usize
@@ -282,6 +285,27 @@ impl<'tcx> BinOp {
282285
}
283286
}
284287

288+
impl<'tcx> UnOp {
289+
pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
290+
match self {
291+
UnOp::Not | UnOp::Neg => arg_ty,
292+
UnOp::PtrMetadata => {
293+
let pointee_ty = arg_ty
294+
.builtin_deref(true)
295+
.unwrap_or_else(|| bug!("PtrMetadata of non-dereferenceable ty {arg_ty:?}"));
296+
if pointee_ty.is_trivially_sized(tcx) {
297+
tcx.types.unit
298+
} else {
299+
let Some(metadata_def_id) = tcx.lang_items().metadata_type() else {
300+
bug!("No metadata_type lang item while looking at {arg_ty:?}")
301+
};
302+
Ty::new_projection(tcx, metadata_def_id, [pointee_ty])
303+
}
304+
}
305+
}
306+
}
307+
}
308+
285309
impl BorrowKind {
286310
pub fn to_mutbl_lossy(self) -> hir::Mutability {
287311
match self {

compiler/rustc_middle/src/ty/print/pretty.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1579,8 +1579,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
15791579
let formatted_op = match op {
15801580
UnOp::Not => "!",
15811581
UnOp::Neg => "-",
1582+
UnOp::PtrMetadata => "PtrMetadata",
15821583
};
15831584
let parenthesized = match ct.kind() {
1585+
_ if op == UnOp::PtrMetadata => true,
15841586
ty::ConstKind::Expr(Expr::UnOp(c_op, ..)) => c_op != op,
15851587
ty::ConstKind::Expr(_) => true,
15861588
_ => false,

compiler/rustc_mir_transform/src/lower_intrinsics.rs

+17
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,23 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
316316

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

compiler/rustc_mir_transform/src/promote_consts.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ impl<'tcx> Validator<'_, 'tcx> {
464464
Rvalue::UnaryOp(op, operand) => {
465465
match op {
466466
// These operations can never fail.
467-
UnOp::Neg | UnOp::Not => {}
467+
UnOp::Neg | UnOp::Not | UnOp::PtrMetadata => {}
468468
}
469469

470470
self.validate_operand(operand)?;

0 commit comments

Comments
 (0)