about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-03-16 16:31:23 +0000
committerbors <bors@rust-lang.org>2020-03-16 16:31:23 +0000
commitdd67187965e136bff1ed05e035293441c60f0790 (patch)
treed14a26886de5881e560f4754531975e84957bd29 /src
parent59f4ba95045e91a63e921e0d736242d7e1ffabec (diff)
parent6ca65bdd752a32b376a7f5cb1dba4953e91f46bb (diff)
downloadrust-dd67187965e136bff1ed05e035293441c60f0790.tar.gz
rust-dd67187965e136bff1ed05e035293441c60f0790.zip
Auto merge of #67133 - oli-obk:it_must_be_a_sign, r=eddyb
Deduplicate pretty printing of constants

r? @eddyb for the pretty printing logic
cc @RalfJung
Diffstat (limited to 'src')
-rw-r--r--src/librustc/mir/mod.rs16
-rw-r--r--src/librustc/ty/print/pretty.rs400
-rw-r--r--src/librustc_mir/interpret/intrinsics/type_name.rs1
-rw-r--r--src/librustc_mir/interpret/operand.rs83
-rw-r--r--src/test/mir-opt/const-promotion-extern-static.rs4
-rw-r--r--src/test/mir-opt/const_prop/discriminant.rs2
-rw-r--r--src/test/mir-opt/const_prop/issue-66971.rs2
-rw-r--r--src/test/mir-opt/const_prop/read_immutable_static.rs4
-rw-r--r--src/test/mir-opt/simplify-locals-removes-unused-consts.rs24
-rw-r--r--src/test/ui/const-generics/cannot-infer-const-args.stderr2
-rw-r--r--src/test/ui/const-generics/const-generic-type_name.rs2
-rw-r--r--src/test/ui/const-generics/fn-const-param-infer.stderr12
-rw-r--r--src/test/ui/const-generics/raw-ptr-const-param.rs4
-rw-r--r--src/test/ui/const-generics/raw-ptr-const-param.stderr10
-rw-r--r--src/test/ui/consts/offset_from_ub.stderr2
15 files changed, 379 insertions, 189 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index a26ff2ea7aa..9018cd2656f 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..cb01d821c18 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};
@@ -17,6 +17,7 @@ use rustc_span::symbol::{kw, Symbol};
 use rustc_target::spec::abi::Abi;
 
 use std::cell::Cell;
+use std::char;
 use std::collections::BTreeMap;
 use std::fmt::{self, Write as _};
 use std::ops::{Deref, DerefMut};
@@ -210,6 +211,21 @@ pub trait PrettyPrinter<'tcx>:
         Ok(self)
     }
 
+    /// Prints `{f: t}` or `{f as t}` depending on the `cast` argument
+    fn typed_value(
+        mut self,
+        f: impl FnOnce(Self) -> Result<Self, Self::Error>,
+        t: impl FnOnce(Self) -> Result<Self, Self::Error>,
+        conversion: &str,
+    ) -> Result<Self::Const, Self::Error> {
+        self.write_str("{")?;
+        self = f(self)?;
+        self.write_str(conversion)?;
+        self = t(self)?;
+        self.write_str("}")?;
+        Ok(self)
+    }
+
     /// Prints `<...>` around what `f` prints.
     fn generic_delimiters(
         self,
@@ -517,14 +533,7 @@ pub trait PrettyPrinter<'tcx>:
             ty::Error => p!(write("[type error]")),
             ty::Param(ref param_ty) => p!(write("{}", param_ty)),
             ty::Bound(debruijn, bound_ty) => match bound_ty.kind {
-                ty::BoundTyKind::Anon => {
-                    if debruijn == ty::INNERMOST {
-                        p!(write("^{}", bound_ty.var.index()))
-                    } else {
-                        p!(write("^{}_{}", debruijn.index(), bound_ty.var.index()))
-                    }
-                }
-
+                ty::BoundTyKind::Anon => self.pretty_print_bound_var(debruijn, bound_ty.var)?,
                 ty::BoundTyKind::Param(p) => p!(write("{}", p)),
             },
             ty::Adt(def, substs) => {
@@ -689,7 +698,7 @@ pub trait PrettyPrinter<'tcx>:
                     // array length anon const, rustc will (with debug assertions) print the
                     // constant's path. Which will end up here again.
                     p!(write("_"));
-                } else if let Some(n) = sz.try_eval_usize(self.tcx(), ty::ParamEnv::empty()) {
+                } else if let Some(n) = sz.val.try_to_bits(self.tcx().data_layout.pointer_size) {
                     p!(write("{}", n));
                 } else {
                     p!(write("_"));
@@ -702,6 +711,18 @@ pub trait PrettyPrinter<'tcx>:
         Ok(self)
     }
 
+    fn pretty_print_bound_var(
+        &mut self,
+        debruijn: ty::DebruijnIndex,
+        var: ty::BoundVar,
+    ) -> Result<(), Self::Error> {
+        if debruijn == ty::INNERMOST {
+            write!(self, "^{}", var.index())
+        } else {
+            write!(self, "^{}_{}", debruijn.index(), var.index())
+        }
+    }
+
     fn infer_ty_name(&self, _: ty::TyVid) -> Option<String> {
         None
     }
@@ -842,16 +863,23 @@ pub trait PrettyPrinter<'tcx>:
 
         macro_rules! print_underscore {
             () => {{
-                p!(write("_"));
                 if print_ty {
-                    p!(write(": "), print(ct.ty));
+                    self = self.typed_value(
+                        |mut this| {
+                            write!(this, "_")?;
+                            Ok(this)
+                        },
+                        |this| this.print_type(ct.ty),
+                        ": ",
+                    )?;
+                } else {
+                    write!(self, "_")?;
                 }
             }};
         }
 
-        match (ct.val, &ct.ty.kind) {
-            (_, ty::FnDef(did, substs)) => p!(print_value_path(*did, substs)),
-            (ty::ConstKind::Unevaluated(did, substs, promoted), _) => {
+        match ct.val {
+            ty::ConstKind::Unevaluated(did, substs, promoted) => {
                 if let Some(promoted) = promoted {
                     p!(print_value_path(did, substs));
                     p!(write("::{:?}", promoted));
@@ -876,49 +904,73 @@ pub trait PrettyPrinter<'tcx>:
                     }
                 }
             }
-            (ty::ConstKind::Infer(..), _) => print_underscore!(),
-            (ty::ConstKind::Param(ParamConst { name, .. }), _) => p!(write("{}", name)),
-            (ty::ConstKind::Value(value), _) => {
+            ty::ConstKind::Infer(..) => print_underscore!(),
+            ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)),
+            ty::ConstKind::Value(value) => {
                 return self.pretty_print_const_value(value, ct.ty, print_ty);
             }
 
-            _ => {
-                // fallback
-                p!(write("{:?}", ct.val));
-                if print_ty {
-                    p!(write(": "), print(ct.ty));
-                }
+            ty::ConstKind::Bound(debruijn, bound_var) => {
+                self.pretty_print_bound_var(debruijn, bound_var)?
             }
+            ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)),
         };
         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) {
+            // Byte strings (&[u8; N])
+            (
+                Scalar::Ptr(ptr),
+                ty::Ref(
+                    _,
+                    ty::TyS {
+                        kind:
+                            ty::Array(
+                                ty::TyS { kind: ty::Uint(ast::UintTy::U8), .. },
+                                ty::Const {
+                                    val:
+                                        ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw {
+                                            data,
+                                            ..
+                                        })),
+                                    ..
+                                },
+                            ),
+                        ..
+                    },
+                    _,
+                ),
+            ) => {
+                let byte_str = self
+                    .tcx()
+                    .alloc_map
+                    .lock()
+                    .unwrap_memory(ptr.alloc_id)
+                    .get_bytes(&self.tcx(), ptr, Size::from_bytes(*data as u64))
+                    .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")),
+            // 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);
 
@@ -926,93 +978,191 @@ pub trait PrettyPrinter<'tcx>:
                 if data == max {
                     p!(write("std::{}::MAX", ui_str))
                 } else {
-                    p!(write("{}{}", data, ui_str))
+                    if print_ty { p!(write("{}{}", data, ui_str)) } else { p!(write("{}", data)) }
                 };
             }
-            (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Int(i)) => {
-                let bit_size = Integer::from_attr(&self.tcx(), SignedInt(*i)).size().bits() as u128;
+            (Scalar::Raw { data, .. }, ty::Int(i)) => {
+                let size = Integer::from_attr(&self.tcx(), SignedInt(*i)).size();
+                let bit_size = size.bits() as u128;
                 let min = 1u128 << (bit_size - 1);
                 let max = min - 1;
 
-                let ty = self.tcx().lift(&ty).unwrap();
-                let size = self.tcx().layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
                 let i_str = i.name_str();
                 match data {
                     d if d == min => p!(write("std::{}::MIN", i_str)),
                     d if d == max => p!(write("std::{}::MAX", i_str)),
-                    _ => p!(write("{}{}", sign_extend(data, size) as i128, i_str)),
+                    _ => {
+                        let data = sign_extend(data, size) as i128;
+                        if print_ty {
+                            p!(write("{}{}", data, i_str))
+                        } else {
+                            p!(write("{}", data))
+                        }
+                    }
                 }
             }
-            (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Char) => {
-                p!(write("{:?}", ::std::char::from_u32(data as u32).unwrap()))
-            }
-            (ConstValue::Scalar(_), ty::RawPtr(_)) => p!(write("{{pointer}}")),
-            (ConstValue::Scalar(Scalar::Ptr(ptr)), ty::FnPtr(_)) => {
+            // Char
+            (Scalar::Raw { data, .. }, ty::Char) if char::from_u32(data as u32).is_some() => {
+                p!(write("{:?}", char::from_u32(data as u32).unwrap()))
+            }
+            // Raw pointers
+            (Scalar::Raw { data, .. }, ty::RawPtr(_)) => {
+                self = self.typed_value(
+                    |mut this| {
+                        write!(this, "0x{:x}", data)?;
+                        Ok(this)
+                    },
+                    |this| this.print_type(ty),
+                    " as ",
+                )?;
+            }
+            (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,
-                    };
-
-                    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
+                self = self.typed_value(
+                    |this| this.print_value_path(instance.def_id(), instance.substs),
+                    |this| this.print_type(ty),
+                    " as ",
+                )?;
+            }
+            // For function type zsts just printing the path is enough
+            (Scalar::Raw { size: 0, .. }, ty::FnDef(d, s)) => p!(print_value_path(*d, s)),
+            // Empty tuples are frequently occurring, so don't print the fallback.
+            (Scalar::Raw { size: 0, .. }, ty::Tuple(ts)) if ts.is_empty() => p!(write("()")),
+            // Zero element arrays have a trivial representation.
+            (
+                Scalar::Raw { size: 0, .. },
+                ty::Array(
+                    _,
+                    ty::Const {
+                        val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data: 0, .. })),
+                        ..
+                    },
+                ),
+            ) => p!(write("[]")),
+            // Nontrivial types with scalar bit representation
+            (Scalar::Raw { data, size }, _) => {
+                let print = |mut this: Self| {
+                    if size == 0 {
+                        write!(this, "transmute(())")?;
                     } else {
-                        false
+                        write!(this, "transmute(0x{:01$x})", data, size as usize * 2)?;
                     }
+                    Ok(this)
+                };
+                self = if print_ty {
+                    self.typed_value(print, |this| this.print_type(ty), ": ")?
                 } else {
-                    false
+                    print(self)?
                 };
-                if !printed {
-                    // fallback
-                    p!(write("{:?}", ct));
-                    if print_ty {
-                        p!(write(": "), 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)
     }
+
+    /// 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(
+        mut self,
+        _: Pointer,
+        ty: Ty<'tcx>,
+        print_ty: bool,
+    ) -> Result<Self::Const, Self::Error> {
+        if print_ty {
+            self.typed_value(
+                |mut this| {
+                    this.write_str("&_")?;
+                    Ok(this)
+                },
+                |this| this.print_type(ty),
+                ": ",
+            )
+        } else {
+            self.write_str("&_")?;
+            Ok(self)
+        }
+    }
+
+    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.val.try_to_bits(self.tcx().data_layout.pointer_size).unwrap();
+                // cast is ok because we already checked for pointer size (32 or 64 bit) above
+                let n = Size::from_bytes(n as u64);
+                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 +1174,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 +1205,7 @@ impl<F> 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,
@@ -1326,6 +1478,22 @@ impl<F: fmt::Write> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> {
         self.pretty_in_binder(value)
     }
 
+    fn typed_value(
+        mut self,
+        f: impl FnOnce(Self) -> Result<Self, Self::Error>,
+        t: impl FnOnce(Self) -> Result<Self, Self::Error>,
+        conversion: &str,
+    ) -> Result<Self::Const, Self::Error> {
+        self.write_str("{")?;
+        self = f(self)?;
+        self.write_str(conversion)?;
+        let was_in_value = std::mem::replace(&mut self.in_value, false);
+        self = t(self)?;
+        self.in_value = was_in_value;
+        self.write_str("}")?;
+        Ok(self)
+    }
+
     fn generic_delimiters(
         mut self,
         f: impl FnOnce(Self) -> Result<Self, Self::Error>,
@@ -1382,6 +1550,28 @@ impl<F: fmt::Write> 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::Const, Self::Error> {
+        let print = |mut this: Self| {
+            define_scoped_cx!(this);
+            if this.print_alloc_ids {
+                p!(write("{:?}", p));
+            } else {
+                p!(write("&_"));
+            }
+            Ok(this)
+        };
+        if print_ty {
+            self.typed_value(print, |this| this.print_type(ty), ": ")
+        } else {
+            print(self)
+        }
+    }
 }
 
 // HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`.
diff --git a/src/librustc_mir/interpret/intrinsics/type_name.rs b/src/librustc_mir/interpret/intrinsics/type_name.rs
index cd8bf7085d1..677dc697735 100644
--- a/src/librustc_mir/interpret/intrinsics/type_name.rs
+++ b/src/librustc_mir/interpret/intrinsics/type_name.rs
@@ -157,6 +157,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
         }
     }
 }
+
 impl PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> {
     fn region_should_not_be_omitted(&self, _region: ty::Region<'_>) -> bool {
         false
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 22b1a7b7137..75452b9395b 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -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,44 @@ 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>(
+            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.typed_value(
+                    |mut this| {
+                        this.write_str("{undef ")?;
+                        Ok(this)
+                    },
+                    |this| this.print_type(ty),
+                    " ",
+                ),
             }
-            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}"),
-        }
+        })
     }
 }
 
diff --git a/src/test/mir-opt/const-promotion-extern-static.rs b/src/test/mir-opt/const-promotion-extern-static.rs
index f6f7d091091..0d4a6d1bafd 100644
--- a/src/test/mir-opt/const-promotion-extern-static.rs
+++ b/src/test/mir-opt/const-promotion-extern-static.rs
@@ -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;
diff --git a/src/test/mir-opt/const_prop/discriminant.rs b/src/test/mir-opt/const_prop/discriminant.rs
index 667d21fc14e..636aa1af653 100644
--- a/src/test/mir-opt/const_prop/discriminant.rs
+++ b/src/test/mir-opt/const_prop/discriminant.rs
@@ -31,7 +31,7 @@ fn main() {
 // START rustc.main.ConstProp.after.mir
 //  bb0: {
 //      ...
-//      _3 = const Scalar(0x01) : std::option::Option<bool>;
+//      _3 = const {transmute(0x01): std::option::Option<bool>};
 //      _4 = const 1isize;
 //      switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1];
 //  }
diff --git a/src/test/mir-opt/const_prop/issue-66971.rs b/src/test/mir-opt/const_prop/issue-66971.rs
index 30c75303b3e..f332bb89509 100644
--- a/src/test/mir-opt/const_prop/issue-66971.rs
+++ b/src/test/mir-opt/const_prop/issue-66971.rs
@@ -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;
diff --git a/src/test/mir-opt/const_prop/read_immutable_static.rs b/src/test/mir-opt/const_prop/read_immutable_static.rs
index 693ef783985..d307cebd715 100644
--- a/src/test/mir-opt/const_prop/read_immutable_static.rs
+++ b/src/test/mir-opt/const_prop/read_immutable_static.rs
@@ -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);
 //      ...
diff --git a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs
index 6f03438ff72..e427fd55ad6 100644
--- a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs
+++ b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs
@@ -1,18 +1,18 @@
 // compile-flags: -C overflow-checks=no
 
-fn use_zst(_: ((), ())) { }
+fn use_zst(_: ((), ())) {}
 
 struct Temp {
-    x: u8
+    x: u8,
 }
 
-fn use_u8(_: u8) { }
+fn use_u8(_: u8) {}
 
 fn main() {
     let ((), ()) = ((), ());
     use_zst(((), ()));
 
-    use_u8((Temp { x : 40 }).x + 2);
+    use_u8((Temp { x: 40 }).x + 2);
 }
 
 // END RUST SOURCE
@@ -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 {transmute(()): ((), ())};
 //   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 {transmute(()): ((), ())}) -> bb1;
 // }
 // bb1: {
 //   StorageDead(_4);
 //   StorageLive(_8);
 //   StorageLive(_10);
 //   StorageLive(_11);
-//   _11 = const Scalar(0x28) : Temp;
+//   _11 = const {transmute(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 {transmute(()): ((), ())}) -> bb1;
 // }
 // bb1: {
 //   StorageDead(_1);
diff --git a/src/test/ui/const-generics/cannot-infer-const-args.stderr b/src/test/ui/const-generics/cannot-infer-const-args.stderr
index 8379cbd4908..c1d7022d56b 100644
--- a/src/test/ui/const-generics/cannot-infer-const-args.stderr
+++ b/src/test/ui/const-generics/cannot-infer-const-args.stderr
@@ -10,7 +10,7 @@ error[E0282]: type annotations needed
   --> $DIR/cannot-infer-const-args.rs:9:5
    |
 LL |     foo();
-   |     ^^^ cannot infer type for fn item `fn() -> usize {foo::<_: usize>}`
+   |     ^^^ cannot infer type for fn item `fn() -> usize {foo::<{_: usize}>}`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/const-generic-type_name.rs b/src/test/ui/const-generics/const-generic-type_name.rs
index 28586426b44..469843d6aae 100644
--- a/src/test/ui/const-generics/const-generic-type_name.rs
+++ b/src/test/ui/const-generics/const-generic-type_name.rs
@@ -7,5 +7,5 @@
 struct S<const N: usize>;
 
 fn main() {
-    assert_eq!(std::any::type_name::<S<3>>(), "const_generic_type_name::S<3usize>");
+    assert_eq!(std::any::type_name::<S<3>>(), "const_generic_type_name::S<3>");
 }
diff --git a/src/test/ui/const-generics/fn-const-param-infer.stderr b/src/test/ui/const-generics/fn-const-param-infer.stderr
index 44eab8baa40..05d2dff8e98 100644
--- a/src/test/ui/const-generics/fn-const-param-infer.stderr
+++ b/src/test/ui/const-generics/fn-const-param-infer.stderr
@@ -10,12 +10,12 @@ error[E0308]: mismatched types
   --> $DIR/fn-const-param-infer.rs:16:31
    |
 LL |     let _: Checked<not_one> = Checked::<not_two>;
-   |            ----------------   ^^^^^^^^^^^^^^^^^^ expected `not_one`, found `not_two`
+   |            ----------------   ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}`
    |            |
    |            expected due to this
    |
-   = note: expected struct `Checked<not_one>`
-              found struct `Checked<not_two>`
+   = note: expected struct `Checked<{not_one as fn(usize) -> bool}>`
+              found struct `Checked<{not_two as fn(usize) -> bool}>`
 
 error[E0308]: mismatched types
   --> $DIR/fn-const-param-infer.rs:20:24
@@ -36,12 +36,12 @@ error[E0308]: mismatched types
   --> $DIR/fn-const-param-infer.rs:25:40
    |
 LL |     let _: Checked<{generic::<u32>}> = Checked::<{generic::<u16>}>;
-   |            -------------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `generic::<u32>`, found `generic::<u16>`
+   |            -------------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic::<u32> as fn(usize) -> bool}`, found `{generic::<u16> as fn(usize) -> bool}`
    |            |
    |            expected due to this
    |
-   = note: expected struct `Checked<generic::<u32>>`
-              found struct `Checked<generic::<u16>>`
+   = note: expected struct `Checked<{generic::<u32> as fn(usize) -> bool}>`
+              found struct `Checked<{generic::<u16> as fn(usize) -> bool}>`
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/const-generics/raw-ptr-const-param.rs b/src/test/ui/const-generics/raw-ptr-const-param.rs
index f69c37fbb8f..f0349f46962 100644
--- a/src/test/ui/const-generics/raw-ptr-const-param.rs
+++ b/src/test/ui/const-generics/raw-ptr-const-param.rs
@@ -4,6 +4,6 @@
 struct Const<const P: *const u32>;
 
 fn main() {
-    let _: Const<{15 as *const _}> = Const::<{10 as *const _}>; //~ mismatched types
-    let _: Const<{10 as *const _}> = Const::<{10 as *const _}>;
+    let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>; //~ mismatched types
+    let _: Const<{ 10 as *const _ }> = Const::<{ 10 as *const _ }>;
 }
diff --git a/src/test/ui/const-generics/raw-ptr-const-param.stderr b/src/test/ui/const-generics/raw-ptr-const-param.stderr
index 9cd39b61dc9..d9794f60a19 100644
--- a/src/test/ui/const-generics/raw-ptr-const-param.stderr
+++ b/src/test/ui/const-generics/raw-ptr-const-param.stderr
@@ -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:7:40
    |
-LL |     let _: Const<{15 as *const _}> = Const::<{10 as *const _}>;
-   |            -----------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{pointer}`, found `{pointer}`
+LL |     let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>;
+   |            -------------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}`
    |            |
    |            expected due to this
    |
-   = note: expected struct `Const<{pointer}>`
-              found struct `Const<{pointer}>`
+   = note: expected struct `Const<{0xf as *const u32}>`
+              found struct `Const<{0xa as *const u32}>`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/consts/offset_from_ub.stderr b/src/test/ui/consts/offset_from_ub.stderr
index 24da983cf08..bbe3344b412 100644
--- a/src/test/ui/consts/offset_from_ub.stderr
+++ b/src/test/ui/consts/offset_from_ub.stderr
@@ -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