From 02dbb35b2b6ed869f14a8aecaf9dad5c72d5cb0b Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Mon, 23 Dec 2019 17:41:06 +0100 Subject: Deduplicate and clean up pretty printing logic --- src/librustc/mir/mod.rs | 16 +-- src/librustc/ty/print/pretty.rs | 292 +++++++++++++++++++++++++++++----------- 2 files changed, 223 insertions(+), 85 deletions(-) (limited to 'src/librustc') diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index b2413f5a2c8..d9f49b6784e 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2562,15 +2562,15 @@ impl<'tcx> Debug for Constant<'tcx> { impl<'tcx> Display for Constant<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use crate::ty::print::PrettyPrinter; write!(fmt, "const ")?; - // FIXME make the default pretty printing of raw pointers more detailed. Here we output the - // debug representation of raw pointers, so that the raw pointers in the mir dump output are - // detailed and just not '{pointer}'. - if let ty::RawPtr(_) = self.literal.ty.kind { - write!(fmt, "{:?} : {}", self.literal.val, self.literal.ty) - } else { - write!(fmt, "{}", self.literal) - } + ty::tls::with(|tcx| { + let literal = tcx.lift(&self.literal).unwrap(); + let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS); + cx.print_alloc_ids = true; + cx.pretty_print_const(literal, true)?; + Ok(()) + }) } } diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs index 05dcc9e85ac..2f53b62fdec 100644 --- a/src/librustc/ty/print/pretty.rs +++ b/src/librustc/ty/print/pretty.rs @@ -1,7 +1,7 @@ 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}; @@ -457,6 +457,22 @@ pub trait PrettyPrinter<'tcx>: }) } + fn print_type_ascribed( + mut self, + f: impl FnOnce(Self) -> Result, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + 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 { define_scoped_cx!(self); @@ -893,32 +909,49 @@ pub trait PrettyPrinter<'tcx>: 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 { 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); @@ -929,7 +962,7 @@ pub trait PrettyPrinter<'tcx>: 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; @@ -943,76 +976,140 @@ pub trait PrettyPrinter<'tcx>: _ => 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.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 { + 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 { + 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. @@ -1024,6 +1121,7 @@ pub struct FmtPrinterData<'a, 'tcx, F> { empty_path: bool, in_value: bool, + pub print_alloc_ids: bool, used_region_names: FxHashSet, region_index: usize, @@ -1054,6 +1152,7 @@ impl FmtPrinter<'a, 'tcx, F> { fmt, empty_path: false, in_value: ns == Namespace::ValueNS, + print_alloc_ids: false, used_region_names: Default::default(), region_index: 0, binder_depth: 0, @@ -1382,6 +1481,45 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { ty::ReStatic | ty::ReEmpty(_) | ty::ReClosureBound(_) => true, } } + + fn pretty_print_const_pointer( + self, + p: Pointer, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + 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, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + 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`. -- cgit 1.4.1-3-g733a5