From ede62b867c90eb8f00618eab95b0ecc35defd53b Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 21 Apr 2024 16:11:01 -0700 Subject: [PATCH 1/4] Add an intrinsic for `ptr::metadata` --- compiler/rustc_codegen_cranelift/src/base.rs | 20 ++++-- .../rustc_codegen_cranelift/src/constant.rs | 2 +- .../src/value_and_place.rs | 8 +++ compiler/rustc_codegen_ssa/src/mir/operand.rs | 5 ++ compiler/rustc_codegen_ssa/src/mir/place.rs | 5 ++ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 34 +++++++--- .../rustc_const_eval/src/interpret/cast.rs | 2 +- .../src/interpret/operator.rs | 28 +++++++-- .../rustc_hir_analysis/src/check/intrinsic.rs | 2 + compiler/rustc_middle/src/mir/syntax.rs | 7 +++ compiler/rustc_middle/src/mir/tcx.rs | 26 +++++++- compiler/rustc_middle/src/ty/print/pretty.rs | 2 + .../src/lower_intrinsics.rs | 17 +++++ .../rustc_mir_transform/src/promote_consts.rs | 2 +- compiler/rustc_mir_transform/src/validate.rs | 10 +++ .../rustc_smir/src/rustc_internal/internal.rs | 14 ++++- compiler/rustc_smir/src/rustc_smir/context.rs | 10 ++- .../rustc_smir/src/rustc_smir/convert/mir.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_ty_utils/src/consts.rs | 2 +- compiler/stable_mir/src/compiler_interface.rs | 5 +- compiler/stable_mir/src/mir/body.rs | 14 ++++- library/core/src/intrinsics.rs | 15 +++++ library/core/src/ptr/metadata.rs | 21 +++++-- library/core/tests/ptr.rs | 12 ++++ tests/codegen/intrinsics/ptr_metadata.rs | 36 +++++++++++ ..._metadata.LowerIntrinsics.panic-abort.diff | 63 +++++++++++++++++++ ...metadata.LowerIntrinsics.panic-unwind.diff | 63 +++++++++++++++++++ tests/mir-opt/lower_intrinsics.rs | 9 +++ ...e_add_fat.PreCodegen.after.panic-abort.mir | 10 +-- ..._add_fat.PreCodegen.after.panic-unwind.mir | 10 +-- 31 files changed, 413 insertions(+), 43 deletions(-) create mode 100644 tests/codegen/intrinsics/ptr_metadata.rs create mode 100644 tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-abort.diff create mode 100644 tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-unwind.diff diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 8d778f736d671..476a3912b968b 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -619,9 +619,10 @@ fn codegen_stmt<'tcx>( Rvalue::UnaryOp(un_op, ref operand) => { let operand = codegen_operand(fx, operand); let layout = operand.layout(); - let val = operand.load_scalar(fx); let res = match un_op { - UnOp::Not => match layout.ty.kind() { + UnOp::Not => { + let val = operand.load_scalar(fx); + match layout.ty.kind() { ty::Bool => { let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0); CValue::by_val(res, layout) @@ -630,11 +631,22 @@ fn codegen_stmt<'tcx>( CValue::by_val(fx.bcx.ins().bnot(val), layout) } _ => unreachable!("un op Not for {:?}", layout.ty), - }, - UnOp::Neg => match layout.ty.kind() { + } + } + UnOp::Neg => { + let val = operand.load_scalar(fx); + match layout.ty.kind() { ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout), ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout), _ => unreachable!("un op Neg for {:?}", layout.ty), + } + } + UnOp::PtrMetadata => match 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, res); diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 64e83e43d3272..ba98f2e772cbf 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -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 { diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 4146137c2263a..512a96450a4b6 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -95,6 +95,14 @@ impl<'tcx> CValue<'tcx> { CValue(CValueInner::ByValPair(value, extra), 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 } diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 5b2f9a3be2792..cc0e913965067 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -565,6 +565,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { for elem in place_ref.projection.iter() { match elem { mir::ProjectionElem::Field(ref f, _) => { + debug_assert!( + !o.layout.ty.is_any_ptr(), + "Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \ + but tried to access field {f:?} of pointer {o:?}", + ); o = o.extract_field(bx, f.index()); } mir::ProjectionElem::Index(_) diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 7c3569a9dd2e2..449fd9ae0db9c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -480,6 +480,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cg_base = match *elem { mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()), mir::ProjectionElem::Field(ref field, _) => { + debug_assert!( + !cg_base.layout.ty.is_any_ptr(), + "Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \ + but tried to access field {field:?} of pointer {cg_base:?}", + ); cg_base.project_field(bx, field.index()) } mir::ProjectionElem::OpaqueCast(ty) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 4dd80d34ea703..c23867be3a10d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -623,19 +623,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::UnaryOp(op, ref operand) => { let operand = self.codegen_operand(bx, operand); - let lloperand = operand.immediate(); let is_float = operand.layout.ty.is_floating_point(); - let llval = match op { - mir::UnOp::Not => bx.not(lloperand), + let (val, layout) = match op { + mir::UnOp::Not => { + let llval = bx.not(operand.immediate()); + (OperandValue::Immediate(llval), operand.layout) + } mir::UnOp::Neg => { - if is_float { - bx.fneg(lloperand) + let llval = if is_float { + bx.fneg(operand.immediate()) + } else { + bx.neg(operand.immediate()) + }; + (OperandValue::Immediate(llval), operand.layout) + } + mir::UnOp::PtrMetadata => { + debug_assert!(operand.layout.ty.is_unsafe_ptr()); + let (_, meta) = operand.val.pointer_parts(); + assert_eq!(operand.layout.fields.count() > 1, meta.is_some()); + if let Some(meta) = meta { + (OperandValue::Immediate(meta), operand.layout.field(self.cx, 1)) } else { - bx.neg(lloperand) + (OperandValue::ZeroSized, bx.cx().layout_of(bx.tcx().types.unit)) } } }; - OperandRef { val: OperandValue::Immediate(llval), layout: operand.layout } + debug_assert!( + val.is_expected_variant_for_type(self.cx, layout), + "Made wrong variant {val:?} for type {layout:?}", + ); + OperandRef { val, layout } } mir::Rvalue::Discriminant(ref place) => { @@ -718,8 +735,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let values = op.val.immediates_or_place().left_or_else(|p| { bug!("Field {field_idx:?} is {p:?} making {layout:?}"); }); - inputs.extend(values); let scalars = self.value_kind(op.layout).scalars().unwrap(); + debug_assert_eq!(values.len(), scalars.len()); + inputs.extend(values); input_scalars.extend(scalars); } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index ecfb25e32cda7..d599d4353754a 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -207,7 +207,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. diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 62641b868e623..4d91ca651616e 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -9,7 +9,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; use tracing::trace; -use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine}; +use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta}; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn three_way_compare(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> { @@ -415,11 +415,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { use rustc_middle::mir::UnOp::*; let layout = val.layout; - let val = val.to_scalar(); trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty); match layout.ty.kind() { ty::Bool => { + let val = val.to_scalar(); let val = val.to_bool()?; let res = match un_op { Not => !val, @@ -428,6 +428,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(ImmTy::from_bool(res, *self.tcx)) } ty::Float(fty) => { + let val = val.to_scalar(); // No NaN adjustment here, `-` is a bitwise operation! let res = match (un_op, fty) { (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?), @@ -436,8 +437,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; Ok(ImmTy::from_scalar(res, layout)) } - _ => { - assert!(layout.ty.is_integral()); + _ if layout.ty.is_integral() => { + let val = val.to_scalar(); let val = val.to_bits(layout.size)?; let res = match un_op { Not => self.truncate(!val, layout), // bitwise negation, then truncate @@ -450,9 +451,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Truncate to target type. self.truncate(res, layout) } + _ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op), }; Ok(ImmTy::from_uint(res, layout)) } + ty::RawPtr(..) => { + assert_eq!(un_op, PtrMetadata); + let (_, meta) = val.to_scalar_and_meta(); + Ok(match meta { + MemPlaceMeta::Meta(scalar) => { + let ty = un_op.ty(*self.tcx, val.layout.ty); + let layout = self.layout_of(ty)?; + ImmTy::from_scalar(scalar, layout) + } + MemPlaceMeta::None => { + let unit_layout = self.layout_of(self.tcx.types.unit)?; + ImmTy::uninit(unit_layout) + } + }) + } + _ => { + bug!("Unexpected unary op argument {val:?}") + } } } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 8506b8d75f957..f7989aeab41bb 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -130,6 +130,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 @@ -576,6 +577,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), diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2b28496faec73..47da924ba49af 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1434,6 +1434,13 @@ pub enum UnOp { Not, /// The `-` operator for negation Neg, + /// Get the metadata `M` from a `*const/mut impl Pointee`. + /// + /// For example, this will give a `()` from `*const i32`, a `usize` from + /// `*mut [u8]`, or a pointer to a vtable from a `*const dyn Foo`. + /// + /// Allowed only in [`MirPhase::Runtime`]; earlier it's an intrinsic. + PtrMetadata, } #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index a122cffdb876a..126387db1d9c5 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -180,7 +180,10 @@ impl<'tcx> Rvalue<'tcx> { let rhs_ty = rhs.ty(local_decls, tcx); op.ty(tcx, lhs_ty, rhs_ty) } - Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx), + Rvalue::UnaryOp(op, ref operand) => { + let arg_ty = operand.ty(local_decls, tcx); + op.ty(tcx, arg_ty) + } Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx), Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize @@ -282,6 +285,27 @@ impl<'tcx> BinOp { } } +impl<'tcx> UnOp { + pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> { + match self { + UnOp::Not | UnOp::Neg => arg_ty, + UnOp::PtrMetadata => { + let pointee_ty = arg_ty + .builtin_deref(true) + .unwrap_or_else(|| bug!("PtrMetadata of non-dereferenceable ty {arg_ty:?}")); + if pointee_ty.is_trivially_sized(tcx) { + tcx.types.unit + } else { + let Some(metadata_def_id) = tcx.lang_items().metadata_type() else { + bug!("No metadata_type lang item while looking at {arg_ty:?}") + }; + Ty::new_projection(tcx, metadata_def_id, [pointee_ty]) + } + } + } + } +} + impl BorrowKind { pub fn to_mutbl_lossy(self) -> hir::Mutability { match self { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 0bcd02aaa3acf..183ea76393c86 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1579,8 +1579,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let formatted_op = match op { UnOp::Not => "!", UnOp::Neg => "-", + UnOp::PtrMetadata => "PtrMetadata", }; let parenthesized = match ct.kind() { + _ if op == UnOp::PtrMetadata => true, ty::ConstKind::Expr(Expr::UnOp(c_op, ..)) => c_op != op, ty::ConstKind::Expr(_) => true, _ => false, diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 221301b2ceb04..3ffc447217d7d 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -316,6 +316,23 @@ 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(); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::UnaryOp(UnOp::PtrMetadata, ptr.node), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; + } _ => {} } } diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index e37f90ae7f400..7ec59cc983f53 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -464,7 +464,7 @@ impl<'tcx> Validator<'_, 'tcx> { Rvalue::UnaryOp(op, operand) => { match op { // These operations can never fail. - UnOp::Neg | UnOp::Not => {} + UnOp::Neg | UnOp::Not | UnOp::PtrMetadata => {} } self.validate_operand(operand)?; diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 66cc65de64709..f3807e731cf3b 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1089,6 +1089,16 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ty::Int(..) | ty::Uint(..) | ty::Bool ); } + UnOp::PtrMetadata => { + if !matches!(self.mir_phase, MirPhase::Runtime(_)) { + // It would probably be fine to support this in earlier phases, + // but at the time of writing it's only ever introduced from intrinsic lowering, + // so earlier things can just `bug!` on it. + self.fail(location, "PtrMetadata should be in runtime MIR only"); + } + + check_kinds!(a, "Cannot PtrMetadata non-pointer type {:?}", ty::RawPtr(..)); + } } } Rvalue::ShallowInitBox(operand, _) => { diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 6b73c1ebd1cac..6ec710f97d16a 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -10,7 +10,7 @@ use rustc_span::Symbol; use stable_mir::abi::Layout; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety}; +use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety, UnOp}; use stable_mir::ty::{ Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, @@ -582,6 +582,18 @@ impl RustcInternal for BinOp { } } +impl RustcInternal for UnOp { + type T<'tcx> = rustc_middle::mir::UnOp; + + fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + match self { + UnOp::Not => rustc_middle::mir::UnOp::Not, + UnOp::Neg => rustc_middle::mir::UnOp::Neg, + UnOp::PtrMetadata => rustc_middle::mir::UnOp::PtrMetadata, + } + } +} + impl RustcInternal for &T where T: RustcInternal, diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 7afe46f2cbe1c..fa7b2a30ba6cf 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -19,7 +19,7 @@ use stable_mir::abi::{FnAbi, Layout, LayoutShape}; use stable_mir::compiler_interface::Context; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{InstanceDef, StaticDef}; -use stable_mir::mir::{BinOp, Body, Place}; +use stable_mir::mir::{BinOp, Body, Place, UnOp}; use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -700,6 +700,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal); ty.stable(&mut *tables) } + + fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let arg_internal = arg.internal(&mut *tables, tcx); + let ty = un_op.internal(&mut *tables, tcx).ty(tcx, arg_internal); + ty.stable(&mut *tables) + } } pub struct TablesWrapper<'tcx>(pub RefCell>); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index d89caabab3e1a..a1a5c09ef0af4 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -526,6 +526,7 @@ impl<'tcx> Stable<'tcx> for mir::UnOp { match self { UnOp::Not => stable_mir::mir::UnOp::Not, UnOp::Neg => stable_mir::mir::UnOp::Neg, + UnOp::PtrMetadata => stable_mir::mir::UnOp::PtrMetadata, } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ace4dff46aa0a..b025fe5454c2f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1433,6 +1433,7 @@ symbols! { ptr_guaranteed_cmp, ptr_is_null, ptr_mask, + ptr_metadata, ptr_null, ptr_null_mut, ptr_offset_from, diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 67ffb8a58b5ad..6e51368f0315f 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -94,7 +94,7 @@ fn check_binop(op: mir::BinOp) -> bool { fn check_unop(op: mir::UnOp) -> bool { use mir::UnOp::*; match op { - Not | Neg => true, + Not | Neg | PtrMetadata => true, } } diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 4dd64228bbadb..858ce5301d84c 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -8,7 +8,7 @@ use std::cell::Cell; use crate::abi::{FnAbi, Layout, LayoutShape}; use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::mono::{Instance, InstanceDef, StaticDef}; -use crate::mir::{BinOp, Body, Place}; +use crate::mir::{BinOp, Body, Place, UnOp}; use crate::target::MachineInfo; use crate::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -226,6 +226,9 @@ pub trait Context { /// Get the resulting type of binary operation. fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty; + + /// Get the resulting type of unary operation. + fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty; } // A thread local variable that stores a pointer to the tables mapping between TyCtxt diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index a1432acf93cb9..4c779ae96a81b 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -346,6 +346,15 @@ impl BinOp { pub enum UnOp { Not, Neg, + PtrMetadata, +} + +impl UnOp { + /// Return the type of this operation for the given input Ty. + /// This function does not perform type checking, and it currently doesn't handle SIMD. + pub fn ty(&self, arg_ty: Ty) -> Ty { + with(|ctx| ctx.unop_ty(*self, arg_ty)) + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -580,7 +589,10 @@ impl Rvalue { let ty = op.ty(lhs_ty, rhs_ty); Ok(Ty::new_tuple(&[ty, Ty::bool_ty()])) } - Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, operand) => operand.ty(locals), + Rvalue::UnaryOp(op, operand) => { + let arg_ty = operand.ty(locals)?; + Ok(op.ty(arg_ty)) + } Rvalue::Discriminant(place) => { let place_ty = place.ty(locals)?; place_ty diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 5a2a4c5ae6ebe..89e0b67099519 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2821,6 +2821,21 @@ impl AggregateRawPtr<*mut T> for *mut P { type Metadata =

::Metadata; } +/// Lowers in MIR to `Rvalue::UnaryOp` with `UnOp::PtrMetadata`. +/// +/// 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 + ?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 . // (`transmute` also falls into this category, but it cannot be wrapped due to the diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index e501970b580de..84287ec3f5142 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -3,6 +3,8 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::intrinsics::aggregate_raw_ptr; +#[cfg(not(bootstrap))] +use crate::intrinsics::ptr_metadata; use crate::marker::Freeze; /// Provides the pointer metadata type of any pointed-to type. @@ -94,10 +96,17 @@ pub trait Thin = Pointee; #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn metadata(ptr: *const T) -> ::Metadata { - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents 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 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. @@ -132,6 +141,7 @@ pub const fn from_raw_parts_mut( } #[repr(C)] +#[cfg(bootstrap)] union PtrRepr { const_ptr: *const T, mut_ptr: *mut T, @@ -139,15 +149,18 @@ union PtrRepr { } #[repr(C)] +#[cfg(bootstrap)] struct PtrComponents { data_pointer: *const (), metadata: ::Metadata, } // Manual impl needed to avoid `T: Copy` bound. +#[cfg(bootstrap)] impl Copy for PtrComponents {} // Manual impl needed to avoid `T: Clone` bound. +#[cfg(bootstrap)] impl Clone for PtrComponents { fn clone(&self) -> Self { *self diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 7b55c2bf8a813..e8d05c2483de2 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1171,3 +1171,15 @@ 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() { + use std::fmt::Debug; + + const ARRAY_META: () = std::ptr::metadata::<[u16; 3]>(&[1, 2, 3]); + const SLICE_META: usize = std::ptr::metadata::<[u16]>(&[1, 2, 3]); + const DYN_META: DynMetadata = std::ptr::metadata::(&[0_u8; 42]); + assert_eq!(ARRAY_META, ()); + assert_eq!(SLICE_META, 3); + assert_eq!(DYN_META.size_of(), 42); +} diff --git a/tests/codegen/intrinsics/ptr_metadata.rs b/tests/codegen/intrinsics/ptr_metadata.rs new file mode 100644 index 0000000000000..f4bf5a1f5f173 --- /dev/null +++ b/tests/codegen/intrinsics/ptr_metadata.rs @@ -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) +} diff --git a/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-abort.diff new file mode 100644 index 0000000000000..d256058c05ee6 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-abort.diff @@ -0,0 +1,63 @@ +- // MIR for `get_metadata` before LowerIntrinsics ++ // MIR for `get_metadata` after LowerIntrinsics + + fn get_metadata(_1: *const i32, _2: *const [u8], _3: *const dyn Debug) -> () { + debug a => _1; + debug b => _2; + debug c => _3; + let mut _0: (); + let _4: (); + let mut _5: *const i32; + let mut _7: *const [u8]; + let mut _9: *const dyn std::fmt::Debug; + scope 1 { + debug _unit => _4; + let _6: usize; + scope 2 { + debug _usize => _6; + let _8: std::ptr::DynMetadata; + scope 3 { + debug _vtable => _8; + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + _5 = _1; +- _4 = ptr_metadata::(move _5) -> [return: bb1, unwind unreachable]; ++ _4 = PtrMetadata(move _5); ++ goto -> bb1; + } + + bb1: { + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + _7 = _2; +- _6 = ptr_metadata::<[u8], usize>(move _7) -> [return: bb2, unwind unreachable]; ++ _6 = PtrMetadata(move _7); ++ goto -> bb2; + } + + bb2: { + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _9 = _3; +- _8 = ptr_metadata::>(move _9) -> [return: bb3, unwind unreachable]; ++ _8 = PtrMetadata(move _9); ++ goto -> bb3; + } + + bb3: { + StorageDead(_9); + _0 = const (); + StorageDead(_8); + StorageDead(_6); + StorageDead(_4); + return; + } + } + diff --git a/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-unwind.diff new file mode 100644 index 0000000000000..d256058c05ee6 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.get_metadata.LowerIntrinsics.panic-unwind.diff @@ -0,0 +1,63 @@ +- // MIR for `get_metadata` before LowerIntrinsics ++ // MIR for `get_metadata` after LowerIntrinsics + + fn get_metadata(_1: *const i32, _2: *const [u8], _3: *const dyn Debug) -> () { + debug a => _1; + debug b => _2; + debug c => _3; + let mut _0: (); + let _4: (); + let mut _5: *const i32; + let mut _7: *const [u8]; + let mut _9: *const dyn std::fmt::Debug; + scope 1 { + debug _unit => _4; + let _6: usize; + scope 2 { + debug _usize => _6; + let _8: std::ptr::DynMetadata; + scope 3 { + debug _vtable => _8; + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + _5 = _1; +- _4 = ptr_metadata::(move _5) -> [return: bb1, unwind unreachable]; ++ _4 = PtrMetadata(move _5); ++ goto -> bb1; + } + + bb1: { + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + _7 = _2; +- _6 = ptr_metadata::<[u8], usize>(move _7) -> [return: bb2, unwind unreachable]; ++ _6 = PtrMetadata(move _7); ++ goto -> bb2; + } + + bb2: { + StorageDead(_7); + StorageLive(_8); + StorageLive(_9); + _9 = _3; +- _8 = ptr_metadata::>(move _9) -> [return: bb3, unwind unreachable]; ++ _8 = PtrMetadata(move _9); ++ goto -> bb3; + } + + bb3: { + StorageDead(_9); + _0 = const (); + StorageDead(_8); + StorageDead(_6); + StorageDead(_4); + return; + } + } + diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 180bfd0a92462..2569f4f4de5df 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -258,3 +258,12 @@ pub fn make_pointers(a: *const u8, b: *mut (), n: usize) { let _slice_const: *const [u16] = aggregate_raw_ptr(a, n); let _slice_mut: *mut [u64] = aggregate_raw_ptr(b, n); } + +// EMIT_MIR lower_intrinsics.get_metadata.LowerIntrinsics.diff +pub fn get_metadata(a: *const i32, b: *const [u8], c: *const dyn std::fmt::Debug) { + use std::intrinsics::ptr_metadata; + + let _unit = ptr_metadata(a); + let _usize = ptr_metadata(b); + let _vtable = ptr_metadata(c); +} diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir index db0c84bd560fd..ea4ed2713056f 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir @@ -13,9 +13,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { let mut _5: *const (); - let mut _7: usize; + let mut _6: usize; scope 5 (inlined std::ptr::metadata::<[u32]>) { - let mut _6: std::ptr::metadata::PtrRepr<[u32]>; } scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { } @@ -30,13 +29,10 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { StorageDead(_3); StorageLive(_5); _5 = _4 as *const () (PtrToPtr); - StorageLive(_7); StorageLive(_6); - _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; - _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); + _6 = PtrMetadata(_1); + _0 = *const [u32] from (_5, _6); StorageDead(_6); - _0 = *const [u32] from (_5, _7); - StorageDead(_7); StorageDead(_5); StorageDead(_4); return; diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir index db0c84bd560fd..ea4ed2713056f 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir @@ -13,9 +13,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { let mut _5: *const (); - let mut _7: usize; + let mut _6: usize; scope 5 (inlined std::ptr::metadata::<[u32]>) { - let mut _6: std::ptr::metadata::PtrRepr<[u32]>; } scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { } @@ -30,13 +29,10 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { StorageDead(_3); StorageLive(_5); _5 = _4 as *const () (PtrToPtr); - StorageLive(_7); StorageLive(_6); - _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; - _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); + _6 = PtrMetadata(_1); + _0 = *const [u32] from (_5, _6); StorageDead(_6); - _0 = *const [u32] from (_5, _7); - StorageDead(_7); StorageDead(_5); StorageDead(_4); return; From ce8f37be22ac26249c917ac6cdcffa0102c11c94 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 25 May 2024 02:11:45 -0700 Subject: [PATCH 2/4] Add custom mir support for `PtrMetadata` --- .../src/build/custom/parse/instruction.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mir.rs | 4 ++++ .../building/custom/operators.g.runtime.after.mir | 13 +++++++++++++ tests/mir-opt/building/custom/operators.rs | 10 ++++++++++ 5 files changed, 29 insertions(+) create mode 100644 tests/mir-opt/building/custom/operators.g.runtime.after.mir diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs index 6f8cfc3af4473..de748b9c85d17 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs @@ -212,6 +212,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset)))) }, @call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)), + @call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)), @call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)), ExprKind::Borrow { borrow_kind, arg } => Ok( Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b025fe5454c2f..90da220b3f549 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1179,6 +1179,7 @@ symbols! { mir_make_place, mir_move, mir_offset, + mir_ptr_metadata, mir_retag, mir_return, mir_return_to, diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 02665b2676cc1..fa5bb28adff94 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -360,6 +360,10 @@ define!("mir_assume", fn Assume(operand: bool)); define!("mir_deinit", fn Deinit(place: T)); define!("mir_checked", fn Checked(binop: T) -> (T, bool)); define!("mir_len", fn Len(place: T) -> usize); +define!( + "mir_ptr_metadata", + fn PtrMetadata(place: *const P) ->

::Metadata +); define!("mir_copy_for_deref", fn CopyForDeref(place: T) -> T); define!("mir_retag", fn Retag(place: T)); define!("mir_move", fn Move(place: T) -> T); diff --git a/tests/mir-opt/building/custom/operators.g.runtime.after.mir b/tests/mir-opt/building/custom/operators.g.runtime.after.mir new file mode 100644 index 0000000000000..a0ad7d0f93f4e --- /dev/null +++ b/tests/mir-opt/building/custom/operators.g.runtime.after.mir @@ -0,0 +1,13 @@ +// MIR for `g` after runtime + +fn g(_1: *const i32, _2: *const [i32]) -> () { + let mut _0: (); + let mut _3: (); + let mut _4: usize; + + bb0: { + _3 = PtrMetadata(_1); + _4 = PtrMetadata(_2); + return; + } +} diff --git a/tests/mir-opt/building/custom/operators.rs b/tests/mir-opt/building/custom/operators.rs index eb97bcc73b7e1..ff0e8dcbb4156 100644 --- a/tests/mir-opt/building/custom/operators.rs +++ b/tests/mir-opt/building/custom/operators.rs @@ -30,3 +30,13 @@ pub fn f(a: i32, b: bool) -> i32 { Return() }) } + +// EMIT_MIR operators.g.runtime.after.mir +#[custom_mir(dialect = "runtime")] +pub fn g(p: *const i32, q: *const [i32]) { + mir!({ + let a = PtrMetadata(p); + let b = PtrMetadata(q); + Return() + }) +} From 0f9e4d6561b42fb4c31f3b961906aac8b899b269 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 26 May 2024 14:20:32 -0700 Subject: [PATCH 3/4] Add Miri tests for `PtrMetadata` UB --- .../ptr_metadata_uninit_slice_data.rs | 22 ++++++++++++ .../ptr_metadata_uninit_slice_data.stderr | 20 +++++++++++ .../ptr_metadata_uninit_slice_len.rs | 22 ++++++++++++ .../ptr_metadata_uninit_slice_len.stderr | 35 +++++++++++++++++++ .../intrinsics/ptr_metadata_uninit_thin.rs | 23 ++++++++++++ .../ptr_metadata_uninit_thin.stderr | 20 +++++++++++ 6 files changed, 142 insertions(+) create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs create mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs new file mode 100644 index 0000000000000..ff23f1e729e87 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs @@ -0,0 +1,22 @@ +//@compile-flags: -Zmiri-disable-validation +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +// This disables validation and uses custom MIR hit exactly the UB in the intrinsic, +// rather than getting UB from the typed load or parameter passing. + +#[custom_mir(dialect = "runtime")] +pub unsafe fn deref_meta(p: *const *const [i32]) -> usize { + mir!({ + RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data + Return() + }) +} + +fn main() { + let mut p = std::mem::MaybeUninit::<*const [i32]>::uninit(); + unsafe { + (*p.as_mut_ptr().cast::<[usize; 2]>())[1] = 4; + let _meta = deref_meta(p.as_ptr().cast()); + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr new file mode 100644 index 0000000000000..61e1541d1ee4a --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory + --> $DIR/ptr_metadata_uninit_slice_data.rs:LL:CC + | +LL | RET = PtrMetadata(*p); + | ^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `deref_meta` at $DIR/ptr_metadata_uninit_slice_data.rs:LL:CC +note: inside `main` + --> $DIR/ptr_metadata_uninit_slice_data.rs:LL:CC + | +LL | let _meta = deref_meta(p.as_ptr().cast()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs new file mode 100644 index 0000000000000..65f74c0acdd6d --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs @@ -0,0 +1,22 @@ +//@compile-flags: -Zmiri-disable-validation +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +// This disables validation and uses custom MIR hit exactly the UB in the intrinsic, +// rather than getting UB from the typed load or parameter passing. + +#[custom_mir(dialect = "runtime")] +pub unsafe fn deref_meta(p: *const *const [i32]) -> usize { + mir!({ + RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data + Return() + }) +} + +fn main() { + let mut p = std::mem::MaybeUninit::<*const [i32]>::uninit(); + unsafe { + (*p.as_mut_ptr().cast::<[*const i32; 2]>())[0] = 4 as *const i32; + let _meta = deref_meta(p.as_ptr().cast()); + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr new file mode 100644 index 0000000000000..de559263a326d --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr @@ -0,0 +1,35 @@ +warning: integer-to-pointer cast + --> $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC + | +LL | (*p.as_mut_ptr().cast::<[*const i32; 2]>())[0] = 4 as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast + | + = help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, + = help: which means that Miri might miss pointer bugs in this program. + = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation. + = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. + = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics. + = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. + = note: BACKTRACE: + = note: inside `main` at $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC + +error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory + --> $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC + | +LL | RET = PtrMetadata(*p); + | ^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `deref_meta` at $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC +note: inside `main` + --> $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC + | +LL | let _meta = deref_meta(p.as_ptr().cast()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs new file mode 100644 index 0000000000000..ad2e9fc800eb9 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs @@ -0,0 +1,23 @@ +//@compile-flags: -Zmiri-disable-validation +#![feature(core_intrinsics, custom_mir)] +use std::intrinsics::mir::*; + +// This disables validation and uses custom MIR hit exactly the UB in the intrinsic, +// rather than getting UB from the typed load or parameter passing. + +#[custom_mir(dialect = "runtime")] +pub unsafe fn deref_meta(p: *const *const i32) -> () { + mir!({ + RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data + Return() + }) +} + +fn main() { + // Even though the meta is the trivially-valid `()`, this is still UB + + let p = std::mem::MaybeUninit::<*const i32>::uninit(); + unsafe { + let _meta = deref_meta(p.as_ptr()); + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr new file mode 100644 index 0000000000000..3ab2643afa728 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory + --> $DIR/ptr_metadata_uninit_thin.rs:LL:CC + | +LL | RET = PtrMetadata(*p); + | ^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `deref_meta` at $DIR/ptr_metadata_uninit_thin.rs:LL:CC +note: inside `main` + --> $DIR/ptr_metadata_uninit_thin.rs:LL:CC + | +LL | let _meta = deref_meta(p.as_ptr()); + | ^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 713ddc20f4f6e908ab190e5eec59de251e62f0fd Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 27 May 2024 14:45:45 -0700 Subject: [PATCH 4/4] Add Miri smoke pass test for ptr_metadata intrinsic --- src/tools/miri/tests/pass/intrinsics/intrinsics.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/tests/pass/intrinsics/intrinsics.rs b/src/tools/miri/tests/pass/intrinsics/intrinsics.rs index 0dda5aadce20a..89289a25d50a3 100644 --- a/src/tools/miri/tests/pass/intrinsics/intrinsics.rs +++ b/src/tools/miri/tests/pass/intrinsics/intrinsics.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-permissive-provenance -#![feature(core_intrinsics, layout_for_ptr)] +#![feature(core_intrinsics, layout_for_ptr, ptr_metadata)] //! Tests for various intrinsics that do not fit anywhere else. use std::intrinsics; @@ -57,4 +57,10 @@ fn main() { // Make sure that even if the discriminant is stored together with data, the intrinsic returns // only the discriminant, nothing about the data. assert_eq!(discriminant(&Some(false)), discriminant(&Some(true))); + + let () = intrinsics::ptr_metadata(&[1, 2, 3]); + let len = intrinsics::ptr_metadata(&[1, 2, 3][..]); + assert_eq!(len, 3); + let dyn_meta = intrinsics::ptr_metadata(&[1, 2, 3] as &dyn std::fmt::Debug); + assert_eq!(dyn_meta.size_of(), 12); }