1
Fork 0

ScalarInt: size mismatches are a bug, do not delay the panic

This commit is contained in:
Ralf Jung 2024-06-08 16:13:45 +02:00
parent 13423befc4
commit 3c57ea0df7
45 changed files with 247 additions and 313 deletions

View file

@ -84,11 +84,11 @@ impl<'tcx> ConstValue<'tcx> {
}
pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
self.try_to_scalar()?.try_to_int().ok()
self.try_to_scalar()?.try_to_scalar_int().ok()
}
pub fn try_to_bits(&self, size: Size) -> Option<u128> {
self.try_to_scalar_int()?.try_to_bits(size).ok()
Some(self.try_to_scalar_int()?.to_bits(size))
}
pub fn try_to_bool(&self) -> Option<bool> {
@ -96,7 +96,7 @@ impl<'tcx> ConstValue<'tcx> {
}
pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.try_to_scalar_int()?.try_to_target_usize(tcx).ok()
Some(self.try_to_scalar_int()?.to_target_usize(tcx))
}
pub fn try_to_bits_for_ty(
@ -300,7 +300,7 @@ impl<'tcx> Const<'tcx> {
#[inline]
pub fn try_to_bits(self, size: Size) -> Option<u128> {
self.try_to_scalar_int()?.try_to_bits(size).ok()
Some(self.try_to_scalar_int()?.to_bits(size))
}
#[inline]
@ -367,7 +367,7 @@ impl<'tcx> Const<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<ScalarInt> {
self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
self.try_eval_scalar(tcx, param_env)?.try_to_scalar_int().ok()
}
#[inline]
@ -375,7 +375,7 @@ impl<'tcx> Const<'tcx> {
let int = self.try_eval_scalar_int(tcx, param_env)?;
let size =
tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size;
int.try_to_bits(size).ok()
Some(int.to_bits(size))
}
/// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
@ -391,7 +391,7 @@ impl<'tcx> Const<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<u64> {
self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok()
Some(self.try_eval_scalar_int(tcx, param_env)?.to_target_usize(tcx))
}
#[inline]

View file

@ -122,16 +122,12 @@ impl<Prov> Scalar<Prov> {
Scalar::Int(c.into())
}
#[inline]
pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
ScalarInt::try_from_uint(i, size).map(Scalar::Int)
}
#[inline]
pub fn from_uint(i: impl Into<u128>, size: Size) -> Self {
let i = i.into();
Self::try_from_uint(i, size)
ScalarInt::try_from_uint(i, size)
.unwrap_or_else(|| bug!("Unsigned value {:#x} does not fit in {} bits", i, size.bits()))
.into()
}
#[inline]
@ -164,16 +160,12 @@ impl<Prov> Scalar<Prov> {
Self::from_uint(i, cx.data_layout().pointer_size)
}
#[inline]
pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
ScalarInt::try_from_int(i, size).map(Scalar::Int)
}
#[inline]
pub fn from_int(i: impl Into<i128>, size: Size) -> Self {
let i = i.into();
Self::try_from_int(i, size)
ScalarInt::try_from_int(i, size)
.unwrap_or_else(|| bug!("Signed value {:#x} does not fit in {} bits", i, size.bits()))
.into()
}
#[inline]
@ -227,7 +219,7 @@ impl<Prov> Scalar<Prov> {
}
/// This is almost certainly not the method you want! You should dispatch on the type
/// and use `to_{u8,u16,...}`/`scalar_to_ptr` to perform ptr-to-int / int-to-ptr casts as needed.
/// and use `to_{u8,u16,...}`/`to_pointer` to perform ptr-to-int / int-to-ptr casts as needed.
///
/// This method only exists for the benefit of low-level operations that truly need to treat the
/// scalar in whatever form it is.
@ -289,7 +281,7 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
/// The error type is `AllocId`, not `CtfeProvenance`, since `AllocId` is the "minimal"
/// component all provenance types must have.
#[inline]
pub fn try_to_int(self) -> Result<ScalarInt, Scalar<AllocId>> {
pub fn try_to_scalar_int(self) -> Result<ScalarInt, Scalar<AllocId>> {
match self {
Scalar::Int(int) => Ok(int),
Scalar::Ptr(ptr, sz) => {
@ -307,13 +299,13 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
#[inline(always)]
pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> {
self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into())
self.try_to_scalar_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into())
}
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_int(self) -> ScalarInt {
self.try_to_int().unwrap()
pub fn assert_scalar_int(self) -> ScalarInt {
self.try_to_scalar_int().expect("got a pointer where a ScalarInt was expected")
}
/// This throws UB (instead of ICEing) on a size mismatch since size mismatches can arise in
@ -330,13 +322,6 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
})
}
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_bits(self, target_size: Size) -> u128 {
self.to_bits(target_size)
.unwrap_or_else(|_| panic!("assertion failed: {self:?} fits {target_size:?}"))
}
pub fn to_bool(self) -> InterpResult<'tcx, bool> {
let val = self.to_u8()?;
match val {

View file

@ -1033,8 +1033,8 @@ impl<'tcx> PatRangeBoundary<'tcx> {
if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) {
let sz = ty.primitive_size(tcx);
let cmp = match ty.kind() {
ty::Uint(_) | ty::Char => a.assert_uint(sz).cmp(&b.assert_uint(sz)),
ty::Int(_) => a.assert_int(sz).cmp(&b.assert_int(sz)),
ty::Uint(_) | ty::Char => a.to_uint(sz).cmp(&b.to_uint(sz)),
ty::Int(_) => a.to_int(sz).cmp(&b.to_int(sz)),
_ => unreachable!(),
};
return Some(cmp);

View file

@ -376,7 +376,7 @@ impl<'tcx> Const<'tcx> {
param_env: ParamEnv<'tcx>,
) -> Option<(Ty<'tcx>, ScalarInt)> {
let (ty, scalar) = self.try_eval_scalar(tcx, param_env)?;
let val = scalar.try_to_int().ok()?;
let val = scalar.try_to_scalar_int().ok()?;
Some((ty, val))
}
@ -388,7 +388,7 @@ impl<'tcx> Const<'tcx> {
let (ty, scalar) = self.try_eval_scalar_int(tcx, param_env)?;
let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
// if `ty` does not depend on generic parameters, use an empty param_env
scalar.try_to_bits(size).ok()
Some(scalar.to_bits(size))
}
#[inline]
@ -405,7 +405,7 @@ impl<'tcx> Const<'tcx> {
param_env: ParamEnv<'tcx>,
) -> Option<u64> {
let (_, scalar) = self.try_eval_scalar_int(tcx, param_env)?;
scalar.try_to_target_usize(tcx).ok()
Some(scalar.to_target_usize(tcx))
}
#[inline]

View file

@ -246,6 +246,10 @@ impl ScalarInt {
Self::try_from_uint(i, tcx.data_layout.pointer_size)
}
/// Try to convert this ScalarInt to the raw underlying bits.
/// Fails if the size is wrong. Generally a wrong size should lead to a panic,
/// but Miri sometimes wants to be resilient to size mismatches,
/// so the interpreter will generally use this `try` method.
#[inline]
pub fn try_to_bits(self, target_size: Size) -> Result<u128, Size> {
assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
@ -258,165 +262,149 @@ impl ScalarInt {
}
#[inline]
pub fn assert_bits(self, target_size: Size) -> u128 {
pub fn to_bits(self, target_size: Size) -> u128 {
self.try_to_bits(target_size).unwrap_or_else(|size| {
bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
})
}
/// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
/// Fails if the size of the `ScalarInt` is not equal to `size` and returns the
/// `ScalarInt`s size in that case.
/// Extracts the bits from the scalar without checking the size.
#[inline]
pub fn try_to_uint(self, size: Size) -> Result<u128, Size> {
self.try_to_bits(size)
pub fn to_bits_unchecked(self) -> u128 {
self.check_data();
self.data
}
/// Converts the `ScalarInt` to an unsigned integer of the given size.
/// Panics if the size of the `ScalarInt` is not equal to `size`.
#[inline]
pub fn to_uint(self, size: Size) -> u128 {
self.to_bits(size)
}
/// Converts the `ScalarInt` to `u8`.
/// Panics if the `size` of the `ScalarInt`in not equal to 1 byte.
#[inline]
pub fn to_u8(self) -> u8 {
self.to_uint(Size::from_bits(8)).try_into().unwrap()
}
/// Converts the `ScalarInt` to `u16`.
/// Panics if the size of the `ScalarInt` in not equal to 2 bytes.
#[inline]
pub fn to_u16(self) -> u16 {
self.to_uint(Size::from_bits(16)).try_into().unwrap()
}
/// Converts the `ScalarInt` to `u32`.
/// Panics if the `size` of the `ScalarInt` in not equal to 4 bytes.
#[inline]
pub fn to_u32(self) -> u32 {
self.to_uint(Size::from_bits(32)).try_into().unwrap()
}
/// Converts the `ScalarInt` to `u64`.
/// Panics if the `size` of the `ScalarInt` in not equal to 8 bytes.
#[inline]
pub fn to_u64(self) -> u64 {
self.to_uint(Size::from_bits(64)).try_into().unwrap()
}
/// Converts the `ScalarInt` to `u128`.
/// Panics if the `size` of the `ScalarInt` in not equal to 16 bytes.
#[inline]
pub fn to_u128(self) -> u128 {
self.to_uint(Size::from_bits(128))
}
#[inline]
pub fn assert_uint(self, size: Size) -> u128 {
self.assert_bits(size)
pub fn to_target_usize(&self, tcx: TyCtxt<'_>) -> u64 {
self.to_uint(tcx.data_layout.pointer_size).try_into().unwrap()
}
// Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
// in not equal to 1 byte and returns the `size` value of the `ScalarInt` in
// that case.
/// Converts the `ScalarInt` to `bool`.
/// Panics if the `size` of the `ScalarInt` is not equal to 1 byte.
/// Errors if it is not a valid `bool`.
#[inline]
pub fn try_to_u8(self) -> Result<u8, Size> {
self.try_to_uint(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
}
/// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
/// in not equal to 2 bytes and returns the `size` value of the `ScalarInt` in
/// that case.
#[inline]
pub fn try_to_u16(self) -> Result<u16, Size> {
self.try_to_uint(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
}
/// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
/// in not equal to 4 bytes and returns the `size` value of the `ScalarInt` in
/// that case.
#[inline]
pub fn try_to_u32(self) -> Result<u32, Size> {
self.try_to_uint(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
}
/// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
/// in not equal to 8 bytes and returns the `size` value of the `ScalarInt` in
/// that case.
#[inline]
pub fn try_to_u64(self) -> Result<u64, Size> {
self.try_to_uint(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
}
/// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
/// in not equal to 16 bytes and returns the `size` value of the `ScalarInt` in
/// that case.
#[inline]
pub fn try_to_u128(self) -> Result<u128, Size> {
self.try_to_uint(Size::from_bits(128))
}
#[inline]
pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Result<u64, Size> {
self.try_to_uint(tcx.data_layout.pointer_size).map(|v| u64::try_from(v).unwrap())
}
// Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
// in not equal to 1 byte or if the value is not 0 or 1 and returns the `size`
// value of the `ScalarInt` in that case.
#[inline]
pub fn try_to_bool(self) -> Result<bool, Size> {
match self.try_to_u8()? {
pub fn try_to_bool(self) -> Result<bool, ()> {
match self.to_u8() {
0 => Ok(false),
1 => Ok(true),
_ => Err(self.size()),
_ => Err(()),
}
}
/// Tries to convert the `ScalarInt` to a signed integer of the given size.
/// Fails if the size of the `ScalarInt` is not equal to `size` and returns the
/// `ScalarInt`s size in that case.
/// Converts the `ScalarInt` to a signed integer of the given size.
/// Panics if the size of the `ScalarInt` is not equal to `size`.
#[inline]
pub fn try_to_int(self, size: Size) -> Result<i128, Size> {
let b = self.try_to_bits(size)?;
Ok(size.sign_extend(b) as i128)
}
#[inline]
pub fn assert_int(self, size: Size) -> i128 {
let b = self.assert_bits(size);
pub fn to_int(self, size: Size) -> i128 {
let b = self.to_bits(size);
size.sign_extend(b) as i128
}
/// Tries to convert the `ScalarInt` to i8.
/// Fails if the size of the `ScalarInt` is not equal to 1 byte
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i8(self) -> Result<i8, Size> {
self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap())
/// Converts the `ScalarInt` to i8.
/// Panics if the size of the `ScalarInt` is not equal to 1 byte.
pub fn to_i8(self) -> i8 {
self.to_int(Size::from_bits(8)).try_into().unwrap()
}
/// Tries to convert the `ScalarInt` to i16.
/// Fails if the size of the `ScalarInt` is not equal to 2 bytes
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i16(self) -> Result<i16, Size> {
self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap())
/// Converts the `ScalarInt` to i16.
/// Panics if the size of the `ScalarInt` is not equal to 2 bytes.
pub fn to_i16(self) -> i16 {
self.to_int(Size::from_bits(16)).try_into().unwrap()
}
/// Tries to convert the `ScalarInt` to i32.
/// Fails if the size of the `ScalarInt` is not equal to 4 bytes
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i32(self) -> Result<i32, Size> {
self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap())
/// Converts the `ScalarInt` to i32.
/// Panics if the size of the `ScalarInt` is not equal to 4 bytes.
pub fn to_i32(self) -> i32 {
self.to_int(Size::from_bits(32)).try_into().unwrap()
}
/// Tries to convert the `ScalarInt` to i64.
/// Fails if the size of the `ScalarInt` is not equal to 8 bytes
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i64(self) -> Result<i64, Size> {
self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap())
/// Converts the `ScalarInt` to i64.
/// Panics if the size of the `ScalarInt` is not equal to 8 bytes.
pub fn to_i64(self) -> i64 {
self.to_int(Size::from_bits(64)).try_into().unwrap()
}
/// Tries to convert the `ScalarInt` to i128.
/// Fails if the size of the `ScalarInt` is not equal to 16 bytes
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i128(self) -> Result<i128, Size> {
self.try_to_int(Size::from_bits(128))
/// Converts the `ScalarInt` to i128.
/// Panics if the size of the `ScalarInt` is not equal to 16 bytes.
pub fn to_i128(self) -> i128 {
self.to_int(Size::from_bits(128))
}
#[inline]
pub fn try_to_target_isize(&self, tcx: TyCtxt<'_>) -> Result<i64, Size> {
self.try_to_int(tcx.data_layout.pointer_size).map(|v| i64::try_from(v).unwrap())
pub fn to_target_isize(&self, tcx: TyCtxt<'_>) -> i64 {
self.to_int(tcx.data_layout.pointer_size).try_into().unwrap()
}
#[inline]
pub fn try_to_float<F: Float>(self) -> Result<F, Size> {
pub fn to_float<F: Float>(self) -> F {
// Going through `to_uint` to check size and truncation.
Ok(F::from_bits(self.try_to_bits(Size::from_bits(F::BITS))?))
F::from_bits(self.to_bits(Size::from_bits(F::BITS)))
}
#[inline]
pub fn try_to_f16(self) -> Result<Half, Size> {
self.try_to_float()
pub fn to_f16(self) -> Half {
self.to_float()
}
#[inline]
pub fn try_to_f32(self) -> Result<Single, Size> {
self.try_to_float()
pub fn to_f32(self) -> Single {
self.to_float()
}
#[inline]
pub fn try_to_f64(self) -> Result<Double, Size> {
self.try_to_float()
pub fn to_f64(self) -> Double {
self.to_float()
}
#[inline]
pub fn try_to_f128(self) -> Result<Quad, Size> {
self.try_to_float()
pub fn to_f128(self) -> Quad {
self.to_float()
}
}
macro_rules! from {
macro_rules! from_x_for_scalar_int {
($($ty:ty),*) => {
$(
impl From<$ty> for ScalarInt {
@ -432,30 +420,29 @@ macro_rules! from {
}
}
macro_rules! try_from {
macro_rules! from_scalar_int_for_x {
($($ty:ty),*) => {
$(
impl TryFrom<ScalarInt> for $ty {
type Error = Size;
impl From<ScalarInt> for $ty {
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
fn from(int: ScalarInt) -> Self {
// The `unwrap` cannot fail because to_bits (if it succeeds)
// is guaranteed to return a value that fits into the size.
int.try_to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
.map(|u| u.try_into().unwrap())
int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
.try_into().unwrap()
}
}
)*
}
}
from!(u8, u16, u32, u64, u128, bool);
try_from!(u8, u16, u32, u64, u128);
from_x_for_scalar_int!(u8, u16, u32, u64, u128, bool);
from_scalar_int_for_x!(u8, u16, u32, u64, u128);
impl TryFrom<ScalarInt> for bool {
type Error = Size;
type Error = ();
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
fn try_from(int: ScalarInt) -> Result<Self, ()> {
int.try_to_bool()
}
}
@ -463,7 +450,7 @@ impl TryFrom<ScalarInt> for bool {
impl From<char> for ScalarInt {
#[inline]
fn from(c: char) -> Self {
Self { data: c as u128, size: NonZero::new(std::mem::size_of::<char>() as u8).unwrap() }
(c as u32).into()
}
}
@ -476,10 +463,7 @@ impl TryFrom<ScalarInt> for char {
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
let Ok(bits) = int.try_to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
return Err(CharTryFromScalarInt);
};
match char::from_u32(bits.try_into().unwrap()) {
match char::from_u32(int.to_u32()) {
Some(c) => Ok(c),
None => Err(CharTryFromScalarInt),
}
@ -494,11 +478,10 @@ impl From<Half> for ScalarInt {
}
}
impl TryFrom<ScalarInt> for Half {
type Error = Size;
impl From<ScalarInt> for Half {
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.try_to_bits(Size::from_bytes(2)).map(Self::from_bits)
fn from(int: ScalarInt) -> Self {
Self::from_bits(int.to_bits(Size::from_bytes(2)))
}
}
@ -510,11 +493,10 @@ impl From<Single> for ScalarInt {
}
}
impl TryFrom<ScalarInt> for Single {
type Error = Size;
impl From<ScalarInt> for Single {
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.try_to_bits(Size::from_bytes(4)).map(Self::from_bits)
fn from(int: ScalarInt) -> Self {
Self::from_bits(int.to_bits(Size::from_bytes(4)))
}
}
@ -526,11 +508,10 @@ impl From<Double> for ScalarInt {
}
}
impl TryFrom<ScalarInt> for Double {
type Error = Size;
impl From<ScalarInt> for Double {
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.try_to_bits(Size::from_bytes(8)).map(Self::from_bits)
fn from(int: ScalarInt) -> Self {
Self::from_bits(int.to_bits(Size::from_bytes(8)))
}
}
@ -542,11 +523,10 @@ impl From<Quad> for ScalarInt {
}
}
impl TryFrom<ScalarInt> for Quad {
type Error = Size;
impl From<ScalarInt> for Quad {
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.try_to_bits(Size::from_bytes(16)).map(Self::from_bits)
fn from(int: ScalarInt) -> Self {
Self::from_bits(int.to_bits(Size::from_bytes(16)))
}
}

View file

@ -79,7 +79,7 @@ impl<'tcx> ValTree<'tcx> {
}
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.try_to_scalar_int().and_then(|s| s.try_to_target_usize(tcx).ok())
self.try_to_scalar_int().map(|s| s.to_target_usize(tcx))
}
/// Get the values inside the ValTree as a slice of bytes. This only works for
@ -100,8 +100,9 @@ impl<'tcx> ValTree<'tcx> {
_ => return None,
}
Some(tcx.arena.alloc_from_iter(
self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().try_to_u8().unwrap()),
))
Some(
tcx.arena
.alloc_from_iter(self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().to_u8())),
)
}
}

View file

@ -1652,7 +1652,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
if let ty::ConstKind::Value(_, ty::ValTree::Leaf(int)) = len.kind() {
match self.tcx().try_get_global_alloc(prov.alloc_id()) {
Some(GlobalAlloc::Memory(alloc)) => {
let len = int.assert_bits(self.tcx().data_layout.pointer_size);
let len = int.to_bits(self.tcx().data_layout.pointer_size);
let range =
AllocRange { start: offset, size: Size::from_bytes(len) };
if let Ok(byte_str) =
@ -1730,7 +1730,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
}
// Pointer types
ty::Ref(..) | ty::RawPtr(_, _) | ty::FnPtr(_) => {
let data = int.assert_bits(self.tcx().data_layout.pointer_size);
let data = int.to_bits(self.tcx().data_layout.pointer_size);
self.typed_value(
|this| {
write!(this, "0x{data:x}")?;