提交 02dbb35b 编写于 作者: O Oliver Scherer

Deduplicate and clean up pretty printing logic

上级 15812785
......@@ -2562,15 +2562,15 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
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(())
})
}
}
......
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 @@ fn pretty_path_append_impl(
})
}
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);
......@@ -893,32 +909,49 @@ fn pretty_print_const(
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);
......@@ -929,7 +962,7 @@ fn pretty_print_const_value(
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 @@ fn pretty_print_const_value(
_ => 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.
......@@ -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<Symbol>,
region_index: usize,
......@@ -1054,6 +1152,7 @@ pub fn new(tcx: TyCtxt<'tcx>, fmt: F, ns: Namespace) -> Self {
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 @@ fn region_should_not_be_omitted(&self, region: ty::Region<'_>) -> bool {
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`.
......
......@@ -157,6 +157,7 @@ fn path_generic_args(
}
}
}
impl PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> {
fn region_should_not_be_omitted(&self, _region: ty::Region<'_>) -> bool {
false
......
......@@ -3,18 +3,20 @@
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.
///
......@@ -92,47 +94,42 @@ pub struct ImmTy<'tcx, Tag = ()> {
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}"),
}
})
}
}
......
......@@ -14,7 +14,7 @@ fn main() {}
// START rustc.FOO.PromoteTemps.before.mir
// bb0: {
// ...
// _5 = const Scalar(alloc1+0) : &i32;
// _5 = const {alloc1+0: &i32};
// _4 = &(*_5);
// _3 = [move _4];
// _2 = &_3;
......@@ -31,7 +31,7 @@ fn main() {}
// START rustc.BAR.PromoteTemps.before.mir
// bb0: {
// ...
// _5 = const Scalar(alloc0+0) : &i32;
// _5 = const {alloc0+0: &i32};
// _4 = &(*_5);
// _3 = [move _4];
// _2 = &_3;
......
......@@ -31,7 +31,7 @@ fn main() {
// START rustc.main.ConstProp.after.mir
// bb0: {
// ...
// _3 = const Scalar(0x01) : std::option::Option<bool>;
// _3 = const {0x01: std::option::Option<bool>};
// _4 = const 1isize;
// switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1];
// }
......
......@@ -29,7 +29,7 @@ fn main() {
// START rustc.main.ConstProp.after.mir
// bb0: {
// ...
// _3 = const Scalar(<ZST>) : ();
// _3 = const ();
// _2 = (move _3, const 0u8, const 0u8);
// ...
// _1 = const encode(move _2) -> bb1;
......
......@@ -10,10 +10,10 @@ fn main() {
// START rustc.main.ConstProp.before.mir
// bb0: {
// ...
// _3 = const Scalar(alloc0+0) : &u8;
// _3 = const {alloc0+0: &u8};
// _2 = (*_3);
// ...
// _5 = const Scalar(alloc0+0) : &u8;
// _5 = const {alloc0+0: &u8};
// _4 = (*_5);
// _1 = Add(move _2, move _4);
// ...
......
......@@ -35,28 +35,28 @@ fn main() {
// bb0: {
// StorageLive(_1);
// StorageLive(_2);
// _2 = const Scalar(<ZST>) : ();
// _2 = const ();
// StorageLive(_3);
// _3 = const Scalar(<ZST>) : ();
// _1 = const Scalar(<ZST>) : ((), ());
// _3 = const ();
// _1 = const ((), ());
// StorageDead(_3);
// StorageDead(_2);
// StorageDead(_1);
// StorageLive(_4);
// StorageLive(_6);
// _6 = const Scalar(<ZST>) : ();
// _6 = const ();
// StorageLive(_7);
// _7 = const Scalar(<ZST>) : ();
// _7 = const ();
// StorageDead(_7);
// StorageDead(_6);
// _4 = const use_zst(const Scalar(<ZST>) : ((), ())) -> bb1;
// _4 = const use_zst(const ((), ())) -> bb1;
// }
// bb1: {
// StorageDead(_4);
// StorageLive(_8);
// StorageLive(_10);
// StorageLive(_11);
// _11 = const Scalar(0x28) : Temp;
// _11 = const {0x28 : Temp};
// _10 = const 40u8;
// StorageDead(_10);
// _8 = const use_u8(const 42u8) -> bb2;
......@@ -75,7 +75,7 @@ fn main() {
// }
// bb0: {
// StorageLive(_1);
// _1 = const use_zst(const Scalar(<ZST>) : ((), ())) -> bb1;
// _1 = const use_zst(const ((), ())) -> bb1;
// }
// bb1: {
// StorageDead(_1);
......
// normalize-stderr-64bit "0x00000000" -> "0x[PREFIX]"
// normalize-stderr-32bit "0x" -> "0x[PREFIX]"
#![feature(const_generics, const_compare_raw_pointers)]
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
......
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
--> $DIR/raw-ptr-const-param.rs:1:12
--> $DIR/raw-ptr-const-param.rs:4:12
|
LL | #![feature(const_generics, const_compare_raw_pointers)]
| ^^^^^^^^^^^^^^
......@@ -7,15 +7,15 @@ LL | #![feature(const_generics, const_compare_raw_pointers)]
= note: `#[warn(incomplete_features)]` on by default
error[E0308]: mismatched types
--> $DIR/raw-ptr-const-param.rs:7:38
--> $DIR/raw-ptr-const-param.rs:10:38
|
LL | let _: Const<{15 as *const _}> = Const::<{10 as *const _}>;
| ----------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{pointer}`, found `{pointer}`
| ----------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `0x[PREFIX]0000000f`, found `0x[PREFIX]0000000a`
| |
| expected due to this
|
= note: expected struct `Const<{pointer}>`
found struct `Const<{pointer}>`
= note: expected struct `Const<0x[PREFIX]0000000f>`
found struct `Const<0x[PREFIX]0000000a>`
error: aborting due to previous error
......
......@@ -43,7 +43,7 @@ error: any use of this value will cause an error
LL | intrinsics::ptr_offset_from(self, origin)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| exact_div: 1 cannot be divided by 2 without remainder
| exact_div: 1isize cannot be divided by 2isize without remainder
| inside call to `std::ptr::const_ptr::<impl *const u16>::offset_from` at $DIR/offset_from_ub.rs:36:14
|
::: $DIR/offset_from_ub.rs:31:1
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册