mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Add an intrinsic for ptr::metadata
This commit is contained in:
parent
7717a306b2
commit
459ce3f6bb
@ -619,22 +619,34 @@ 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() {
|
||||
ty::Bool => {
|
||||
let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0);
|
||||
CValue::by_val(res, layout)
|
||||
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)
|
||||
}
|
||||
ty::Uint(_) | ty::Int(_) => {
|
||||
CValue::by_val(fx.bcx.ins().bnot(val), layout)
|
||||
}
|
||||
_ => unreachable!("un op Not for {:?}", layout.ty),
|
||||
}
|
||||
ty::Uint(_) | ty::Int(_) => {
|
||||
CValue::by_val(fx.bcx.ins().bnot(val), layout)
|
||||
}
|
||||
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),
|
||||
}
|
||||
_ => unreachable!("un op Not for {:?}", layout.ty),
|
||||
},
|
||||
UnOp::Neg => 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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(_)
|
||||
|
@ -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) => {
|
||||
|
@ -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(lloperand)
|
||||
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 {
|
||||
(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);
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'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.
|
||||
|
@ -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<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> {
|
||||
@ -415,11 +415,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'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<'tcx, M: Machine<'tcx>> InterpCx<'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<'tcx, M: Machine<'tcx>> InterpCx<'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<'tcx, M: Machine<'tcx>> InterpCx<'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:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
||||
|
@ -1434,6 +1434,13 @@ pub enum UnOp {
|
||||
Not,
|
||||
/// The `-` operator for negation
|
||||
Neg,
|
||||
/// Get the metadata `M` from a `*const/mut impl Pointee<Metadata = M>`.
|
||||
///
|
||||
/// 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)]
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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 };
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
@ -1109,6 +1109,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, _) => {
|
||||
|
@ -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<T> RustcInternal for &T
|
||||
where
|
||||
T: RustcInternal,
|
||||
|
@ -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<Tables<'tcx>>);
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1433,6 +1433,7 @@ symbols! {
|
||||
ptr_guaranteed_cmp,
|
||||
ptr_is_null,
|
||||
ptr_mask,
|
||||
ptr_metadata,
|
||||
ptr_null,
|
||||
ptr_null_mut,
|
||||
ptr_offset_from,
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -2821,6 +2821,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::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<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
|
||||
|
@ -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<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.
|
||||
@ -132,6 +141,7 @@ 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,
|
||||
@ -139,15 +149,18 @@ union PtrRepr<T: ?Sized> {
|
||||
}
|
||||
|
||||
#[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
|
||||
|
@ -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<dyn Debug> = std::ptr::metadata::<dyn Debug>(&[0_u8; 42]);
|
||||
assert_eq!(ARRAY_META, ());
|
||||
assert_eq!(SLICE_META, 3);
|
||||
assert_eq!(DYN_META.size_of(), 42);
|
||||
}
|
||||
|
36
tests/codegen/intrinsics/ptr_metadata.rs
Normal file
36
tests/codegen/intrinsics/ptr_metadata.rs
Normal file
@ -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)
|
||||
}
|
@ -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<dyn std::fmt::Debug>;
|
||||
scope 3 {
|
||||
debug _vtable => _8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_4);
|
||||
StorageLive(_5);
|
||||
_5 = _1;
|
||||
- _4 = ptr_metadata::<i32, ()>(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::<dyn Debug, DynMetadata<dyn Debug>>(move _9) -> [return: bb3, unwind unreachable];
|
||||
+ _8 = PtrMetadata(move _9);
|
||||
+ goto -> bb3;
|
||||
}
|
||||
|
||||
bb3: {
|
||||
StorageDead(_9);
|
||||
_0 = const ();
|
||||
StorageDead(_8);
|
||||
StorageDead(_6);
|
||||
StorageDead(_4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -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<dyn std::fmt::Debug>;
|
||||
scope 3 {
|
||||
debug _vtable => _8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_4);
|
||||
StorageLive(_5);
|
||||
_5 = _1;
|
||||
- _4 = ptr_metadata::<i32, ()>(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::<dyn Debug, DynMetadata<dyn Debug>>(move _9) -> [return: bb3, unwind unreachable];
|
||||
+ _8 = PtrMetadata(move _9);
|
||||
+ goto -> bb3;
|
||||
}
|
||||
|
||||
bb3: {
|
||||
StorageDead(_9);
|
||||
_0 = const ();
|
||||
StorageDead(_8);
|
||||
StorageDead(_6);
|
||||
StorageDead(_4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -13,9 +13,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
|
||||
}
|
||||
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::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;
|
||||
|
@ -13,9 +13,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
|
||||
}
|
||||
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::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;
|
||||
|
Loading…
Reference in New Issue
Block a user