about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/infer/error_reporting/mod.rs54
-rw-r--r--src/librustc/infer/error_reporting/need_type_info.rs4
-rw-r--r--src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs10
-rw-r--r--src/librustc/mir/mod.rs9
-rw-r--r--src/librustc/ty/instance.rs9
-rw-r--r--src/librustc/ty/print/mod.rs143
-rw-r--r--src/librustc/ty/print/pretty.rs1256
-rw-r--r--src/librustc/ty/structural_impls.rs30
-rw-r--r--src/librustc_codegen_utils/symbol_names.rs177
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs8
-rw-r--r--src/librustdoc/clean/mod.rs57
11 files changed, 850 insertions, 907 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 10c34aaf5b7..52b42881038 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -445,14 +445,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         sp: Span,
     ) {
         use hir::def_id::CrateNum;
-        use ty::print::{PrintCx, Printer};
+        use ty::print::Printer;
         use ty::subst::Kind;
 
-        struct AbsolutePathPrinter;
+        struct AbsolutePathPrinter<'a, 'gcx, 'tcx> {
+            tcx: TyCtxt<'a, 'gcx, 'tcx>,
+        }
 
         struct NonTrivialPath;
 
-        impl Printer for AbsolutePathPrinter {
+        impl<'gcx, 'tcx> Printer<'gcx, 'tcx> for AbsolutePathPrinter<'_, 'gcx, 'tcx> {
             type Error = NonTrivialPath;
 
             type Path = Vec<String>;
@@ -460,67 +462,65 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             type Type = !;
             type DynExistential = !;
 
+            fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx> {
+                self.tcx
+            }
+
             fn print_region(
-                self: PrintCx<'_, '_, '_, Self>,
+                self,
                 _region: ty::Region<'_>,
             ) -> Result<Self::Region, Self::Error> {
                 Err(NonTrivialPath)
             }
 
-            fn print_type<'tcx>(
-                self: PrintCx<'_, '_, 'tcx, Self>,
+            fn print_type(
+                self,
                 _ty: Ty<'tcx>,
             ) -> Result<Self::Type, Self::Error> {
                 Err(NonTrivialPath)
             }
 
-            fn print_dyn_existential<'tcx>(
-                self: PrintCx<'_, '_, 'tcx, Self>,
+            fn print_dyn_existential(
+                self,
                 _predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
             ) -> Result<Self::DynExistential, Self::Error> {
                 Err(NonTrivialPath)
             }
 
             fn path_crate(
-                self: PrintCx<'_, '_, '_, Self>,
+                self,
                 cnum: CrateNum,
             ) -> Result<Self::Path, Self::Error> {
                 Ok(vec![self.tcx.original_crate_name(cnum).to_string()])
             }
-            fn path_qualified<'tcx>(
-                self: PrintCx<'_, '_, 'tcx, Self>,
+            fn path_qualified(
+                self,
                 _self_ty: Ty<'tcx>,
                 _trait_ref: Option<ty::TraitRef<'tcx>>,
             ) -> Result<Self::Path, Self::Error> {
                 Err(NonTrivialPath)
             }
 
-            fn path_append_impl<'gcx, 'tcx>(
-                self: PrintCx<'_, 'gcx, 'tcx, Self>,
-                _print_prefix: impl FnOnce(
-                    PrintCx<'_, 'gcx, 'tcx, Self>,
-                ) -> Result<Self::Path, Self::Error>,
+            fn path_append_impl(
+                self,
+                _print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
                 _self_ty: Ty<'tcx>,
                 _trait_ref: Option<ty::TraitRef<'tcx>>,
             ) -> Result<Self::Path, Self::Error> {
                 Err(NonTrivialPath)
             }
-            fn path_append<'gcx, 'tcx>(
-                self: PrintCx<'_, 'gcx, 'tcx, Self>,
-                print_prefix: impl FnOnce(
-                    PrintCx<'_, 'gcx, 'tcx, Self>,
-                ) -> Result<Self::Path, Self::Error>,
+            fn path_append(
+                self,
+                print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
                 text: &str,
             ) -> Result<Self::Path, Self::Error> {
                 let mut path = print_prefix(self)?;
                 path.push(text.to_string());
                 Ok(path)
             }
-            fn path_generic_args<'gcx, 'tcx>(
-                self: PrintCx<'_, 'gcx, 'tcx, Self>,
-                print_prefix: impl FnOnce(
-                    PrintCx<'_, 'gcx, 'tcx, Self>,
-                ) -> Result<Self::Path, Self::Error>,
+            fn path_generic_args(
+                self,
+                print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
                 _args: &[Kind<'tcx>],
             ) -> Result<Self::Path, Self::Error> {
                 print_prefix(self)
@@ -532,7 +532,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             // module we could have false positives
             if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
                 let abs_path = |def_id| {
-                    PrintCx::new(self.tcx, AbsolutePathPrinter)
+                    AbsolutePathPrinter { tcx: self.tcx }
                         .print_def_path(def_id, None)
                 };
 
diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs
index f649309004b..0a83b839201 100644
--- a/src/librustc/infer/error_reporting/need_type_info.rs
+++ b/src/librustc/infer/error_reporting/need_type_info.rs
@@ -80,11 +80,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
 
         let mut s = String::new();
-        let mut printer = ty::print::FmtPrinter::new(&mut s, Namespace::TypeNS);
+        let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
         if let Some(highlight) = highlight {
             printer.region_highlight_mode = highlight;
         }
-        let _ = ty.print(ty::print::PrintCx::new(self.tcx, printer));
+        let _ = ty.print(printer);
         s
     }
 
diff --git a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs
index fd01ed85ef7..e708454b5b6 100644
--- a/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs
+++ b/src/librustc/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -337,17 +337,17 @@ impl NiceRegionError<'me, 'gcx, 'tcx> {
             }
         }
 
-        impl<'tcx, T> fmt::Display for Highlighted<'_, '_, 'tcx, T>
-            where T: for<'a, 'b> Print<'tcx,
-                FmtPrinter<&'a mut fmt::Formatter<'b>>,
+        impl<'a, 'gcx, 'tcx, T> fmt::Display for Highlighted<'a, 'gcx, 'tcx, T>
+            where T: for<'b, 'c> Print<'gcx, 'tcx,
+                FmtPrinter<'a, 'gcx, 'tcx, &'b mut fmt::Formatter<'c>>,
                 Error = fmt::Error,
             >,
         {
             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                let mut printer = ty::print::FmtPrinter::new(f, Namespace::TypeNS);
+                let mut printer = ty::print::FmtPrinter::new(self.tcx, f, Namespace::TypeNS);
                 printer.region_highlight_mode = self.highlight;
 
-                self.value.print(ty::print::PrintCx::new(self.tcx, printer))?;
+                self.value.print(printer)?;
                 Ok(())
             }
         }
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index d1574bb322d..3218f8ea5de 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -34,7 +34,7 @@ use crate::ty::{
     self, AdtDef, CanonicalUserTypeAnnotations, ClosureSubsts, GeneratorSubsts, Region, Ty, TyCtxt,
     UserTypeAnnotationIndex,
 };
-use crate::ty::print::{FmtPrinter, Printer, PrintCx};
+use crate::ty::print::{FmtPrinter, Printer};
 
 pub use crate::mir::interpret::AssertMessage;
 
@@ -2407,9 +2407,10 @@ impl<'tcx> Debug for Rvalue<'tcx> {
                         let variant_def = &adt_def.variants[variant];
 
                         let f = &mut *fmt;
-                        PrintCx::with_tls_tcx(FmtPrinter::new(f, Namespace::ValueNS), |cx| {
-                            let substs = cx.tcx.lift(&substs).expect("could not lift for printing");
-                            cx.print_def_path(variant_def.did, Some(substs))?;
+                        ty::tls::with(|tcx| {
+                            let substs = tcx.lift(&substs).expect("could not lift for printing");
+                            FmtPrinter::new(tcx, f, Namespace::ValueNS)
+                                .print_def_path(variant_def.did, Some(substs))?;
                             Ok(())
                         })?;
 
diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs
index 66c99a7c4fc..89d956c8bfa 100644
--- a/src/librustc/ty/instance.rs
+++ b/src/librustc/ty/instance.rs
@@ -2,7 +2,7 @@ use crate::hir::Unsafety;
 use crate::hir::def::Namespace;
 use crate::hir::def_id::DefId;
 use crate::ty::{self, Ty, PolyFnSig, TypeFoldable, SubstsRef, TyCtxt};
-use crate::ty::print::{FmtPrinter, Printer, PrintCx};
+use crate::ty::print::{FmtPrinter, Printer};
 use crate::traits;
 use rustc_target::spec::abi::Abi;
 use rustc_macros::HashStable;
@@ -176,9 +176,10 @@ impl<'tcx> InstanceDef<'tcx> {
 
 impl<'tcx> fmt::Display for Instance<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        PrintCx::with_tls_tcx(FmtPrinter::new(&mut *f, Namespace::ValueNS), |cx| {
-            let substs = cx.tcx.lift(&self.substs).expect("could not lift for printing");
-            cx.print_def_path(self.def_id(), Some(substs))?;
+        ty::tls::with(|tcx| {
+            let substs = tcx.lift(&self.substs).expect("could not lift for printing");
+            FmtPrinter::new(tcx, &mut *f, Namespace::ValueNS)
+                .print_def_path(self.def_id(), Some(substs))?;
             Ok(())
         })?;
 
diff --git a/src/librustc/ty/print/mod.rs b/src/librustc/ty/print/mod.rs
index 4e81533589e..ad17a8114ca 100644
--- a/src/librustc/ty/print/mod.rs
+++ b/src/librustc/ty/print/mod.rs
@@ -5,59 +5,18 @@ use crate::ty::subst::{Kind, Subst, SubstsRef};
 
 use rustc_data_structures::fx::FxHashSet;
 
-use std::ops::{Deref, DerefMut};
-
 // `pretty` is a separate module only for organization.
 mod pretty;
 pub use self::pretty::*;
 
-pub struct PrintCx<'a, 'gcx, 'tcx, P> {
-    pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
-    inner: P,
-}
-
-impl<P> Deref for PrintCx<'_, '_, '_, P> {
-    type Target = P;
-    fn deref(&self) -> &P {
-        &self.inner
-    }
-}
-
-impl<P> DerefMut for PrintCx<'_, '_, '_, P> {
-    fn deref_mut(&mut self) -> &mut P {
-        &mut self.inner
-    }
-}
-
-impl<'a, 'gcx, 'tcx, P> PrintCx<'a, 'gcx, 'tcx, P> {
-    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, inner: P) -> Self {
-        PrintCx {
-            tcx,
-            inner,
-        }
-    }
-
-    pub fn with_tls_tcx<R>(inner: P, f: impl FnOnce(PrintCx<'_, '_, '_, P>) -> R) -> R {
-        ty::tls::with(|tcx| f(PrintCx::new(tcx, inner)))
-    }
-
-    pub fn into_inner(self) -> P {
-        self.inner
-    }
-
-    pub fn ok<E>(self) -> Result<P, E> {
-        Ok(self.into_inner())
-    }
-}
-
-pub trait Print<'tcx, P> {
+pub trait Print<'gcx, 'tcx, P> {
     type Output;
     type Error;
 
-    fn print(&self, cx: PrintCx<'_, '_, 'tcx, P>) -> Result<Self::Output, Self::Error>;
+    fn print(&self, cx: P) -> Result<Self::Output, Self::Error>;
 }
 
-pub trait Printer: Sized {
+pub trait Printer<'gcx: 'tcx, 'tcx>: Sized {
     type Error;
 
     type Path;
@@ -65,15 +24,17 @@ pub trait Printer: Sized {
     type Type;
     type DynExistential;
 
+    fn tcx(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>;
+
     fn print_def_path(
-        self: PrintCx<'_, '_, 'tcx, Self>,
+        self,
         def_id: DefId,
         substs: Option<SubstsRef<'tcx>>,
     ) -> Result<Self::Path, Self::Error> {
         self.default_print_def_path(def_id, substs)
     }
     fn print_impl_path(
-        self: PrintCx<'_, '_, 'tcx, Self>,
+        self,
         impl_def_id: DefId,
         substs: Option<SubstsRef<'tcx>>,
         self_ty: Ty<'tcx>,
@@ -83,62 +44,56 @@ pub trait Printer: Sized {
     }
 
     fn print_region(
-        self: PrintCx<'_, '_, '_, Self>,
+        self,
         region: ty::Region<'_>,
     ) -> Result<Self::Region, Self::Error>;
 
     fn print_type(
-        self: PrintCx<'_, '_, 'tcx, Self>,
+        self,
         ty: Ty<'tcx>,
     ) -> Result<Self::Type, Self::Error>;
 
     fn print_dyn_existential(
-        self: PrintCx<'_, '_, 'tcx, Self>,
+        self,
         predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
     ) -> Result<Self::DynExistential, Self::Error>;
 
     fn path_crate(
-        self: PrintCx<'_, '_, '_, Self>,
+        self,
         cnum: CrateNum,
     ) -> Result<Self::Path, Self::Error>;
     fn path_qualified(
-        self: PrintCx<'_, '_, 'tcx, Self>,
+        self,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<Self::Path, Self::Error>;
 
-    fn path_append_impl<'gcx, 'tcx>(
-        self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, Self>,
-        ) -> Result<Self::Path, Self::Error>,
+    fn path_append_impl(
+        self,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<Self::Path, Self::Error>;
-    fn path_append<'gcx, 'tcx>(
-        self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, Self>,
-        ) -> Result<Self::Path, Self::Error>,
+    fn path_append(
+        self,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
         text: &str,
     ) -> Result<Self::Path, Self::Error>;
-    fn path_generic_args<'gcx, 'tcx>(
-        self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, Self>,
-        ) -> Result<Self::Path, Self::Error>,
+    fn path_generic_args(
+        self,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
         args: &[Kind<'tcx>],
     ) -> Result<Self::Path, Self::Error>;
-}
 
-impl<P: Printer> PrintCx<'_, 'gcx, 'tcx, P> {
-    pub fn default_print_def_path(
+    // Defaults (should not be overriden):
+
+    fn default_print_def_path(
         self,
         def_id: DefId,
         substs: Option<SubstsRef<'tcx>>,
-    ) -> Result<P::Path, P::Error> {
+    ) -> Result<Self::Path, Self::Error> {
         debug!("default_print_def_path: def_id={:?}, substs={:?}", def_id, substs);
-        let key = self.tcx.def_key(def_id);
+        let key = self.tcx().def_key(def_id);
         debug!("default_print_def_path: key={:?}", key);
 
         match key.disambiguated_data.data {
@@ -148,29 +103,29 @@ impl<P: Printer> PrintCx<'_, 'gcx, 'tcx, P> {
             }
 
             DefPathData::Impl => {
-                let mut self_ty = self.tcx.type_of(def_id);
+                let mut self_ty = self.tcx().type_of(def_id);
                 if let Some(substs) = substs {
-                    self_ty = self_ty.subst(self.tcx, substs);
+                    self_ty = self_ty.subst(self.tcx(), substs);
                 }
 
-                let mut impl_trait_ref = self.tcx.impl_trait_ref(def_id);
+                let mut impl_trait_ref = self.tcx().impl_trait_ref(def_id);
                 if let Some(substs) = substs {
-                    impl_trait_ref = impl_trait_ref.subst(self.tcx, substs);
+                    impl_trait_ref = impl_trait_ref.subst(self.tcx(), substs);
                 }
                 self.print_impl_path(def_id, substs, self_ty, impl_trait_ref)
             }
 
             _ => {
-                let generics = substs.map(|_| self.tcx.generics_of(def_id));
+                let generics = substs.map(|_| self.tcx().generics_of(def_id));
                 let generics_parent = generics.as_ref().and_then(|g| g.parent);
                 let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id };
-                let print_parent_path = |cx: PrintCx<'_, 'gcx, 'tcx, P>| {
+                let print_parent_path = |cx: Self| {
                     if let Some(generics_parent_def_id) = generics_parent {
                         assert_eq!(parent_def_id, generics_parent_def_id);
 
                         // FIXME(eddyb) try to move this into the parent's printing
                         // logic, instead of doing it when printing the child.
-                        let parent_generics = cx.tcx.generics_of(parent_def_id);
+                        let parent_generics = cx.tcx().generics_of(parent_def_id);
                         let parent_has_own_self =
                             parent_generics.has_self && parent_generics.parent_count == 0;
                         if let (Some(substs), true) = (substs, parent_has_own_self) {
@@ -183,7 +138,7 @@ impl<P: Printer> PrintCx<'_, 'gcx, 'tcx, P> {
                         cx.print_def_path(parent_def_id, None)
                     }
                 };
-                let print_path = |cx: PrintCx<'_, 'gcx, 'tcx, P>| {
+                let print_path = |cx: Self| {
                     match key.disambiguated_data.data {
                         // Skip `::{{constructor}}` on tuple/unit structs.
                         DefPathData::StructCtor => print_parent_path(cx),
@@ -207,7 +162,7 @@ impl<P: Printer> PrintCx<'_, 'gcx, 'tcx, P> {
         }
     }
 
-    pub fn generic_args_to_print(
+    fn generic_args_to_print(
         &self,
         generics: &'tcx ty::Generics,
         substs: SubstsRef<'tcx>,
@@ -225,7 +180,7 @@ impl<P: Printer> PrintCx<'_, 'gcx, 'tcx, P> {
                 ty::GenericParamDefKind::Lifetime => false,
                 ty::GenericParamDefKind::Type { has_default, .. } => {
                     has_default && substs[param.index as usize] == Kind::from(
-                        self.tcx.type_of(param.def_id).subst(self.tcx, substs)
+                        self.tcx().type_of(param.def_id).subst(self.tcx(), substs)
                     )
                 }
                 ty::GenericParamDefKind::Const => false, // FIXME(const_generics:defaults)
@@ -241,7 +196,7 @@ impl<P: Printer> PrintCx<'_, 'gcx, 'tcx, P> {
         _substs: Option<SubstsRef<'tcx>>,
         self_ty: Ty<'tcx>,
         impl_trait_ref: Option<ty::TraitRef<'tcx>>,
-    ) -> Result<P::Path, P::Error> {
+    ) -> Result<Self::Path, Self::Error> {
         debug!("default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}",
                impl_def_id, self_ty, impl_trait_ref);
 
@@ -250,14 +205,14 @@ impl<P: Printer> PrintCx<'_, 'gcx, 'tcx, P> {
         // users may find it useful. Currently, we omit the parent if
         // the impl is either in the same module as the self-type or
         // as the trait.
-        let parent_def_id = self.tcx.parent(impl_def_id).unwrap();
+        let parent_def_id = self.tcx().parent(impl_def_id).unwrap();
         let in_self_mod = match characteristic_def_id_of_type(self_ty) {
             None => false,
-            Some(ty_def_id) => self.tcx.parent(ty_def_id) == Some(parent_def_id),
+            Some(ty_def_id) => self.tcx().parent(ty_def_id) == Some(parent_def_id),
         };
         let in_trait_mod = match impl_trait_ref {
             None => false,
-            Some(trait_ref) => self.tcx.parent(trait_ref.def_id) == Some(parent_def_id),
+            Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == Some(parent_def_id),
         };
 
         if !in_self_mod && !in_trait_mod {
@@ -325,34 +280,36 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
     }
 }
 
-impl<P: Printer> Print<'tcx, P> for ty::RegionKind {
+impl<'gcx: 'tcx, 'tcx, P: Printer<'gcx, 'tcx>> Print<'gcx, 'tcx, P> for ty::RegionKind {
     type Output = P::Region;
     type Error = P::Error;
-    fn print(&self, cx: PrintCx<'_, '_, 'tcx, P>) -> Result<Self::Output, Self::Error> {
+    fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
         cx.print_region(self)
     }
 }
 
-impl<P: Printer> Print<'tcx, P> for ty::Region<'_> {
+impl<'gcx: 'tcx, 'tcx, P: Printer<'gcx, 'tcx>> Print<'gcx, 'tcx, P> for ty::Region<'_> {
     type Output = P::Region;
     type Error = P::Error;
-    fn print(&self, cx: PrintCx<'_, '_, 'tcx, P>) -> Result<Self::Output, Self::Error> {
+    fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
         cx.print_region(self)
     }
 }
 
-impl<P: Printer> Print<'tcx, P> for Ty<'tcx> {
+impl<'gcx: 'tcx, 'tcx, P: Printer<'gcx, 'tcx>> Print<'gcx, 'tcx, P> for Ty<'tcx> {
     type Output = P::Type;
     type Error = P::Error;
-    fn print(&self, cx: PrintCx<'_, '_, 'tcx, P>) -> Result<Self::Output, Self::Error> {
+    fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
         cx.print_type(self)
     }
 }
 
-impl<P: Printer> Print<'tcx, P> for &'tcx ty::List<ty::ExistentialPredicate<'tcx>> {
+impl<'gcx: 'tcx, 'tcx, P: Printer<'gcx, 'tcx>> Print<'gcx, 'tcx, P>
+    for &'tcx ty::List<ty::ExistentialPredicate<'tcx>>
+{
     type Output = P::DynExistential;
     type Error = P::Error;
-    fn print(&self, cx: PrintCx<'_, '_, 'tcx, P>) -> Result<Self::Output, Self::Error> {
+    fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
         cx.print_dyn_existential(self)
     }
 }
diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs
index da01fd11a3f..fff4ca822d2 100644
--- a/src/librustc/ty/print/pretty.rs
+++ b/src/librustc/ty/print/pretty.rs
@@ -19,17 +19,12 @@ use std::ops::{Deref, DerefMut};
 // `pretty` is a separate module only for organization.
 use super::*;
 
-macro_rules! nest {
-    ($e:expr) => {
-        scoped_cx!() = PrintCx::new(scoped_cx!().tcx, $e?)
-    }
-}
 macro_rules! print_inner {
     (write ($($data:expr),+)) => {
         write!(scoped_cx!(), $($data),+)?
     };
     ($kind:ident ($data:expr)) => {
-        nest!($data.$kind(scoped_cx!()))
+        scoped_cx!() = $data.$kind(scoped_cx!())?
     };
 }
 macro_rules! p {
@@ -170,8 +165,8 @@ impl RegionHighlightMode {
 }
 
 /// Trait for printers that pretty-print using `fmt::Write` to the printer.
-pub trait PrettyPrinter:
-    Printer<
+pub trait PrettyPrinter<'gcx: 'tcx, 'tcx>:
+    Printer<'gcx, 'tcx,
         Error = fmt::Error,
         Path = Self,
         Region = Self,
@@ -182,7 +177,7 @@ pub trait PrettyPrinter:
 {
     /// Like `print_def_path` but for value paths.
     fn print_value_path(
-        self: PrintCx<'_, '_, 'tcx, Self>,
+        self,
         def_id: DefId,
         substs: Option<SubstsRef<'tcx>>,
     ) -> Result<Self::Path, Self::Error> {
@@ -190,93 +185,54 @@ pub trait PrettyPrinter:
     }
 
     fn in_binder<T>(
-        self: PrintCx<'_, '_, 'tcx, Self>,
+        self,
         value: &ty::Binder<T>,
     ) -> Result<Self, Self::Error>
-        where T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>
+        where T: Print<'gcx, 'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>
     {
         value.skip_binder().print(self)
     }
 
     /// Print comma-separated elements.
     fn comma_sep<T>(
-        mut self: PrintCx<'_, '_, 'tcx, Self>,
+        mut self,
         mut elems: impl Iterator<Item = T>,
     ) -> Result<Self, Self::Error>
-        where T: Print<'tcx, Self, Output = Self, Error = Self::Error>
+        where T: Print<'gcx, 'tcx, Self, Output = Self, Error = Self::Error>
     {
-        define_scoped_cx!(self);
-
         if let Some(first) = elems.next() {
-            nest!(first.print(self));
+            self = first.print(self)?;
             for elem in elems {
                 self.write_str(", ")?;
-                nest!(elem.print(self));
+                self = elem.print(self)?;
             }
         }
-        self.ok()
+        Ok(self)
     }
 
     /// Print `<...>` around what `f` prints.
-    fn generic_delimiters<'gcx, 'tcx>(
-        self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        f: impl FnOnce(PrintCx<'_, 'gcx, 'tcx, Self>) -> Result<Self, Self::Error>,
+    fn generic_delimiters(
+        self,
+        f: impl FnOnce(Self) -> Result<Self, Self::Error>,
     ) -> Result<Self, Self::Error>;
 
     /// Return `true` if the region should be printed in
     /// optional positions, e.g. `&'a T` or `dyn Tr + 'b`.
     /// This is typically the case for all non-`'_` regions.
     fn region_should_not_be_omitted(
-        self: &PrintCx<'_, '_, '_, Self>,
+        &self,
         region: ty::Region<'_>,
     ) -> bool;
-}
-
-impl<P: PrettyPrinter> fmt::Write for PrintCx<'_, '_, '_, P> {
-    fn write_str(&mut self, s: &str) -> fmt::Result {
-        (**self).write_str(s)
-    }
-}
 
-impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
-    // HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always
-    // (but also some things just print a `DefId` generally so maybe we need this?)
-    fn guess_def_namespace(self, def_id: DefId) -> Namespace {
-        match self.def_key(def_id).disambiguated_data.data {
-            DefPathData::ValueNs(..) |
-            DefPathData::EnumVariant(..) |
-            DefPathData::Field(..) |
-            DefPathData::AnonConst |
-            DefPathData::ConstParam(..) |
-            DefPathData::ClosureExpr |
-            DefPathData::StructCtor => Namespace::ValueNS,
+    // Defaults (should not be overriden):
 
-            DefPathData::MacroDef(..) => Namespace::MacroNS,
-
-            _ => Namespace::TypeNS,
-        }
-    }
-
-    /// Returns a string identifying this `DefId. This string is
-    /// suitable for user output.
-    pub fn def_path_str(self, def_id: DefId) -> String {
-        let ns = self.guess_def_namespace(def_id);
-        debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns);
-        let mut s = String::new();
-        let _ = PrintCx::new(self, FmtPrinter::new(&mut s, ns))
-            .print_def_path(def_id, None);
-        s
-    }
-}
-
-impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
     /// If possible, this returns a global path resolving to `def_id` that is visible
     /// from at least one local module and returns true. If the crate defining `def_id` is
     /// declared with an `extern crate`, the path is guaranteed to use the `extern crate`.
     fn try_print_visible_def_path(
         mut self,
         def_id: DefId,
-    ) -> Result<(P, bool), P::Error> {
+    ) -> Result<(Self, bool), Self::Error> {
         define_scoped_cx!(self);
 
         debug!("try_print_visible_def_path: def_id={:?}", def_id);
@@ -300,7 +256,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             // 2. for an extern inferred from a path or an indirect crate,
             //    where there is no explicit `extern crate`, we just prepend
             //    the crate name.
-            match *self.tcx.extern_crate(def_id) {
+            match *self.tcx().extern_crate(def_id) {
                 Some(ExternCrate {
                     src: ExternCrateSource::Extern(def_id),
                     direct: true,
@@ -322,12 +278,12 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
         }
 
         if def_id.is_local() {
-            return self.ok().map(|path| (path, false));
+            return Ok((self, false));
         }
 
-        let visible_parent_map = self.tcx.visible_parent_map(LOCAL_CRATE);
+        let visible_parent_map = self.tcx().visible_parent_map(LOCAL_CRATE);
 
-        let mut cur_def_key = self.tcx.def_key(def_id);
+        let mut cur_def_key = self.tcx().def_key(def_id);
         debug!("try_print_visible_def_path: cur_def_key={:?}", cur_def_key);
 
         // For a UnitStruct or TupleStruct we want the name of its parent rather than <unnamed>.
@@ -337,27 +293,22 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                 index: cur_def_key.parent.expect("DefPathData::StructCtor missing a parent"),
             };
 
-            cur_def_key = self.tcx.def_key(parent);
+            cur_def_key = self.tcx().def_key(parent);
         }
 
         let visible_parent = match visible_parent_map.get(&def_id).cloned() {
             Some(parent) => parent,
-            None => return self.ok().map(|path| (path, false)),
-        };
-        // HACK(eddyb) this uses `nest` to avoid knowing ahead of time whether
-        // the entire path will succeed or not. To support printers that do not
-        // implement `PrettyPrinter`, a `Vec` or linked list on the stack would
-        // need to be built, before starting to print anything.
-        let prefix_success;
-        nest!({
-            let (path, success) = self.try_print_visible_def_path(visible_parent)?;
-            prefix_success = success;
-            Ok(path)
-        });
-        if !prefix_success {
-            return self.ok().map(|path| (path, false));
+            None => return Ok((self, false)),
         };
-        let actual_parent = self.tcx.parent(def_id);
+        // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid
+        // knowing ahead of time whether the entire path will succeed or not.
+        // To support printers that do not implement `PrettyPrinter`, a `Vec` or
+        // linked list on the stack would need to be built, before any printing.
+        match self.try_print_visible_def_path(visible_parent)? {
+            (cx, false) => return Ok((cx, false)),
+            (cx, true) => self = cx,
+        }
+        let actual_parent = self.tcx().parent(def_id);
         debug!(
             "try_print_visible_def_path: visible_parent={:?} actual_parent={:?}",
             visible_parent, actual_parent,
@@ -403,7 +354,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             // have access to the re-exported name.
             DefPathData::Module(actual_name) |
             DefPathData::TypeNs(actual_name) if Some(visible_parent) != actual_parent => {
-                self.tcx.item_children(visible_parent)
+                self.tcx().item_children(visible_parent)
                     .iter()
                     .find(|child| child.def.def_id() == def_id)
                     .map(|child| child.ident.as_str())
@@ -413,7 +364,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                 data.get_opt_name().map(|n| n.as_str()).unwrap_or_else(|| {
                     // Re-exported `extern crate` (#43189).
                     if let DefPathData::CrateRoot = data {
-                        self.tcx.original_crate_name(def_id.krate).as_str()
+                        self.tcx().original_crate_name(def_id.krate).as_str()
                     } else {
                         Symbol::intern("<unnamed>").as_str()
                     }
@@ -421,14 +372,14 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             },
         };
         debug!("try_print_visible_def_path: symbol={:?}", symbol);
-        Ok((self.path_append(|cx| cx.ok(), &symbol)?, true))
+        Ok((self.path_append(Ok, &symbol)?, true))
     }
 
-    pub fn pretty_path_qualified(
+    fn pretty_path_qualified(
         self,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
-    ) -> Result<P::Path, P::Error> {
+    ) -> Result<Self::Path, Self::Error> {
         if trait_ref.is_none() {
             // Inherent impls. Try to print `Foo::bar` for an inherent
             // impl on `Foo`, but fallback to `<Foo>::bar` if self-type is
@@ -451,19 +402,17 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             if let Some(trait_ref) = trait_ref {
                 p!(write(" as "), print(trait_ref));
             }
-            cx.ok()
+            Ok(cx)
         })
     }
 
-    pub fn pretty_path_append_impl(
+    fn pretty_path_append_impl(
         mut self,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, P>,
-        ) -> Result<P::Path, P::Error>,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
-    ) -> Result<P::Path, P::Error> {
-        self = PrintCx::new(self.tcx, print_prefix(self)?);
+    ) -> Result<Self::Path, Self::Error> {
+        self = print_prefix(self)?;
 
         self.generic_delimiters(|mut cx| {
             define_scoped_cx!(cx);
@@ -474,438 +423,14 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             }
             p!(print(self_ty));
 
-            cx.ok()
+            Ok(cx)
         })
     }
-}
-
-// HACK(eddyb) boxed to avoid moving around a large struct by-value.
-pub struct FmtPrinter<F>(Box<FmtPrinterData<F>>);
-
-pub struct FmtPrinterData<F> {
-    fmt: F,
-
-    empty_path: bool,
-    in_value: bool,
-
-    used_region_names: FxHashSet<InternedString>,
-    region_index: usize,
-    binder_depth: usize,
-
-    pub region_highlight_mode: RegionHighlightMode,
-}
-
-impl<F> Deref for FmtPrinter<F> {
-    type Target = FmtPrinterData<F>;
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl<F> DerefMut for FmtPrinter<F> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
-    }
-}
-
-impl<F> FmtPrinter<F> {
-    pub fn new(fmt: F, ns: Namespace) -> Self {
-        FmtPrinter(Box::new(FmtPrinterData {
-            fmt,
-            empty_path: false,
-            in_value: ns == Namespace::ValueNS,
-            used_region_names: Default::default(),
-            region_index: 0,
-            binder_depth: 0,
-            region_highlight_mode: RegionHighlightMode::default(),
-        }))
-    }
-}
-
-impl<F: fmt::Write> fmt::Write for FmtPrinter<F> {
-    fn write_str(&mut self, s: &str) -> fmt::Result {
-        self.fmt.write_str(s)
-    }
-}
-
-impl<F: fmt::Write> Printer for FmtPrinter<F> {
-    type Error = fmt::Error;
-
-    type Path = Self;
-    type Region = Self;
-    type Type = Self;
-    type DynExistential = Self;
-
-    fn print_def_path(
-        mut self: PrintCx<'_, '_, 'tcx, Self>,
-        def_id: DefId,
-        substs: Option<SubstsRef<'tcx>>,
-    ) -> Result<Self::Path, Self::Error> {
-        define_scoped_cx!(self);
-
-        // FIXME(eddyb) avoid querying `tcx.generics_of` and `tcx.def_key`
-        // both here and in `default_print_def_path`.
-        let generics = substs.map(|_| self.tcx.generics_of(def_id));
-        if generics.as_ref().and_then(|g| g.parent).is_none() {
-            let visible_path_success;
-            nest!({
-                let (path, success) = self.try_print_visible_def_path(def_id)?;
-                visible_path_success = success;
-                Ok(path)
-            });
-            if visible_path_success {
-                return if let (Some(generics), Some(substs)) = (generics, substs) {
-                    let args = self.generic_args_to_print(generics, substs);
-                    self.path_generic_args(|cx| cx.ok(), args)
-                } else {
-                    self.ok()
-                };
-            }
-        }
-
-        let key = self.tcx.def_key(def_id);
-        if let DefPathData::Impl = key.disambiguated_data.data {
-            // Always use types for non-local impls, where types are always
-            // available, and filename/line-number is mostly uninteresting.
-            let use_types =
-                !def_id.is_local() || {
-                    // Otherwise, use filename/line-number if forced.
-                    let force_no_types = FORCE_IMPL_FILENAME_LINE.with(|f| f.get());
-                    !force_no_types
-                };
-
-            if !use_types {
-                // If no type info is available, fall back to
-                // pretty printing some span information. This should
-                // only occur very early in the compiler pipeline.
-                let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id };
-                let span = self.tcx.def_span(def_id);
-                return self.path_append(
-                    |cx| cx.print_def_path(parent_def_id, None),
-                    &format!("<impl at {:?}>", span),
-                );
-            }
-        }
-
-        self.default_print_def_path(def_id, substs)
-    }
-
-    fn print_region(
-        self: PrintCx<'_, '_, '_, Self>,
-        region: ty::Region<'_>,
-    ) -> Result<Self::Region, Self::Error> {
-        self.pretty_print_region(region)
-    }
-
-    fn print_type(
-        self: PrintCx<'_, '_, 'tcx, Self>,
-        ty: Ty<'tcx>,
-    ) -> Result<Self::Type, Self::Error> {
-        self.pretty_print_type(ty)
-    }
-
-    fn print_dyn_existential(
-        self: PrintCx<'_, '_, 'tcx, Self>,
-        predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
-    ) -> Result<Self::DynExistential, Self::Error> {
-        self.pretty_print_dyn_existential(predicates)
-    }
-
-    fn path_crate(
-        mut self: PrintCx<'_, '_, '_, Self>,
-        cnum: CrateNum,
-    ) -> Result<Self::Path, Self::Error> {
-        self.empty_path = true;
-        if cnum == LOCAL_CRATE {
-            if self.tcx.sess.rust_2018() {
-                // We add the `crate::` keyword on Rust 2018, only when desired.
-                if SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) {
-                    write!(self, "{}", keywords::Crate.name())?;
-                    self.empty_path = false;
-                }
-            }
-        } else {
-            write!(self, "{}", self.tcx.crate_name(cnum))?;
-            self.empty_path = false;
-        }
-        self.ok()
-    }
-    fn path_qualified(
-        self: PrintCx<'_, '_, 'tcx, Self>,
-        self_ty: Ty<'tcx>,
-        trait_ref: Option<ty::TraitRef<'tcx>>,
-    ) -> Result<Self::Path, Self::Error> {
-        let mut path = self.pretty_path_qualified(self_ty, trait_ref)?;
-        path.empty_path = false;
-        Ok(path)
-    }
-
-    fn path_append_impl<'gcx, 'tcx>(
-        self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, Self>,
-        ) -> Result<Self::Path, Self::Error>,
-        self_ty: Ty<'tcx>,
-        trait_ref: Option<ty::TraitRef<'tcx>>,
-    ) -> Result<Self::Path, Self::Error> {
-        let mut path = self.pretty_path_append_impl(|cx| {
-            let mut path = print_prefix(cx)?;
-            if !path.empty_path {
-                write!(path, "::")?;
-            }
-
-            Ok(path)
-        }, self_ty, trait_ref)?;
-        path.empty_path = false;
-        Ok(path)
-    }
-    fn path_append<'gcx, 'tcx>(
-        self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, Self>,
-        ) -> Result<Self::Path, Self::Error>,
-        text: &str,
-    ) -> Result<Self::Path, Self::Error> {
-        let mut path = print_prefix(self)?;
-
-        // FIXME(eddyb) `text` should never be empty, but it
-        // currently is for `extern { ... }` "foreign modules".
-        if !text.is_empty() {
-            if !path.empty_path {
-                write!(path, "::")?;
-            }
-            write!(path, "{}", text)?;
-            path.empty_path = false;
-        }
-
-        Ok(path)
-    }
-    fn path_generic_args<'gcx, 'tcx>(
-        mut self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, Self>,
-        ) -> Result<Self::Path, Self::Error>,
-        args: &[Kind<'tcx>],
-    ) -> Result<Self::Path, Self::Error> {
-        define_scoped_cx!(self);
-
-        nest!(print_prefix(self));
 
-        // Don't print `'_` if there's no unerased regions.
-        let print_regions = args.iter().any(|arg| {
-            match arg.unpack() {
-                UnpackedKind::Lifetime(r) => *r != ty::ReErased,
-                _ => false,
-            }
-        });
-        let args = args.iter().cloned().filter(|arg| {
-            match arg.unpack() {
-                UnpackedKind::Lifetime(_) => print_regions,
-                _ => true,
-            }
-        });
-
-        if args.clone().next().is_some() {
-            if self.in_value {
-                write!(self, "::")?;
-            }
-            self.generic_delimiters(|cx| cx.comma_sep(args))
-        } else {
-            self.ok()
-        }
-    }
-}
-
-impl<F: fmt::Write> PrettyPrinter for FmtPrinter<F> {
-    fn print_value_path(
-        mut self: PrintCx<'_, '_, 'tcx, Self>,
-        def_id: DefId,
-        substs: Option<SubstsRef<'tcx>>,
-    ) -> Result<Self::Path, Self::Error> {
-        let was_in_value = std::mem::replace(&mut self.in_value, true);
-        let mut path = self.print_def_path(def_id, substs)?;
-        path.in_value = was_in_value;
-
-        Ok(path)
-    }
-
-    fn in_binder<T>(
-        self: PrintCx<'_, '_, 'tcx, Self>,
-        value: &ty::Binder<T>,
-    ) -> Result<Self, Self::Error>
-        where T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>
-    {
-        self.pretty_in_binder(value)
-    }
-
-    fn generic_delimiters<'gcx, 'tcx>(
-        mut self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        f: impl FnOnce(PrintCx<'_, 'gcx, 'tcx, Self>) -> Result<Self, Self::Error>,
-    ) -> Result<Self, Self::Error> {
-        write!(self, "<")?;
-
-        let was_in_value = std::mem::replace(&mut self.in_value, false);
-        let mut inner = f(self)?;
-        inner.in_value = was_in_value;
-
-        write!(inner, ">")?;
-        Ok(inner)
-    }
-
-    fn region_should_not_be_omitted(
-        self: &PrintCx<'_, '_, '_, Self>,
-        region: ty::Region<'_>,
-    ) -> bool {
-        let highlight = self.region_highlight_mode;
-        if highlight.region_highlighted(region).is_some() {
-            return true;
-        }
-
-        if self.tcx.sess.verbose() {
-            return true;
-        }
-
-        let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions;
-
-        match *region {
-            ty::ReEarlyBound(ref data) => {
-                data.name != "" && data.name != "'_"
-            }
-
-            ty::ReLateBound(_, br) |
-            ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
-            ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
-                if let ty::BrNamed(_, name) = br {
-                    if name != "" && name != "'_" {
-                        return true;
-                    }
-                }
-
-                if let Some((region, _)) = highlight.highlight_bound_region {
-                    if br == region {
-                        return true;
-                    }
-                }
-
-                false
-            }
-
-            ty::ReScope(_) |
-            ty::ReVar(_) if identify_regions => true,
-
-            ty::ReVar(_) |
-            ty::ReScope(_) |
-            ty::ReErased => false,
-
-            ty::ReStatic |
-            ty::ReEmpty |
-            ty::ReClosureBound(_) => true,
-        }
-    }
-}
-
-// HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`.
-impl<F: fmt::Write> FmtPrinter<F> {
-    pub fn pretty_print_region(
-        mut self: PrintCx<'_, '_, '_, Self>,
-        region: ty::Region<'_>,
-    ) -> Result<Self, fmt::Error> {
-        define_scoped_cx!(self);
-
-        // Watch out for region highlights.
-        let highlight = self.region_highlight_mode;
-        if let Some(n) = highlight.region_highlighted(region) {
-            p!(write("'{}", n));
-            return self.ok();
-        }
-
-        if self.tcx.sess.verbose() {
-            p!(write("{:?}", region));
-            return self.ok();
-        }
-
-        let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions;
-
-        // These printouts are concise.  They do not contain all the information
-        // the user might want to diagnose an error, but there is basically no way
-        // to fit that into a short string.  Hence the recommendation to use
-        // `explain_region()` or `note_and_explain_region()`.
-        match *region {
-            ty::ReEarlyBound(ref data) => {
-                if data.name != "" {
-                    p!(write("{}", data.name));
-                    return self.ok();
-                }
-            }
-            ty::ReLateBound(_, br) |
-            ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
-            ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
-                if let ty::BrNamed(_, name) = br {
-                    if name != "" && name != "'_" {
-                        p!(write("{}", name));
-                        return self.ok();
-                    }
-                }
-
-                if let Some((region, counter)) = highlight.highlight_bound_region {
-                    if br == region {
-                        p!(write("'{}", counter));
-                        return self.ok();
-                    }
-                }
-            }
-            ty::ReScope(scope) if identify_regions => {
-                match scope.data {
-                    region::ScopeData::Node =>
-                        p!(write("'{}s", scope.item_local_id().as_usize())),
-                    region::ScopeData::CallSite =>
-                        p!(write("'{}cs", scope.item_local_id().as_usize())),
-                    region::ScopeData::Arguments =>
-                        p!(write("'{}as", scope.item_local_id().as_usize())),
-                    region::ScopeData::Destruction =>
-                        p!(write("'{}ds", scope.item_local_id().as_usize())),
-                    region::ScopeData::Remainder(first_statement_index) => p!(write(
-                        "'{}_{}rs",
-                        scope.item_local_id().as_usize(),
-                        first_statement_index.index()
-                    )),
-                }
-                return self.ok();
-            }
-            ty::ReVar(region_vid) if identify_regions => {
-                p!(write("{:?}", region_vid));
-                return self.ok();
-            }
-            ty::ReVar(_) => {}
-            ty::ReScope(_) |
-            ty::ReErased => {}
-            ty::ReStatic => {
-                p!(write("'static"));
-                return self.ok();
-            }
-            ty::ReEmpty => {
-                p!(write("'<empty>"));
-                return self.ok();
-            }
-
-            // The user should never encounter these in unsubstituted form.
-            ty::ReClosureBound(vid) => {
-                p!(write("{:?}", vid));
-                return self.ok();
-            }
-        }
-
-        p!(write("'_"));
-
-        self.ok()
-    }
-}
-
-impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
-    pub fn pretty_print_type(
+    fn pretty_print_type(
         mut self,
         ty: Ty<'tcx>,
-    ) -> Result<P::Type, P::Error> {
+    ) -> Result<Self::Type, Self::Error> {
         define_scoped_cx!(self);
 
         match ty.sty {
@@ -944,9 +469,9 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                 p!(write(")"))
             }
             ty::FnDef(def_id, substs) => {
-                let sig = self.tcx.fn_sig(def_id).subst(self.tcx, substs);
+                let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs);
                 p!(print(sig), write(" {{"));
-                nest!(self.print_value_path(def_id, Some(substs)));
+                self = self.print_value_path(def_id, Some(substs))?;
                 p!(write("}}"))
             }
             ty::FnPtr(ref bare_fn) => {
@@ -969,7 +494,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                 }
             }
             ty::Adt(def, substs) => {
-                nest!(self.print_def_path(def.did, Some(substs)));
+                self = self.print_def_path(def.did, Some(substs))?;
             }
             ty::Dynamic(data, r) => {
                 let print_r = self.region_should_not_be_omitted(r);
@@ -982,7 +507,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                 }
             }
             ty::Foreign(def_id) => {
-                nest!(self.print_def_path(def_id, None));
+                self = self.print_def_path(def_id, None)?;
             }
             ty::Projection(ref data) => p!(print(data)),
             ty::UnnormalizedProjection(ref data) => {
@@ -993,12 +518,12 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             }
             ty::Opaque(def_id, substs) => {
                 // FIXME(eddyb) print this with `print_def_path`.
-                if self.tcx.sess.verbose() {
+                if self.tcx().sess.verbose() {
                     p!(write("Opaque({:?}, {:?})", def_id, substs));
-                    return self.ok();
+                    return Ok(self);
                 }
 
-                let def_key = self.tcx.def_key(def_id);
+                let def_key = self.tcx().def_key(def_id);
                 if let Some(name) = def_key.disambiguated_data.data.get_opt_name() {
                     p!(write("{}", name));
                     let mut substs = substs.iter();
@@ -1011,11 +536,11 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                         }
                         p!(write(">"));
                     }
-                    return self.ok();
+                    return Ok(self);
                 }
                 // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
                 // by looking up the projections associated with the def_id.
-                let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
+                let bounds = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs);
 
                 let mut first = true;
                 let mut is_sized = false;
@@ -1023,7 +548,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                 for predicate in bounds.predicates {
                     if let Some(trait_ref) = predicate.to_opt_poly_trait_ref() {
                         // Don't print +Sized, but rather +?Sized if absent.
-                        if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() {
+                        if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() {
                             is_sized = true;
                             continue;
                         }
@@ -1042,8 +567,8 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             }
             ty::Str => p!(write("str")),
             ty::Generator(did, substs, movability) => {
-                let upvar_tys = substs.upvar_tys(did, self.tcx);
-                let witness = substs.witness(did, self.tcx);
+                let upvar_tys = substs.upvar_tys(did, self.tcx());
+                let witness = substs.witness(did, self.tcx());
                 if movability == hir::GeneratorMovability::Movable {
                     p!(write("[generator"));
                 } else {
@@ -1051,10 +576,10 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                 }
 
                 // FIXME(eddyb) should use `def_span`.
-                if let Some(hir_id) = self.tcx.hir().as_local_hir_id(did) {
-                    p!(write("@{:?}", self.tcx.hir().span_by_hir_id(hir_id)));
+                if let Some(hir_id) = self.tcx().hir().as_local_hir_id(did) {
+                    p!(write("@{:?}", self.tcx().hir().span_by_hir_id(hir_id)));
                     let mut sep = " ";
-                    for (freevar, upvar_ty) in self.tcx.freevars(did)
+                    for (freevar, upvar_ty) in self.tcx().freevars(did)
                         .as_ref()
                         .map_or(&[][..], |fv| &fv[..])
                         .iter()
@@ -1063,7 +588,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                         p!(
                             write("{}{}:",
                                     sep,
-                                    self.tcx.hir().name(freevar.var_id())),
+                                    self.tcx().hir().name(freevar.var_id())),
                             print(upvar_ty));
                         sep = ", ";
                     }
@@ -1083,21 +608,21 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                 p!(write(" "), print(witness), write("]"))
             },
             ty::GeneratorWitness(types) => {
-                nest!(self.in_binder(&types))
+                self = self.in_binder(&types)?;
             }
             ty::Closure(did, substs) => {
-                let upvar_tys = substs.upvar_tys(did, self.tcx);
+                let upvar_tys = substs.upvar_tys(did, self.tcx());
                 p!(write("[closure"));
 
                 // FIXME(eddyb) should use `def_span`.
-                if let Some(hir_id) = self.tcx.hir().as_local_hir_id(did) {
-                    if self.tcx.sess.opts.debugging_opts.span_free_formats {
+                if let Some(hir_id) = self.tcx().hir().as_local_hir_id(did) {
+                    if self.tcx().sess.opts.debugging_opts.span_free_formats {
                         p!(write("@{:?}", hir_id));
                     } else {
-                        p!(write("@{:?}", self.tcx.hir().span_by_hir_id(hir_id)));
+                        p!(write("@{:?}", self.tcx().hir().span_by_hir_id(hir_id)));
                     }
                     let mut sep = " ";
-                    for (freevar, upvar_ty) in self.tcx.freevars(did)
+                    for (freevar, upvar_ty) in self.tcx().freevars(did)
                         .as_ref()
                         .map_or(&[][..], |fv| &fv[..])
                         .iter()
@@ -1106,7 +631,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                         p!(
                             write("{}{}:",
                                     sep,
-                                    self.tcx.hir().name(freevar.var_id())),
+                                    self.tcx().hir().name(freevar.var_id())),
                             print(upvar_ty));
                         sep = ", ";
                     }
@@ -1123,11 +648,11 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                     }
                 }
 
-                if self.tcx.sess.verbose() {
+                if self.tcx().sess.verbose() {
                     p!(write(
                         " closure_kind_ty={:?} closure_sig_ty={:?}",
-                        substs.closure_kind_ty(did, self.tcx),
-                        substs.closure_sig_ty(did, self.tcx)
+                        substs.closure_kind_ty(did, self.tcx()),
+                        substs.closure_sig_ty(did, self.tcx())
                     ));
                 }
 
@@ -1144,7 +669,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                             ConstValue::Infer(..) => p!(write("_")),
                             ConstValue::Param(ParamConst { name, .. }) =>
                                 p!(write("{}", name)),
-                            _ => p!(write("{}", c.unwrap_usize(self.tcx))),
+                            _ => p!(write("{}", c.unwrap_usize(self.tcx()))),
                         }
                     }
                 }
@@ -1155,30 +680,30 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             }
         }
 
-        self.ok()
+        Ok(self)
     }
 
     fn pretty_print_dyn_existential(
         mut self,
         predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
-    ) -> Result<P::DynExistential, P::Error> {
+    ) -> Result<Self::DynExistential, Self::Error> {
         define_scoped_cx!(self);
 
         // Generate the main trait ref, including associated types.
         let mut first = true;
 
         if let Some(principal) = predicates.principal() {
-            nest!(self.print_def_path(principal.def_id, None));
+            self = self.print_def_path(principal.def_id, None)?;
 
             let mut resugared = false;
 
             // Special-case `Fn(...) -> ...` and resugar it.
-            let fn_trait_kind = self.tcx.lang_items().fn_trait_kind(principal.def_id);
-            if !self.tcx.sess.verbose() && fn_trait_kind.is_some() {
+            let fn_trait_kind = self.tcx().lang_items().fn_trait_kind(principal.def_id);
+            if !self.tcx().sess.verbose() && fn_trait_kind.is_some() {
                 if let ty::Tuple(ref args) = principal.substs.type_at(0).sty {
                     let mut projections = predicates.projection_bounds();
                     if let (Some(proj), None) = (projections.next(), projections.next()) {
-                        nest!(self.pretty_fn_sig(args, false, proj.ty));
+                        self = self.pretty_fn_sig(args, false, proj.ty)?;
                         resugared = true;
                     }
                 }
@@ -1188,11 +713,11 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             // in order to place the projections inside the `<...>`.
             if !resugared {
                 // Use a type that can't appear in defaults of type parameters.
-                let dummy_self = self.tcx.mk_infer(ty::FreshTy(0));
-                let principal = principal.with_self_ty(self.tcx, dummy_self);
+                let dummy_self = self.tcx().mk_infer(ty::FreshTy(0));
+                let principal = principal.with_self_ty(self.tcx(), dummy_self);
 
                 let args = self.generic_args_to_print(
-                    self.tcx.generics_of(principal.def_id),
+                    self.tcx().generics_of(principal.def_id),
                     principal.substs,
                 );
 
@@ -1217,13 +742,13 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
                     let args = arg0.into_iter().chain(args);
                     let projections = projection0.into_iter().chain(projections);
 
-                    nest!(self.generic_delimiters(|mut cx| {
-                        cx = PrintCx::new(cx.tcx, cx.comma_sep(args)?);
+                    self = self.generic_delimiters(|mut cx| {
+                        cx = cx.comma_sep(args)?;
                         if arg0.is_some() && projection0.is_some() {
                             write!(cx, ", ")?;
                         }
                         cx.comma_sep(projections)
-                    }));
+                    })?;
                 }
             }
             first = false;
@@ -1233,7 +758,7 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
         // FIXME(eddyb) avoid printing twice (needed to ensure
         // that the auto traits are sorted *and* printed via cx).
         let mut auto_traits: Vec<_> = predicates.auto_traits().map(|did| {
-            (self.tcx.def_path_str(did), did)
+            (self.tcx().def_path_str(did), did)
         }).collect();
 
         // The auto traits come ordered by `DefPathHash`. While
@@ -1251,18 +776,18 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             }
             first = false;
 
-            nest!(self.print_def_path(def_id, None));
+            self = self.print_def_path(def_id, None)?;
         }
 
-        self.ok()
+        Ok(self)
     }
 
-    pub fn pretty_fn_sig(
+    fn pretty_fn_sig(
         mut self,
         inputs: &[Ty<'tcx>],
         c_variadic: bool,
         output: Ty<'tcx>,
-    ) -> Result<P, fmt::Error> {
+    ) -> Result<Self, Self::Error> {
         define_scoped_cx!(self);
 
         p!(write("("));
@@ -1281,18 +806,464 @@ impl<'gcx, 'tcx, P: PrettyPrinter> PrintCx<'_, 'gcx, 'tcx, P> {
             p!(write(" -> "), print(output));
         }
 
-        self.ok()
+        Ok(self)
+    }
+}
+
+// HACK(eddyb) boxed to avoid moving around a large struct by-value.
+pub struct FmtPrinter<'a, 'gcx, 'tcx, F>(Box<FmtPrinterData<'a, 'gcx, 'tcx, F>>);
+
+pub struct FmtPrinterData<'a, 'gcx, 'tcx, F> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    fmt: F,
+
+    empty_path: bool,
+    in_value: bool,
+
+    used_region_names: FxHashSet<InternedString>,
+    region_index: usize,
+    binder_depth: usize,
+
+    pub region_highlight_mode: RegionHighlightMode,
+}
+
+impl<F> Deref for FmtPrinter<'a, 'gcx, 'tcx, F> {
+    type Target = FmtPrinterData<'a, 'gcx, 'tcx, F>;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<F> DerefMut for FmtPrinter<'_, '_, '_, F> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+impl<F> FmtPrinter<'a, 'gcx, 'tcx, F> {
+    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, fmt: F, ns: Namespace) -> Self {
+        FmtPrinter(Box::new(FmtPrinterData {
+            tcx,
+            fmt,
+            empty_path: false,
+            in_value: ns == Namespace::ValueNS,
+            used_region_names: Default::default(),
+            region_index: 0,
+            binder_depth: 0,
+            region_highlight_mode: RegionHighlightMode::default(),
+        }))
+    }
+}
+
+impl TyCtxt<'_, '_, '_> {
+    // HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always
+    // (but also some things just print a `DefId` generally so maybe we need this?)
+    fn guess_def_namespace(self, def_id: DefId) -> Namespace {
+        match self.def_key(def_id).disambiguated_data.data {
+            DefPathData::ValueNs(..) |
+            DefPathData::EnumVariant(..) |
+            DefPathData::Field(..) |
+            DefPathData::AnonConst |
+            DefPathData::ConstParam(..) |
+            DefPathData::ClosureExpr |
+            DefPathData::StructCtor => Namespace::ValueNS,
+
+            DefPathData::MacroDef(..) => Namespace::MacroNS,
+
+            _ => Namespace::TypeNS,
+        }
+    }
+
+    /// Returns a string identifying this `DefId`. This string is
+    /// suitable for user output.
+    pub fn def_path_str(self, def_id: DefId) -> String {
+        let ns = self.guess_def_namespace(def_id);
+        debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns);
+        let mut s = String::new();
+        let _ = FmtPrinter::new(self, &mut s, ns)
+            .print_def_path(def_id, None);
+        s
+    }
+}
+
+impl<F: fmt::Write> fmt::Write for FmtPrinter<'_, '_, '_, F> {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        self.fmt.write_str(s)
+    }
+}
+
+impl<F: fmt::Write> Printer<'gcx, 'tcx> for FmtPrinter<'_, 'gcx, 'tcx, F> {
+    type Error = fmt::Error;
+
+    type Path = Self;
+    type Region = Self;
+    type Type = Self;
+    type DynExistential = Self;
+
+    fn tcx(&'a self) -> TyCtxt<'a, 'gcx, 'tcx> {
+        self.tcx
+    }
+
+    fn print_def_path(
+        mut self,
+        def_id: DefId,
+        substs: Option<SubstsRef<'tcx>>,
+    ) -> Result<Self::Path, Self::Error> {
+        define_scoped_cx!(self);
+
+        // FIXME(eddyb) avoid querying `tcx.generics_of` and `tcx.def_key`
+        // both here and in `default_print_def_path`.
+        let generics = substs.map(|_| self.tcx.generics_of(def_id));
+        if generics.as_ref().and_then(|g| g.parent).is_none() {
+            match self.try_print_visible_def_path(def_id)? {
+                (cx, true) => return if let (Some(generics), Some(substs)) = (generics, substs) {
+                    let args = cx.generic_args_to_print(generics, substs);
+                    cx.path_generic_args(Ok, args)
+                } else {
+                    Ok(cx)
+                },
+                (cx, false) => self = cx,
+            }
+        }
+
+        let key = self.tcx.def_key(def_id);
+        if let DefPathData::Impl = key.disambiguated_data.data {
+            // Always use types for non-local impls, where types are always
+            // available, and filename/line-number is mostly uninteresting.
+            let use_types =
+                !def_id.is_local() || {
+                    // Otherwise, use filename/line-number if forced.
+                    let force_no_types = FORCE_IMPL_FILENAME_LINE.with(|f| f.get());
+                    !force_no_types
+                };
+
+            if !use_types {
+                // If no type info is available, fall back to
+                // pretty printing some span information. This should
+                // only occur very early in the compiler pipeline.
+                let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id };
+                let span = self.tcx.def_span(def_id);
+                return self.path_append(
+                    |cx| cx.print_def_path(parent_def_id, None),
+                    &format!("<impl at {:?}>", span),
+                );
+            }
+        }
+
+        self.default_print_def_path(def_id, substs)
+    }
+
+    fn print_region(
+        self,
+        region: ty::Region<'_>,
+    ) -> Result<Self::Region, Self::Error> {
+        self.pretty_print_region(region)
+    }
+
+    fn print_type(
+        self,
+        ty: Ty<'tcx>,
+    ) -> Result<Self::Type, Self::Error> {
+        self.pretty_print_type(ty)
+    }
+
+    fn print_dyn_existential(
+        self,
+        predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
+    ) -> Result<Self::DynExistential, Self::Error> {
+        self.pretty_print_dyn_existential(predicates)
+    }
+
+    fn path_crate(
+        mut self,
+        cnum: CrateNum,
+    ) -> Result<Self::Path, Self::Error> {
+        self.empty_path = true;
+        if cnum == LOCAL_CRATE {
+            if self.tcx.sess.rust_2018() {
+                // We add the `crate::` keyword on Rust 2018, only when desired.
+                if SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) {
+                    write!(self, "{}", keywords::Crate.name())?;
+                    self.empty_path = false;
+                }
+            }
+        } else {
+            write!(self, "{}", self.tcx.crate_name(cnum))?;
+            self.empty_path = false;
+        }
+        Ok(self)
+    }
+    fn path_qualified(
+        mut self,
+        self_ty: Ty<'tcx>,
+        trait_ref: Option<ty::TraitRef<'tcx>>,
+    ) -> Result<Self::Path, Self::Error> {
+        self = self.pretty_path_qualified(self_ty, trait_ref)?;
+        self.empty_path = false;
+        Ok(self)
+    }
+
+    fn path_append_impl(
+        mut self,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+        self_ty: Ty<'tcx>,
+        trait_ref: Option<ty::TraitRef<'tcx>>,
+    ) -> Result<Self::Path, Self::Error> {
+        self = self.pretty_path_append_impl(|mut cx| {
+            cx = print_prefix(cx)?;
+            if !cx.empty_path {
+                write!(cx, "::")?;
+            }
+
+            Ok(cx)
+        }, self_ty, trait_ref)?;
+        self.empty_path = false;
+        Ok(self)
+    }
+    fn path_append(
+        mut self,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+        text: &str,
+    ) -> Result<Self::Path, Self::Error> {
+        self = print_prefix(self)?;
+
+        // FIXME(eddyb) `text` should never be empty, but it
+        // currently is for `extern { ... }` "foreign modules".
+        if !text.is_empty() {
+            if !self.empty_path {
+                write!(self, "::")?;
+            }
+            write!(self, "{}", text)?;
+            self.empty_path = false;
+        }
+
+        Ok(self)
+    }
+    fn path_generic_args(
+        mut self,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
+        args: &[Kind<'tcx>],
+    ) -> Result<Self::Path, Self::Error> {
+        self = print_prefix(self)?;
+
+        // Don't print `'_` if there's no unerased regions.
+        let print_regions = args.iter().any(|arg| {
+            match arg.unpack() {
+                UnpackedKind::Lifetime(r) => *r != ty::ReErased,
+                _ => false,
+            }
+        });
+        let args = args.iter().cloned().filter(|arg| {
+            match arg.unpack() {
+                UnpackedKind::Lifetime(_) => print_regions,
+                _ => true,
+            }
+        });
+
+        if args.clone().next().is_some() {
+            if self.in_value {
+                write!(self, "::")?;
+            }
+            self.generic_delimiters(|cx| cx.comma_sep(args))
+        } else {
+            Ok(self)
+        }
+    }
+}
+
+impl<F: fmt::Write> PrettyPrinter<'gcx, 'tcx> for FmtPrinter<'_, 'gcx, 'tcx, F> {
+    fn print_value_path(
+        mut self,
+        def_id: DefId,
+        substs: Option<SubstsRef<'tcx>>,
+    ) -> Result<Self::Path, Self::Error> {
+        let was_in_value = std::mem::replace(&mut self.in_value, true);
+        self = self.print_def_path(def_id, substs)?;
+        self.in_value = was_in_value;
+
+        Ok(self)
+    }
+
+    fn in_binder<T>(
+        self,
+        value: &ty::Binder<T>,
+    ) -> Result<Self, Self::Error>
+        where T: Print<'gcx, 'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>
+    {
+        self.pretty_in_binder(value)
+    }
+
+    fn generic_delimiters(
+        mut self,
+        f: impl FnOnce(Self) -> Result<Self, Self::Error>,
+    ) -> Result<Self, Self::Error> {
+        write!(self, "<")?;
+
+        let was_in_value = std::mem::replace(&mut self.in_value, false);
+        let mut inner = f(self)?;
+        inner.in_value = was_in_value;
+
+        write!(inner, ">")?;
+        Ok(inner)
+    }
+
+    fn region_should_not_be_omitted(
+        &self,
+        region: ty::Region<'_>,
+    ) -> bool {
+        let highlight = self.region_highlight_mode;
+        if highlight.region_highlighted(region).is_some() {
+            return true;
+        }
+
+        if self.tcx.sess.verbose() {
+            return true;
+        }
+
+        let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions;
+
+        match *region {
+            ty::ReEarlyBound(ref data) => {
+                data.name != "" && data.name != "'_"
+            }
+
+            ty::ReLateBound(_, br) |
+            ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
+            ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
+                if let ty::BrNamed(_, name) = br {
+                    if name != "" && name != "'_" {
+                        return true;
+                    }
+                }
+
+                if let Some((region, _)) = highlight.highlight_bound_region {
+                    if br == region {
+                        return true;
+                    }
+                }
+
+                false
+            }
+
+            ty::ReScope(_) |
+            ty::ReVar(_) if identify_regions => true,
+
+            ty::ReVar(_) |
+            ty::ReScope(_) |
+            ty::ReErased => false,
+
+            ty::ReStatic |
+            ty::ReEmpty |
+            ty::ReClosureBound(_) => true,
+        }
+    }
+}
+
+// HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`.
+impl<F: fmt::Write> FmtPrinter<'_, '_, '_, F> {
+    pub fn pretty_print_region(
+        mut self,
+        region: ty::Region<'_>,
+    ) -> Result<Self, fmt::Error> {
+        define_scoped_cx!(self);
+
+        // Watch out for region highlights.
+        let highlight = self.region_highlight_mode;
+        if let Some(n) = highlight.region_highlighted(region) {
+            p!(write("'{}", n));
+            return Ok(self);
+        }
+
+        if self.tcx.sess.verbose() {
+            p!(write("{:?}", region));
+            return Ok(self);
+        }
+
+        let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions;
+
+        // These printouts are concise.  They do not contain all the information
+        // the user might want to diagnose an error, but there is basically no way
+        // to fit that into a short string.  Hence the recommendation to use
+        // `explain_region()` or `note_and_explain_region()`.
+        match *region {
+            ty::ReEarlyBound(ref data) => {
+                if data.name != "" {
+                    p!(write("{}", data.name));
+                    return Ok(self);
+                }
+            }
+            ty::ReLateBound(_, br) |
+            ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
+            ty::RePlaceholder(ty::Placeholder { name: br, .. }) => {
+                if let ty::BrNamed(_, name) = br {
+                    if name != "" && name != "'_" {
+                        p!(write("{}", name));
+                        return Ok(self);
+                    }
+                }
+
+                if let Some((region, counter)) = highlight.highlight_bound_region {
+                    if br == region {
+                        p!(write("'{}", counter));
+                        return Ok(self);
+                    }
+                }
+            }
+            ty::ReScope(scope) if identify_regions => {
+                match scope.data {
+                    region::ScopeData::Node =>
+                        p!(write("'{}s", scope.item_local_id().as_usize())),
+                    region::ScopeData::CallSite =>
+                        p!(write("'{}cs", scope.item_local_id().as_usize())),
+                    region::ScopeData::Arguments =>
+                        p!(write("'{}as", scope.item_local_id().as_usize())),
+                    region::ScopeData::Destruction =>
+                        p!(write("'{}ds", scope.item_local_id().as_usize())),
+                    region::ScopeData::Remainder(first_statement_index) => p!(write(
+                        "'{}_{}rs",
+                        scope.item_local_id().as_usize(),
+                        first_statement_index.index()
+                    )),
+                }
+                return Ok(self);
+            }
+            ty::ReVar(region_vid) if identify_regions => {
+                p!(write("{:?}", region_vid));
+                return Ok(self);
+            }
+            ty::ReVar(_) => {}
+            ty::ReScope(_) |
+            ty::ReErased => {}
+            ty::ReStatic => {
+                p!(write("'static"));
+                return Ok(self);
+            }
+            ty::ReEmpty => {
+                p!(write("'<empty>"));
+                return Ok(self);
+            }
+
+            // The user should never encounter these in unsubstituted form.
+            ty::ReClosureBound(vid) => {
+                p!(write("{:?}", vid));
+                return Ok(self);
+            }
+        }
+
+        p!(write("'_"));
+
+        Ok(self)
     }
 }
 
 // HACK(eddyb) limited to `FmtPrinter` because of `binder_depth`,
 // `region_index` and `used_region_names`.
-impl<F: fmt::Write> FmtPrinter<F> {
+impl<F: fmt::Write> FmtPrinter<'_, 'gcx, 'tcx, F> {
     pub fn pretty_in_binder<T>(
-        mut self: PrintCx<'_, '_, 'tcx, Self>,
+        mut self,
         value: &ty::Binder<T>,
     ) -> Result<Self, fmt::Error>
-        where T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>
+        where T: Print<'gcx, 'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>
     {
         fn name_by_region_index(index: usize) -> InternedString {
             match index {
@@ -1382,107 +1353,94 @@ impl<F: fmt::Write> FmtPrinter<F> {
     }
 }
 
-impl<T, P: PrettyPrinter> Print<'tcx, P> for ty::Binder<T>
-    where T: Print<'tcx, P, Output = P, Error = P::Error> + TypeFoldable<'tcx>
+impl<'gcx: 'tcx, 'tcx, T, P: PrettyPrinter<'gcx, 'tcx>> Print<'gcx, 'tcx, P>
+    for ty::Binder<T>
+    where T: Print<'gcx, 'tcx, P, Output = P, Error = P::Error> + TypeFoldable<'tcx>
 {
     type Output = P;
     type Error = P::Error;
-    fn print(&self, cx: PrintCx<'_, '_, 'tcx, P>) -> Result<Self::Output, Self::Error> {
+    fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
         cx.in_binder(self)
     }
 }
 
-pub trait LiftAndPrintToFmt<'tcx> {
-    fn lift_and_print_to_fmt(
-        &self,
-        tcx: TyCtxt<'_, '_, 'tcx>,
-        f: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result;
-}
-
-impl<T> LiftAndPrintToFmt<'tcx> for T
-    where T: ty::Lift<'tcx>,
-          for<'a, 'b> <T as ty::Lift<'tcx>>::Lifted:
-            Print<'tcx, FmtPrinter<&'a mut fmt::Formatter<'b>>, Error = fmt::Error>
+impl<'gcx: 'tcx, 'tcx, T, U, P: PrettyPrinter<'gcx, 'tcx>> Print<'gcx, 'tcx, P>
+    for ty::OutlivesPredicate<T, U>
+    where T: Print<'gcx, 'tcx, P, Output = P, Error = P::Error>,
+          U: Print<'gcx, 'tcx, P, Output = P, Error = P::Error>,
 {
-    fn lift_and_print_to_fmt(
-        &self,
-        tcx: TyCtxt<'_, '_, 'tcx>,
-        f: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result {
-        tcx.lift(self)
-            .expect("could not lift for printing")
-            .print(PrintCx::new(tcx, FmtPrinter::new(f, Namespace::TypeNS)))?;
-        Ok(())
-    }
-}
-
-// HACK(eddyb) this is separate because `ty::RegionKind` doesn't need lifting.
-impl LiftAndPrintToFmt<'tcx> for ty::RegionKind {
-    fn lift_and_print_to_fmt(
-        &self,
-        tcx: TyCtxt<'_, '_, 'tcx>,
-        f: &mut fmt::Formatter<'_>,
-    ) -> fmt::Result {
-        self.print(PrintCx::new(tcx, FmtPrinter::new(f, Namespace::TypeNS)))?;
-        Ok(())
+    type Output = P;
+    type Error = P::Error;
+    fn print(&self, mut cx: P) -> Result<Self::Output, Self::Error> {
+        define_scoped_cx!(cx);
+        p!(print(self.0), write(" : "), print(self.1));
+        Ok(cx)
     }
 }
 
 macro_rules! forward_display_to_print {
-    (<$($T:ident),*> $ty:ty) => {
-        impl<$($T),*> fmt::Display for $ty
-            where Self: for<'a> LiftAndPrintToFmt<'a>
-        {
+    ($($ty:ty),+) => {
+        $(impl fmt::Display for $ty {
             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                ty::tls::with(|tcx| self.lift_and_print_to_fmt(tcx, f))
+                ty::tls::with(|tcx| {
+                    tcx.lift(self)
+                        .expect("could not lift for printing")
+                        .print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?;
+                    Ok(())
+                })
             }
-        }
-    };
-
-    ($ty:ty) => {
-        forward_display_to_print!(<> $ty);
+        })+
     };
 }
 
 macro_rules! define_print_and_forward_display {
-    (($self:ident, $cx:ident): <$($T:ident),*> $ty:ty $print:block) => {
-        impl<$($T,)* P: PrettyPrinter> Print<'tcx, P> for $ty
-            where $($T: Print<'tcx, P, Output = P, Error = P::Error>),*
-        {
+    (($self:ident, $cx:ident): $($ty:ty $print:block)+) => {
+        $(impl<'gcx: 'tcx, 'tcx, P: PrettyPrinter<'gcx, 'tcx>> Print<'gcx, 'tcx, P> for $ty {
             type Output = P;
             type Error = fmt::Error;
-            fn print(&$self, $cx: PrintCx<'_, '_, 'tcx, P>) -> Result<Self::Output, Self::Error> {
+            fn print(&$self, $cx: P) -> Result<Self::Output, Self::Error> {
                 #[allow(unused_mut)]
                 let mut $cx = $cx;
                 define_scoped_cx!($cx);
                 let _: () = $print;
                 #[allow(unreachable_code)]
-                $cx.ok()
+                Ok($cx)
             }
-        }
-
-        forward_display_to_print!(<$($T),*> $ty);
-    };
+        })+
 
-    (($self:ident, $cx:ident): $($ty:ty $print:block)+) => {
-        $(define_print_and_forward_display!(($self, $cx): <> $ty $print);)+
+        forward_display_to_print!($($ty),+);
     };
 }
 
-forward_display_to_print!(ty::RegionKind);
-forward_display_to_print!(Ty<'tcx>);
-forward_display_to_print!(&'tcx ty::List<ty::ExistentialPredicate<'tcx>>);
-forward_display_to_print!(<T> ty::Binder<T>);
-
-define_print_and_forward_display! {
-    (self, cx):
-
-    <T, U> ty::OutlivesPredicate<T, U> {
-        p!(print(self.0), write(" : "), print(self.1))
+// HACK(eddyb) this is separate because `ty::RegionKind` doesn't need lifting.
+impl fmt::Display for ty::RegionKind {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        ty::tls::with(|tcx| {
+            self.print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?;
+            Ok(())
+        })
     }
 }
 
+forward_display_to_print! {
+    Ty<'tcx>,
+    &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
+
+    // HACK(eddyb) these are exhaustive instead of generic,
+    // because `for<'gcx: 'tcx, 'tcx>` isn't possible yet.
+    ty::Binder<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>,
+    ty::Binder<ty::TraitRef<'tcx>>,
+    ty::Binder<ty::FnSig<'tcx>>,
+    ty::Binder<ty::TraitPredicate<'tcx>>,
+    ty::Binder<ty::SubtypePredicate<'tcx>>,
+    ty::Binder<ty::ProjectionPredicate<'tcx>>,
+    ty::Binder<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>,
+    ty::Binder<ty::OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>>,
+
+    ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>,
+    ty::OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>
+}
+
 define_print_and_forward_display! {
     (self, cx):
 
@@ -1505,13 +1463,13 @@ define_print_and_forward_display! {
 
     ty::ExistentialTraitRef<'tcx> {
         // Use a type that can't appear in defaults of type parameters.
-        let dummy_self = cx.tcx.mk_infer(ty::FreshTy(0));
-        let trait_ref = self.with_self_ty(cx.tcx, dummy_self);
+        let dummy_self = cx.tcx().mk_infer(ty::FreshTy(0));
+        let trait_ref = self.with_self_ty(cx.tcx(), dummy_self);
         p!(print(trait_ref))
     }
 
     ty::ExistentialProjection<'tcx> {
-        let name = cx.tcx.associated_item(self.item_def_id).ident;
+        let name = cx.tcx().associated_item(self.item_def_id).ident;
         p!(write("{}=", name), print(self.ty))
     }
 
@@ -1520,7 +1478,7 @@ define_print_and_forward_display! {
             ty::ExistentialPredicate::Trait(x) => p!(print(x)),
             ty::ExistentialPredicate::Projection(x) => p!(print(x)),
             ty::ExistentialPredicate::AutoTrait(def_id) => {
-                nest!(cx.print_def_path(def_id, None))
+                cx = cx.print_def_path(def_id, None)?;
             }
         }
     }
@@ -1535,13 +1493,13 @@ define_print_and_forward_display! {
         }
 
         p!(write("fn"));
-        nest!(cx.pretty_fn_sig(self.inputs(), self.c_variadic, self.output()));
+        cx = cx.pretty_fn_sig(self.inputs(), self.c_variadic, self.output())?;
     }
 
     ty::InferTy {
-        if cx.tcx.sess.verbose() {
+        if cx.tcx().sess.verbose() {
             p!(write("{:?}", self));
-            return cx.ok();
+            return Ok(cx);
         }
         match *self {
             ty::TyVar(_) => p!(write("_")),
@@ -1554,7 +1512,7 @@ define_print_and_forward_display! {
     }
 
     ty::TraitRef<'tcx> {
-        nest!(cx.print_def_path(self.def_id, Some(self.substs)));
+        cx = cx.print_def_path(self.def_id, Some(self.substs))?;
     }
 
     ConstValue<'tcx> {
@@ -1569,7 +1527,7 @@ define_print_and_forward_display! {
         p!(write("{} : {}", self.val, self.ty))
     }
 
-    ty::LazyConst<'tcx> {
+    &'tcx ty::LazyConst<'tcx> {
         match self {
             // FIXME(const_generics) this should print at least the type.
             ty::LazyConst::Unevaluated(..) => p!(write("_ : _")),
@@ -1598,7 +1556,7 @@ define_print_and_forward_display! {
     }
 
     ty::ProjectionTy<'tcx> {
-        nest!(cx.print_def_path(self.item_def_id, Some(self.substs)));
+        cx = cx.print_def_path(self.item_def_id, Some(self.substs))?;
     }
 
     ty::ClosureKind {
@@ -1619,17 +1577,17 @@ define_print_and_forward_display! {
             ty::Predicate::WellFormed(ty) => p!(print(ty), write(" well-formed")),
             ty::Predicate::ObjectSafe(trait_def_id) => {
                 p!(write("the trait `"));
-                nest!(cx.print_def_path(trait_def_id, None));
+                cx = cx.print_def_path(trait_def_id, None)?;
                 p!(write("` is object-safe"))
             }
             ty::Predicate::ClosureKind(closure_def_id, _closure_substs, kind) => {
                 p!(write("the closure `"));
-                nest!(cx.print_value_path(closure_def_id, None));
+                cx = cx.print_value_path(closure_def_id, None)?;
                 p!(write("` implements the trait `{}`", kind))
             }
             ty::Predicate::ConstEvaluatable(def_id, substs) => {
                 p!(write("the constant `"));
-                nest!(cx.print_value_path(def_id, Some(substs)));
+                cx = cx.print_value_path(def_id, Some(substs))?;
                 p!(write("` can be evaluated"))
             }
         }
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 32a39c2eb88..4cbb315cefa 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -8,7 +8,7 @@ use crate::mir::ProjectionKind;
 use crate::mir::interpret::ConstValue;
 use crate::ty::{self, Lift, Ty, TyCtxt, ConstVid, InferConst};
 use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
-use crate::ty::print::{FmtPrinter, PrintCx, Printer};
+use crate::ty::print::{FmtPrinter, Printer};
 use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 use smallvec::SmallVec;
 use crate::mir::interpret;
@@ -34,8 +34,9 @@ impl fmt::Debug for ty::GenericParamDef {
 
 impl fmt::Debug for ty::TraitDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        PrintCx::with_tls_tcx(FmtPrinter::new(f, Namespace::TypeNS), |cx| {
-            cx.print_def_path(self.def_id, None)?;
+        ty::tls::with(|tcx| {
+            FmtPrinter::new(tcx, f, Namespace::TypeNS)
+                .print_def_path(self.def_id, None)?;
             Ok(())
         })
     }
@@ -43,8 +44,9 @@ impl fmt::Debug for ty::TraitDef {
 
 impl fmt::Debug for ty::AdtDef {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        PrintCx::with_tls_tcx(FmtPrinter::new(f, Namespace::TypeNS), |cx| {
-            cx.print_def_path(self.did, None)?;
+        ty::tls::with(|tcx| {
+            FmtPrinter::new(tcx, f, Namespace::TypeNS)
+                .print_def_path(self.did, None)?;
             Ok(())
         })
     }
@@ -333,6 +335,7 @@ CloneTypeFoldableAndLiftImpls! {
 ///////////////////////////////////////////////////////////////////////////
 // Lift implementations
 
+// FIXME(eddyb) replace all the uses of `Option::map` with `?`.
 impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) {
     type Lifted = (A::Lifted, B::Lifted);
     fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Self::Lifted> {
@@ -429,6 +432,23 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialTraitRef<'a> {
     }
 }
 
+impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialPredicate<'a> {
+    type Lifted = ty::ExistentialPredicate<'tcx>;
+    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lifted> {
+        match self {
+            ty::ExistentialPredicate::Trait(x) => {
+                tcx.lift(x).map(ty::ExistentialPredicate::Trait)
+            }
+            ty::ExistentialPredicate::Projection(x) => {
+                tcx.lift(x).map(ty::ExistentialPredicate::Projection)
+            }
+            ty::ExistentialPredicate::AutoTrait(def_id) => {
+                Some(ty::ExistentialPredicate::AutoTrait(*def_id))
+            }
+        }
+    }
+}
+
 impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> {
     type Lifted = ty::TraitPredicate<'tcx>;
     fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>)
diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs
index 084b86b1eb4..d1fcc88e599 100644
--- a/src/librustc_codegen_utils/symbol_names.rs
+++ b/src/librustc_codegen_utils/symbol_names.rs
@@ -92,7 +92,7 @@ use rustc::hir::Node;
 use rustc::hir::CodegenFnAttrFlags;
 use rustc::hir::map::definitions::DefPathData;
 use rustc::ich::NodeIdHashingMode;
-use rustc::ty::print::{PrettyPrinter, PrintCx, Printer, Print};
+use rustc::ty::print::{PrettyPrinter, Printer, Print};
 use rustc::ty::query::Providers;
 use rustc::ty::subst::{Kind, SubstsRef, UnpackedKind};
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
@@ -223,10 +223,11 @@ fn get_symbol_hash<'a, 'tcx>(
 }
 
 fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::SymbolName {
-    PrintCx::new(tcx, SymbolPath::new(tcx))
-        .print_def_path(def_id, None)
-        .unwrap()
-        .into_interned()
+    SymbolPrinter {
+        tcx,
+        path: SymbolPath::new(),
+        keep_within_component: false,
+    }.print_def_path(def_id, None).unwrap().path.into_interned()
 }
 
 fn symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) -> ty::SymbolName {
@@ -318,13 +319,17 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance
 
     let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs);
 
-    let mut buf = SymbolPath::from_interned(tcx.def_symbol_name(def_id), tcx);
+    let mut printer = SymbolPrinter {
+        tcx,
+        path: SymbolPath::from_interned(tcx.def_symbol_name(def_id)),
+        keep_within_component: false,
+    };
 
     if instance.is_vtable_shim() {
-        let _ = buf.write_str("{{vtable-shim}}");
+        let _ = printer.write_str("{{vtable-shim}}");
     }
 
-    buf.finish(hash)
+    printer.path.finish(hash)
 }
 
 // Follow C++ namespace-mangling style, see
@@ -344,33 +349,22 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance
 struct SymbolPath {
     result: String,
     temp_buf: String,
-    strict_naming: bool,
-
-    // When `true`, `finalize_pending_component` isn't used.
-    // This is needed when recursing into `path_qualified`,
-    // or `path_generic_args`, as any nested paths are
-    // logically within one component.
-    keep_within_component: bool,
 }
 
 impl SymbolPath {
-    fn new(tcx: TyCtxt<'_, '_, '_>) -> Self {
+    fn new() -> Self {
         let mut result = SymbolPath {
             result: String::with_capacity(64),
             temp_buf: String::with_capacity(16),
-            strict_naming: tcx.has_strict_asm_symbol_naming(),
-            keep_within_component: false,
         };
         result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested
         result
     }
 
-    fn from_interned(symbol: ty::SymbolName, tcx: TyCtxt<'_, '_, '_>) -> Self {
+    fn from_interned(symbol: ty::SymbolName) -> Self {
         let mut result = SymbolPath {
             result: String::with_capacity(64),
             temp_buf: String::with_capacity(16),
-            strict_naming: tcx.has_strict_asm_symbol_naming(),
-            keep_within_component: false,
         };
         result.result.push_str(&symbol.as_str());
         result
@@ -398,11 +392,22 @@ impl SymbolPath {
     }
 }
 
+struct SymbolPrinter<'a, 'tcx> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    path: SymbolPath,
+
+    // When `true`, `finalize_pending_component` isn't used.
+    // This is needed when recursing into `path_qualified`,
+    // or `path_generic_args`, as any nested paths are
+    // logically within one component.
+    keep_within_component: bool,
+}
+
 // HACK(eddyb) this relies on using the `fmt` interface to get
 // `PrettyPrinter` aka pretty printing of e.g. types in paths,
 // symbol names should have their own printing machinery.
 
-impl Printer for SymbolPath {
+impl Printer<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> {
     type Error = fmt::Error;
 
     type Path = Self;
@@ -410,15 +415,19 @@ impl Printer for SymbolPath {
     type Type = Self;
     type DynExistential = Self;
 
+    fn tcx(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
+        self.tcx
+    }
+
     fn print_region(
-        self: PrintCx<'_, '_, '_, Self>,
+        self,
         _region: ty::Region<'_>,
     ) -> Result<Self::Region, Self::Error> {
-        self.ok()
+        Ok(self)
     }
 
     fn print_type(
-        self: PrintCx<'_, '_, 'tcx, Self>,
+        self,
         ty: Ty<'tcx>,
     ) -> Result<Self::Type, Self::Error> {
         match ty.sty {
@@ -436,7 +445,7 @@ impl Printer for SymbolPath {
     }
 
     fn print_dyn_existential(
-        mut self: PrintCx<'_, '_, 'tcx, Self>,
+        mut self,
         predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
     ) -> Result<Self::DynExistential, Self::Error> {
         let mut first = false;
@@ -445,20 +454,20 @@ impl Printer for SymbolPath {
                 write!(self, "+")?;
             }
             first = false;
-            self = PrintCx::new(self.tcx, p.print(self)?);
+            self = p.print(self)?;
         }
-        self.ok()
+        Ok(self)
     }
 
     fn path_crate(
-        mut self: PrintCx<'_, '_, '_, Self>,
+        mut self,
         cnum: CrateNum,
     ) -> Result<Self::Path, Self::Error> {
         self.write_str(&self.tcx.original_crate_name(cnum).as_str())?;
-        self.ok()
+        Ok(self)
     }
     fn path_qualified(
-        self: PrintCx<'_, '_, 'tcx, Self>,
+        self,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<Self::Path, Self::Error> {
@@ -480,11 +489,9 @@ impl Printer for SymbolPath {
         }
     }
 
-    fn path_append_impl<'gcx, 'tcx>(
-        self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, Self>,
-        ) -> Result<Self::Path, Self::Error>,
+    fn path_append_impl(
+        self,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<Self::Path, Self::Error> {
@@ -494,33 +501,29 @@ impl Printer for SymbolPath {
             trait_ref,
         )
     }
-    fn path_append<'gcx, 'tcx>(
-        self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, Self>,
-        ) -> Result<Self::Path, Self::Error>,
+    fn path_append(
+        mut self,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
         text: &str,
     ) -> Result<Self::Path, Self::Error> {
-        let mut path = print_prefix(self)?;
+        self = print_prefix(self)?;
 
-        if path.keep_within_component {
+        if self.keep_within_component {
             // HACK(eddyb) print the path similarly to how `FmtPrinter` prints it.
-            path.write_str("::")?;
+            self.write_str("::")?;
         } else {
-            path.finalize_pending_component();
+            self.path.finalize_pending_component();
         }
 
-        path.write_str(text)?;
-        Ok(path)
+        self.write_str(text)?;
+        Ok(self)
     }
-    fn path_generic_args<'gcx, 'tcx>(
-        mut self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        print_prefix: impl FnOnce(
-            PrintCx<'_, 'gcx, 'tcx, Self>,
-        ) -> Result<Self::Path, Self::Error>,
+    fn path_generic_args(
+        mut self,
+        print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
         args: &[Kind<'tcx>],
     )  -> Result<Self::Path, Self::Error> {
-        self = PrintCx::new(self.tcx, print_prefix(self)?);
+        self = print_prefix(self)?;
 
         let args = args.iter().cloned().filter(|arg| {
             match arg.unpack() {
@@ -532,52 +535,52 @@ impl Printer for SymbolPath {
         if args.clone().next().is_some() {
             self.generic_delimiters(|cx| cx.comma_sep(args))
         } else {
-            self.ok()
+            Ok(self)
         }
     }
 }
 
-impl PrettyPrinter for SymbolPath {
+impl PrettyPrinter<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> {
     fn region_should_not_be_omitted(
-        self: &PrintCx<'_, '_, '_, Self>,
+        &self,
         _region: ty::Region<'_>,
     ) -> bool {
         false
     }
     fn comma_sep<T>(
-        mut self: PrintCx<'_, '_, 'tcx, Self>,
+        mut self,
         mut elems: impl Iterator<Item = T>,
     ) -> Result<Self, Self::Error>
-        where T: Print<'tcx, Self, Output = Self, Error = Self::Error>
+        where T: Print<'tcx, 'tcx, Self, Output = Self, Error = Self::Error>
     {
         if let Some(first) = elems.next() {
-            self = PrintCx::new(self.tcx, first.print(self)?);
+            self = first.print(self)?;
             for elem in elems {
                 self.write_str(",")?;
-                self = PrintCx::new(self.tcx, elem.print(self)?);
+                self = elem.print(self)?;
             }
         }
-        self.ok()
+        Ok(self)
     }
 
-    fn generic_delimiters<'gcx, 'tcx>(
-        mut self: PrintCx<'_, 'gcx, 'tcx, Self>,
-        f: impl FnOnce(PrintCx<'_, 'gcx, 'tcx, Self>) -> Result<Self, Self::Error>,
+    fn generic_delimiters(
+        mut self,
+        f: impl FnOnce(Self) -> Result<Self, Self::Error>,
     ) -> Result<Self, Self::Error> {
         write!(self, "<")?;
 
         let kept_within_component =
             mem::replace(&mut self.keep_within_component, true);
-        let mut path = f(self)?;
-        path.keep_within_component = kept_within_component;
+        self = f(self)?;
+        self.keep_within_component = kept_within_component;
 
-        write!(path, ">")?;
+        write!(self, ">")?;
 
-        Ok(path)
+        Ok(self)
     }
 }
 
-impl fmt::Write for SymbolPath {
+impl fmt::Write for SymbolPrinter<'_, '_> {
     fn write_str(&mut self, s: &str) -> fmt::Result {
         // Name sanitation. LLVM will happily accept identifiers with weird names, but
         // gas doesn't!
@@ -586,45 +589,45 @@ impl fmt::Write for SymbolPath {
         // are replaced with '$' there.
 
         for c in s.chars() {
-            if self.temp_buf.is_empty() {
+            if self.path.temp_buf.is_empty() {
                 match c {
                     'a'..='z' | 'A'..='Z' | '_' => {}
                     _ => {
                         // Underscore-qualify anything that didn't start as an ident.
-                        self.temp_buf.push('_');
+                        self.path.temp_buf.push('_');
                     }
                 }
             }
             match c {
                 // Escape these with $ sequences
-                '@' => self.temp_buf.push_str("$SP$"),
-                '*' => self.temp_buf.push_str("$BP$"),
-                '&' => self.temp_buf.push_str("$RF$"),
-                '<' => self.temp_buf.push_str("$LT$"),
-                '>' => self.temp_buf.push_str("$GT$"),
-                '(' => self.temp_buf.push_str("$LP$"),
-                ')' => self.temp_buf.push_str("$RP$"),
-                ',' => self.temp_buf.push_str("$C$"),
-
-                '-' | ':' | '.' if self.strict_naming => {
+                '@' => self.path.temp_buf.push_str("$SP$"),
+                '*' => self.path.temp_buf.push_str("$BP$"),
+                '&' => self.path.temp_buf.push_str("$RF$"),
+                '<' => self.path.temp_buf.push_str("$LT$"),
+                '>' => self.path.temp_buf.push_str("$GT$"),
+                '(' => self.path.temp_buf.push_str("$LP$"),
+                ')' => self.path.temp_buf.push_str("$RP$"),
+                ',' => self.path.temp_buf.push_str("$C$"),
+
+                '-' | ':' | '.' if self.tcx.has_strict_asm_symbol_naming() => {
                     // NVPTX doesn't support these characters in symbol names.
-                    self.temp_buf.push('$')
+                    self.path.temp_buf.push('$')
                 }
 
                 // '.' doesn't occur in types and functions, so reuse it
                 // for ':' and '-'
-                '-' | ':' => self.temp_buf.push('.'),
+                '-' | ':' => self.path.temp_buf.push('.'),
 
                 // These are legal symbols
-                'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.' | '$' => self.temp_buf.push(c),
+                'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.' | '$' => self.path.temp_buf.push(c),
 
                 _ => {
-                    self.temp_buf.push('$');
+                    self.path.temp_buf.push('$');
                     for c in c.escape_unicode().skip(1) {
                         match c {
                             '{' => {}
-                            '}' => self.temp_buf.push('$'),
-                            c => self.temp_buf.push(c),
+                            '}' => self.path.temp_buf.push('$'),
+                            c => self.path.temp_buf.push(c),
                         }
                     }
                 }
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index d7dd0313e94..86134647324 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -2326,7 +2326,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     /// name where required.
     fn get_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String {
         let mut s = String::new();
-        let mut printer = ty::print::FmtPrinter::new(&mut s, Namespace::TypeNS);
+        let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
 
         // We need to add synthesized lifetimes where appropriate. We do
         // this by hooking into the pretty printer and telling it to label the
@@ -2341,7 +2341,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             _ => {}
         }
 
-        let _ = ty.print(ty::print::PrintCx::new(self.infcx.tcx, printer));
+        let _ = ty.print(printer);
         s
     }
 
@@ -2349,7 +2349,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     /// synthesized lifetime name where required.
     fn get_region_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String {
         let mut s = String::new();
-        let mut printer = ty::print::FmtPrinter::new(&mut s, Namespace::TypeNS);
+        let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
 
         let region = match ty.sty {
             ty::TyKind::Ref(region, _, _) => {
@@ -2366,7 +2366,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             _ => bug!("ty for annotation of borrow region is not a reference"),
         };
 
-        let _ = region.print(ty::print::PrintCx::new(self.infcx.tcx, printer));
+        let _ = region.print(printer);
         s
     }
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 6822de2bceb..53087eb1b1a 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -4223,13 +4223,18 @@ pub fn path_to_def(tcx: &TyCtxt<'_, '_, '_>, path: &[&str]) -> Option<DefId> {
     }
 }
 
-pub fn get_path_for_type<F>(tcx: TyCtxt<'_, '_, '_>, def_id: DefId, def_ctor: F) -> hir::Path
-where F: Fn(DefId) -> Def {
-    use rustc::ty::print::{PrintCx, Printer};
+pub fn get_path_for_type(
+    tcx: TyCtxt<'_, '_, '_>,
+    def_id: DefId,
+    def_ctor: impl Fn(DefId) -> Def,
+) -> hir::Path {
+    use rustc::ty::print::Printer;
 
-    struct AbsolutePathPrinter;
+    struct AbsolutePathPrinter<'a, 'tcx> {
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    }
 
-    impl Printer for AbsolutePathPrinter {
+    impl Printer<'tcx, 'tcx> for AbsolutePathPrinter<'_, 'tcx> {
         type Error = !;
 
         type Path = Vec<String>;
@@ -4237,35 +4242,39 @@ where F: Fn(DefId) -> Def {
         type Type = ();
         type DynExistential = ();
 
+        fn tcx(&'a self) -> TyCtxt<'a, 'tcx, 'tcx> {
+            self.tcx
+        }
+
         fn print_region(
-            self: PrintCx<'_, '_, '_, Self>,
+            self,
             _region: ty::Region<'_>,
         ) -> Result<Self::Region, Self::Error> {
             Ok(())
         }
 
         fn print_type(
-            self: PrintCx<'_, '_, 'tcx, Self>,
+            self,
             _ty: Ty<'tcx>,
         ) -> Result<Self::Type, Self::Error> {
             Ok(())
         }
 
-        fn print_dyn_existential<'tcx>(
-            self: PrintCx<'_, '_, 'tcx, Self>,
+        fn print_dyn_existential(
+            self,
             _predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
         ) -> Result<Self::DynExistential, Self::Error> {
             Ok(())
         }
 
         fn path_crate(
-            self: PrintCx<'_, '_, '_, Self>,
+            self,
             cnum: CrateNum,
         ) -> Result<Self::Path, Self::Error> {
             Ok(vec![self.tcx.original_crate_name(cnum).to_string()])
         }
         fn path_qualified(
-            self: PrintCx<'_, '_, 'tcx, Self>,
+            self,
             self_ty: Ty<'tcx>,
             trait_ref: Option<ty::TraitRef<'tcx>>,
         ) -> Result<Self::Path, Self::Error> {
@@ -4276,11 +4285,9 @@ where F: Fn(DefId) -> Def {
             }])
         }
 
-        fn path_append_impl<'gcx, 'tcx>(
-            self: PrintCx<'_, 'gcx, 'tcx, Self>,
-            print_prefix: impl FnOnce(
-                PrintCx<'_, 'gcx, 'tcx, Self>,
-            ) -> Result<Self::Path, Self::Error>,
+        fn path_append_impl(
+            self,
+            print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
             self_ty: Ty<'tcx>,
             trait_ref: Option<ty::TraitRef<'tcx>>,
         ) -> Result<Self::Path, Self::Error> {
@@ -4296,29 +4303,25 @@ where F: Fn(DefId) -> Def {
 
             Ok(path)
         }
-        fn path_append<'gcx, 'tcx>(
-            self: PrintCx<'_, 'gcx, 'tcx, Self>,
-            print_prefix: impl FnOnce(
-                PrintCx<'_, 'gcx, 'tcx, Self>,
-            ) -> Result<Self::Path, Self::Error>,
+        fn path_append(
+            self,
+            print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
             text: &str,
         ) -> Result<Self::Path, Self::Error> {
             let mut path = print_prefix(self)?;
             path.push(text.to_string());
             Ok(path)
         }
-        fn path_generic_args<'gcx, 'tcx>(
-            self: PrintCx<'_, 'gcx, 'tcx, Self>,
-            print_prefix: impl FnOnce(
-                PrintCx<'_, 'gcx, 'tcx, Self>,
-            ) -> Result<Self::Path, Self::Error>,
+        fn path_generic_args(
+            self,
+            print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
             _args: &[Kind<'tcx>],
         ) -> Result<Self::Path, Self::Error> {
             print_prefix(self)
         }
     }
 
-    let names = PrintCx::new(tcx, AbsolutePathPrinter)
+    let names = AbsolutePathPrinter { tcx: tcx.global_tcx() }
         .print_def_path(def_id, None)
         .unwrap();