use crate::hir::map::{DefPathData, DisambiguatedDefPathData};
use crate::middle::cstore::{ExternCrate, ExternCrateSource};
use crate::middle::region;
-use crate::mir::interpret::{sign_extend, truncate, ConstValue, Scalar};
+use crate::mir::interpret::{sign_extend, truncate, AllocId, ConstValue, Pointer, Scalar};
use crate::ty::layout::{Integer, IntegerExt, Size};
use crate::ty::subst::{GenericArg, GenericArgKind, Subst};
use crate::ty::{self, DefIdTree, ParamConst, Ty, TyCtxt, TypeFoldable};
})
}
+ fn print_type_ascribed(
+ mut self,
+ f: impl FnOnce(Self) -> Result<Self, Self::Error>,
+ ty: Ty<'tcx>,
+ print_ty: bool,
+ ) -> Result<Self::Const, Self::Error> {
+ self.write_str("{")?;
+ self = f(self)?;
+ if print_ty {
+ self.write_str(": ")?;
+ self = self.print_type(ty)?;
+ }
+ self.write_str("}")?;
+ Ok(self)
+ }
+
fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
define_scoped_cx!(self);
Ok(self)
}
- fn pretty_print_const_value(
+ fn pretty_print_const_scalar(
mut self,
- ct: ConstValue<'tcx>,
+ scalar: Scalar,
ty: Ty<'tcx>,
print_ty: bool,
) -> Result<Self::Const, Self::Error> {
define_scoped_cx!(self);
- if self.tcx().sess.verbose() {
- p!(write("ConstValue({:?}: {:?})", ct, ty));
- return Ok(self);
- }
-
- let u8 = self.tcx().types.u8;
-
- match (ct, &ty.kind) {
- (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Bool) => {
- p!(write("{}", if data == 0 { "false" } else { "true" }))
- }
- (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Float(ast::FloatTy::F32)) => {
+ match (scalar, &ty.kind) {
+ // Single element arrays print their element (they are `#[transparent]`) enclosed in
+ // square brackets.
+ (_, ty::Array(t, n)) if n.eval_usize(self.tcx(), ty::ParamEnv::empty()) == 1 => {
+ p!(write("["));
+ self = self.pretty_print_const_scalar(scalar, t, print_ty)?;
+ p!(write("]"));
+ }
+ // Byte strings (&[u8; N])
+ (Scalar::Ptr(ptr), ty::Ref(_, ty::TyS { kind: ty::Array(t, n), .. }, _))
+ if *t == self.tcx().types.u8 =>
+ {
+ let n = n.eval_usize(self.tcx(), ty::ParamEnv::empty());
+ let byte_str = self
+ .tcx()
+ .alloc_map
+ .lock()
+ .unwrap_memory(ptr.alloc_id)
+ .get_bytes(&self.tcx(), ptr, Size::from_bytes(n))
+ .unwrap();
+ p!(pretty_print_byte_str(byte_str));
+ }
+ // Bool
+ (Scalar::Raw { data: 0, .. }, ty::Bool) => p!(write("false")),
+ (Scalar::Raw { data: 1, .. }, ty::Bool) => p!(write("true")),
+ (Scalar::Raw { data, .. }, ty::Bool) => p!(write("{}_bool", data)),
+ // Float
+ (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F32)) => {
p!(write("{}f32", Single::from_bits(data)))
}
- (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Float(ast::FloatTy::F64)) => {
+ (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F64)) => {
p!(write("{}f64", Double::from_bits(data)))
}
- (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Uint(ui)) => {
+ // Int
+ (Scalar::Raw { data, .. }, ty::Uint(ui)) => {
let bit_size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size();
let max = truncate(u128::MAX, bit_size);
p!(write("{}{}", data, ui_str))
};
}
- (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Int(i)) => {
+ (Scalar::Raw { data, .. }, ty::Int(i)) => {
let bit_size = Integer::from_attr(&self.tcx(), SignedInt(*i)).size().bits() as u128;
let min = 1u128 << (bit_size - 1);
let max = min - 1;
_ => p!(write("{}{}", sign_extend(data, size) as i128, i_str)),
}
}
- (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Char) => {
- p!(write("{:?}", ::std::char::from_u32(data as u32).unwrap()))
+ // Char
+ (Scalar::Raw { data, .. }, ty::Char) => match ::std::char::from_u32(data as u32) {
+ Some(c) => p!(write("{:?}", c)),
+ None => p!(write("{}_char", data)),
+ },
+ // References and pointers
+ (Scalar::Raw { data: 0, .. }, ty::RawPtr(_)) => p!(write("{{null pointer}}")),
+ // This is UB, but we still print it
+ (Scalar::Raw { data: 0, .. }, ty::Ref(_, ty, _)) => {
+ p!(write("{{null reference to "), print(ty), write("}}"))
}
- (ConstValue::Scalar(_), ty::RawPtr(_)) => p!(write("{{pointer}}")),
- (ConstValue::Scalar(Scalar::Ptr(ptr)), ty::FnPtr(_)) => {
+ (Scalar::Raw { data, .. }, ty::Ref(..)) | (Scalar::Raw { data, .. }, ty::RawPtr(_)) => {
+ let pointer_width = self.tcx().data_layout.pointer_size.bytes();
+ p!(write("0x{:01$x}", data, pointer_width as usize * 2))
+ }
+ (Scalar::Ptr(ptr), ty::FnPtr(_)) => {
let instance = {
let alloc_map = self.tcx().alloc_map.lock();
alloc_map.unwrap_fn(ptr.alloc_id)
};
p!(print_value_path(instance.def_id(), instance.substs));
}
- _ => {
- let printed = if let ty::Ref(_, ref_ty, _) = ty.kind {
- let byte_str = match (ct, &ref_ty.kind) {
- (ConstValue::Scalar(Scalar::Ptr(ptr)), ty::Array(t, n)) if *t == u8 => {
- let n = n.eval_usize(self.tcx(), ty::ParamEnv::empty());
- Some(
- self.tcx()
- .alloc_map
- .lock()
- .unwrap_memory(ptr.alloc_id)
- .get_bytes(&self.tcx(), ptr, Size::from_bytes(n))
- .unwrap(),
- )
- }
- (ConstValue::Slice { data, start, end }, ty::Slice(t)) if *t == u8 => {
- // The `inspect` here is okay since we checked the bounds, and there are
- // no relocations (we have an active slice reference here). We don't use
- // this result to affect interpreter execution.
- Some(data.inspect_with_undef_and_ptr_outside_interpreter(start..end))
- }
- _ => None,
- };
+ // For zsts just print their type as their value gives no extra information
+ (Scalar::Raw { size: 0, .. }, _) => p!(print(ty)),
+ // Nontrivial types with scalar bit representation
+ (Scalar::Raw { data, size }, _) => {
+ self = self.print_type_ascribed(
+ |mut this| {
+ write!(this, "0x{:01$x}", data, size as usize * 2)?;
+ Ok(this)
+ },
+ ty,
+ print_ty,
+ )?
+ }
+ // Any pointer values not covered by a branch above
+ (Scalar::Ptr(p), _) => {
+ self = self.pretty_print_const_pointer(p, ty, print_ty)?;
+ }
+ }
+ Ok(self)
+ }
- if let Some(byte_str) = byte_str {
- p!(write("b\""));
- for &c in byte_str {
- for e in std::ascii::escape_default(c) {
- self.write_char(e as char)?;
- }
- }
- p!(write("\""));
- true
- } else if let (ConstValue::Slice { data, start, end }, ty::Str) =
- (ct, &ref_ty.kind)
- {
- // The `inspect` here is okay since we checked the bounds, and there are no
- // relocations (we have an active `str` reference here). We don't use this
- // result to affect interpreter execution.
- let slice = data.inspect_with_undef_and_ptr_outside_interpreter(start..end);
- let s = ::std::str::from_utf8(slice).expect("non utf8 str from miri");
- p!(write("{:?}", s));
- true
- } else {
- false
- }
- } else {
- false
- };
- if !printed {
- // fallback
- p!(write("{:?}", ct));
- if print_ty {
- p!(write(": "), print(ty));
- }
- }
+ /// This is overridden for MIR printing because we only want to hide alloc ids from users, not
+ /// from MIR where it is actually useful.
+ fn pretty_print_const_pointer(
+ self,
+ _: Pointer,
+ ty: Ty<'tcx>,
+ print_ty: bool,
+ ) -> Result<Self::Const, Self::Error> {
+ self.print_type_ascribed(
+ |mut this| {
+ this.write_str("pointer")?;
+ Ok(this)
+ },
+ ty,
+ print_ty,
+ )
+ }
+
+ fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result<Self::Const, Self::Error> {
+ define_scoped_cx!(self);
+ p!(write("b\""));
+ for &c in byte_str {
+ for e in std::ascii::escape_default(c) {
+ self.write_char(e as char)?;
}
- };
+ }
+ p!(write("\""));
Ok(self)
}
+
+ fn pretty_print_const_value(
+ mut self,
+ ct: ConstValue<'tcx>,
+ ty: Ty<'tcx>,
+ print_ty: bool,
+ ) -> Result<Self::Const, Self::Error> {
+ define_scoped_cx!(self);
+
+ if self.tcx().sess.verbose() {
+ p!(write("ConstValue({:?}: {:?})", ct, ty));
+ return Ok(self);
+ }
+
+ let u8_type = self.tcx().types.u8;
+
+ match (ct, &ty.kind) {
+ (ConstValue::Scalar(scalar), _) => self.pretty_print_const_scalar(scalar, ty, print_ty),
+ (
+ ConstValue::Slice { data, start, end },
+ ty::Ref(_, ty::TyS { kind: ty::Slice(t), .. }, _),
+ ) if *t == u8_type => {
+ // The `inspect` here is okay since we checked the bounds, and there are
+ // no relocations (we have an active slice reference here). We don't use
+ // this result to affect interpreter execution.
+ let byte_str = data.inspect_with_undef_and_ptr_outside_interpreter(start..end);
+ self.pretty_print_byte_str(byte_str)
+ }
+ (
+ ConstValue::Slice { data, start, end },
+ ty::Ref(_, ty::TyS { kind: ty::Str, .. }, _),
+ ) => {
+ // The `inspect` here is okay since we checked the bounds, and there are no
+ // relocations (we have an active `str` reference here). We don't use this
+ // result to affect interpreter execution.
+ let slice = data.inspect_with_undef_and_ptr_outside_interpreter(start..end);
+ let s = ::std::str::from_utf8(slice).expect("non utf8 str from miri");
+ p!(write("{:?}", s));
+ Ok(self)
+ }
+ (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => {
+ let n = n.eval_usize(self.tcx(), ty::ParamEnv::empty());
+ let n = Size::from_bytes(n);
+ let ptr = Pointer::new(AllocId(0), offset);
+
+ let byte_str = alloc.get_bytes(&self.tcx(), ptr, n).unwrap();
+ p!(write("*"));
+ p!(pretty_print_byte_str(byte_str));
+ Ok(self)
+ }
+ // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
+ // their fields instead of just dumping the memory.
+ _ => {
+ // fallback
+ p!(write("{:?}", ct));
+ if print_ty {
+ p!(write(": "), print(ty));
+ }
+ Ok(self)
+ }
+ }
+ }
}
// HACK(eddyb) boxed to avoid moving around a large struct by-value.
empty_path: bool,
in_value: bool,
+ pub print_alloc_ids: bool,
used_region_names: FxHashSet<Symbol>,
region_index: usize,
fmt,
empty_path: false,
in_value: ns == Namespace::ValueNS,
+ print_alloc_ids: false,
used_region_names: Default::default(),
region_index: 0,
binder_depth: 0,
ty::ReStatic | ty::ReEmpty(_) | ty::ReClosureBound(_) => true,
}
}
+
+ fn pretty_print_const_pointer(
+ self,
+ p: Pointer,
+ ty: Ty<'tcx>,
+ print_ty: bool,
+ ) -> Result<Self::Const, Self::Error> {
+ self.print_type_ascribed(
+ |mut this| {
+ define_scoped_cx!(this);
+ if this.print_alloc_ids {
+ p!(write("{:?}", p));
+ } else {
+ p!(write("pointer"));
+ }
+ Ok(this)
+ },
+ ty,
+ print_ty,
+ )
+ }
+
+ fn print_type_ascribed(
+ mut self,
+ f: impl FnOnce(Self) -> Result<Self, Self::Error>,
+ ty: Ty<'tcx>,
+ print_ty: bool,
+ ) -> Result<Self::Const, Self::Error> {
+ self.write_str("{")?;
+ self = f(self)?;
+ if print_ty {
+ self.write_str(": ")?;
+ let was_in_value = std::mem::replace(&mut self.in_value, false);
+ self = self.print_type(ty)?;
+ self.in_value = was_in_value;
+ }
+ self.write_str("}")?;
+ Ok(self)
+ }
}
// HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`.
use std::convert::{TryFrom, TryInto};
-use rustc::ty::layout::{
- self, HasDataLayout, IntegerExt, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx,
-};
-use rustc::{mir, ty};
-
use super::{InterpCx, MPlaceTy, Machine, MemPlace, Place, PlaceTy};
pub use rustc::mir::interpret::ScalarMaybeUndef;
use rustc::mir::interpret::{
sign_extend, truncate, AllocId, ConstValue, GlobalId, InterpResult, Pointer, Scalar,
};
-use rustc_ast::ast;
+use rustc::ty::layout::{
+ self, HasDataLayout, IntegerExt, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx,
+};
+use rustc::ty::print::{FmtPrinter, PrettyPrinter, Printer};
+use rustc::ty::Ty;
+use rustc::{mir, ty};
+use rustc_hir::def::Namespace;
use rustc_macros::HashStable;
+use std::fmt::Write;
/// An `Immediate` represents a single immediate self-contained Rust value.
///
pub layout: TyLayout<'tcx>,
}
-// `Tag: Copy` because some methods on `Scalar` consume them by value
impl<Tag: Copy> std::fmt::Display for ImmTy<'tcx, Tag> {
- fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match &self.imm {
- // We cannot use `to_bits_or_ptr` as we do not have a `tcx`.
- // So we use `is_bits` and circumvent a bunch of sanity checking -- but
- // this is anyway only for printing.
- Immediate::Scalar(ScalarMaybeUndef::Scalar(s)) if s.is_ptr() => {
- fmt.write_str("{pointer}")
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ /// Helper function for printing a scalar to a FmtPrinter
+ fn p<'a, 'tcx, F: std::fmt::Write, Tag>(
+ mut cx: FmtPrinter<'a, 'tcx, F>,
+ s: ScalarMaybeUndef<Tag>,
+ ty: Ty<'tcx>,
+ ) -> Result<FmtPrinter<'a, 'tcx, F>, std::fmt::Error> {
+ match s {
+ ScalarMaybeUndef::Scalar(s) => {
+ cx.pretty_print_const_scalar(s.erase_tag(), ty, true)
+ }
+ ScalarMaybeUndef::Undef => {
+ cx.write_str("{undef ")?;
+ cx = cx.print_type(ty)?;
+ cx.write_str("}")?;
+ Ok(cx)
+ }
}
- Immediate::Scalar(ScalarMaybeUndef::Scalar(s)) => {
- let s = s.assert_bits(self.layout.size);
- match self.layout.ty.kind {
- ty::Int(_) => {
- return write!(fmt, "{}", super::sign_extend(s, self.layout.size) as i128,);
- }
- ty::Uint(_) => return write!(fmt, "{}", s),
- ty::Bool if s == 0 => return fmt.write_str("false"),
- ty::Bool if s == 1 => return fmt.write_str("true"),
- ty::Char => {
- if let Some(c) = u32::try_from(s).ok().and_then(std::char::from_u32) {
- return write!(fmt, "{}", c);
- }
- }
- ty::Float(ast::FloatTy::F32) => {
- if let Ok(u) = u32::try_from(s) {
- return write!(fmt, "{}", f32::from_bits(u));
- }
- }
- ty::Float(ast::FloatTy::F64) => {
- if let Ok(u) = u64::try_from(s) {
- return write!(fmt, "{}", f64::from_bits(u));
- }
+ }
+ ty::tls::with(|tcx| {
+ match self.imm {
+ Immediate::Scalar(s) => {
+ if let Some(ty) = tcx.lift(&self.layout.ty) {
+ let cx = FmtPrinter::new(tcx, f, Namespace::ValueNS);
+ p(cx, s, ty)?;
+ return Ok(());
}
- _ => {}
+ write!(f, "{:?}: {}", s.erase_tag(), self.layout.ty)
+ }
+ Immediate::ScalarPair(a, b) => {
+ // FIXME(oli-obk): at least print tuples and slices nicely
+ write!(f, "({:?}, {:?}): {}", a.erase_tag(), b.erase_tag(), self.layout.ty,)
}
- write!(fmt, "{:x}", s)
}
- Immediate::Scalar(ScalarMaybeUndef::Undef) => fmt.write_str("{undef}"),
- Immediate::ScalarPair(..) => fmt.write_str("{wide pointer or tuple}"),
- }
+ })
}
}