diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 1436e51f31820..f9d1666977134 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -118,7 +118,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                 span: Span::dummy(),
                 unsafety: hir::Unsafety::Normal,
                 generics: new_generics,
-                trait_: Some(trait_ref.clean(self.cx).get_trait_type().unwrap()),
+                trait_: Some(trait_ref.clean(self.cx)),
                 for_: ty.clean(self.cx),
                 items: Vec::new(),
                 negative_polarity,
@@ -166,16 +166,16 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
             .clone()
     }
 
-    // This method calculates two things: Lifetime constraints of the form 'a: 'b,
-    // and region constraints of the form ReVar: 'a
-    //
-    // This is essentially a simplified version of lexical_region_resolve. However,
-    // handle_lifetimes determines what *needs be* true in order for an impl to hold.
-    // lexical_region_resolve, along with much of the rest of the compiler, is concerned
-    // with determining if a given set up constraints/predicates *are* met, given some
-    // starting conditions (e.g., user-provided code). For this reason, it's easier
-    // to perform the calculations we need on our own, rather than trying to make
-    // existing inference/solver code do what we want.
+    /// This method calculates two things: Lifetime constraints of the form `'a: 'b`,
+    /// and region constraints of the form `RegionVid: 'a`
+    ///
+    /// This is essentially a simplified version of lexical_region_resolve. However,
+    /// handle_lifetimes determines what *needs be* true in order for an impl to hold.
+    /// lexical_region_resolve, along with much of the rest of the compiler, is concerned
+    /// with determining if a given set up constraints/predicates *are* met, given some
+    /// starting conditions (e.g., user-provided code). For this reason, it's easier
+    /// to perform the calculations we need on our own, rather than trying to make
+    /// existing inference/solver code do what we want.
     fn handle_lifetimes<'cx>(
         regions: &RegionConstraintData<'cx>,
         names_map: &FxHashMap<Symbol, Lifetime>,
@@ -353,48 +353,35 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                 if let Some(data) = ty_to_fn.get(&ty) {
                     let (poly_trait, output) =
                         (data.0.as_ref().unwrap().clone(), data.1.as_ref().cloned().map(Box::new));
-                    let new_ty = match poly_trait.trait_ {
-                        Type::ResolvedPath { ref path, ref did } => {
-                            let mut new_path = path.clone();
-                            let last_segment =
-                                new_path.segments.pop().expect("segments were empty");
-
-                            let (old_input, old_output) = match last_segment.args {
-                                GenericArgs::AngleBracketed { args, .. } => {
-                                    let types = args
-                                        .iter()
-                                        .filter_map(|arg| match arg {
-                                            GenericArg::Type(ty) => Some(ty.clone()),
-                                            _ => None,
-                                        })
-                                        .collect();
-                                    (types, None)
-                                }
-                                GenericArgs::Parenthesized { inputs, output, .. } => {
-                                    (inputs, output)
-                                }
-                            };
+                    let mut new_path = poly_trait.trait_.clone();
+                    let last_segment = new_path.segments.pop().expect("segments were empty");
+
+                    let (old_input, old_output) = match last_segment.args {
+                        GenericArgs::AngleBracketed { args, .. } => {
+                            let types = args
+                                .iter()
+                                .filter_map(|arg| match arg {
+                                    GenericArg::Type(ty) => Some(ty.clone()),
+                                    _ => None,
+                                })
+                                .collect();
+                            (types, None)
+                        }
+                        GenericArgs::Parenthesized { inputs, output } => (inputs, output),
+                    };
 
-                            if old_output.is_some() && old_output != output {
-                                panic!(
-                                    "Output mismatch for {:?} {:?} {:?}",
-                                    ty, old_output, data.1
-                                );
-                            }
+                    if old_output.is_some() && old_output != output {
+                        panic!("Output mismatch for {:?} {:?} {:?}", ty, old_output, data.1);
+                    }
 
-                            let new_params =
-                                GenericArgs::Parenthesized { inputs: old_input, output };
+                    let new_params = GenericArgs::Parenthesized { inputs: old_input, output };
 
-                            new_path
-                                .segments
-                                .push(PathSegment { name: last_segment.name, args: new_params });
+                    new_path
+                        .segments
+                        .push(PathSegment { name: last_segment.name, args: new_params });
 
-                            Type::ResolvedPath { path: new_path, did: *did }
-                        }
-                        _ => panic!("Unexpected data: {:?}, {:?}", ty, data),
-                    };
                     bounds.insert(GenericBound::TraitBound(
-                        PolyTrait { trait_: new_ty, generic_params: poly_trait.generic_params },
+                        PolyTrait { trait_: new_path, generic_params: poly_trait.generic_params },
                         hir::TraitBoundModifier::None,
                     ));
                 }
@@ -423,15 +410,15 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
             .collect()
     }
 
-    // Converts the calculated ParamEnv and lifetime information to a clean::Generics, suitable for
-    // display on the docs page. Cleaning the Predicates produces sub-optimal `WherePredicate`s,
-    // so we fix them up:
-    //
-    // * Multiple bounds for the same type are coalesced into one: e.g., 'T: Copy', 'T: Debug'
-    // becomes 'T: Copy + Debug'
-    // * Fn bounds are handled specially - instead of leaving it as 'T: Fn(), <T as Fn::Output> =
-    // K', we use the dedicated syntax 'T: Fn() -> K'
-    // * We explicitly add a '?Sized' bound if we didn't find any 'Sized' predicates for a type
+    /// Converts the calculated `ParamEnv` and lifetime information to a [`clean::Generics`](Generics), suitable for
+    /// display on the docs page. Cleaning the `Predicates` produces sub-optimal [`WherePredicate`]s,
+    /// so we fix them up:
+    ///
+    /// * Multiple bounds for the same type are coalesced into one: e.g., `T: Copy`, `T: Debug`
+    /// becomes `T: Copy + Debug`
+    /// * `Fn` bounds are handled specially - instead of leaving it as `T: Fn(), <T as Fn::Output> =
+    /// K`, we use the dedicated syntax `T: Fn() -> K`
+    /// * We explicitly add a `?Sized` bound if we didn't find any `Sized` predicates for a type
     fn param_env_to_generics(
         &mut self,
         item_def_id: DefId,
@@ -476,7 +463,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
         let mut has_sized = FxHashSet::default();
         let mut ty_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default();
         let mut lifetime_to_bounds: FxHashMap<_, FxHashSet<_>> = Default::default();
-        let mut ty_to_traits: FxHashMap<Type, FxHashSet<Type>> = Default::default();
+        let mut ty_to_traits: FxHashMap<Type, FxHashSet<Path>> = Default::default();
 
         let mut ty_to_fn: FxHashMap<Type, (Option<PolyTrait>, Option<Type>)> = Default::default();
 
@@ -511,11 +498,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                     if b.is_sized_bound(self.cx) {
                         has_sized.insert(ty.clone());
                     } else if !b
-                        .get_trait_type()
-                        .and_then(|t| {
+                        .get_trait_path()
+                        .and_then(|trait_| {
                             ty_to_traits
                                 .get(&ty)
-                                .map(|bounds| bounds.contains(&strip_type(t.clone())))
+                                .map(|bounds| bounds.contains(&strip_path_generics(trait_.clone())))
                         })
                         .unwrap_or(false)
                     {
@@ -532,7 +519,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                                 // that we don't end up with duplicate bounds (e.g., for<'b, 'b>)
                                 for_generics.extend(p.generic_params.clone());
                                 p.generic_params = for_generics.into_iter().collect();
-                                self.is_fn_ty(&p.trait_)
+                                self.is_fn_trait(&p.trait_)
                             }
                             _ => false,
                         };
@@ -558,78 +545,59 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                     match lhs {
                         Type::QPath { name: left_name, ref self_type, ref trait_, .. } => {
                             let ty = &*self_type;
-                            match **trait_ {
-                                Type::ResolvedPath { path: ref trait_path, ref did } => {
-                                    let mut new_trait_path = trait_path.clone();
-
-                                    if self.is_fn_ty(trait_) && left_name == sym::Output {
-                                        ty_to_fn
-                                            .entry(*ty.clone())
-                                            .and_modify(|e| *e = (e.0.clone(), Some(rhs.clone())))
-                                            .or_insert((None, Some(rhs)));
-                                        continue;
-                                    }
-
-                                    let args = &mut new_trait_path
-                                        .segments
-                                        .last_mut()
-                                        .expect("segments were empty")
-                                        .args;
-
-                                    match args {
-                                        // Convert something like '<T as Iterator::Item> = u8'
-                                        // to 'T: Iterator<Item=u8>'
-                                        GenericArgs::AngleBracketed {
-                                            ref mut bindings, ..
-                                        } => {
-                                            bindings.push(TypeBinding {
-                                                name: left_name,
-                                                kind: TypeBindingKind::Equality { ty: rhs },
-                                            });
-                                        }
-                                        GenericArgs::Parenthesized { .. } => {
-                                            existing_predicates.push(WherePredicate::EqPredicate {
-                                                lhs: lhs.clone(),
-                                                rhs,
-                                            });
-                                            continue; // If something other than a Fn ends up
-                                            // with parenthesis, leave it alone
-                                        }
-                                    }
-
-                                    let bounds = ty_to_bounds.entry(*ty.clone()).or_default();
-
-                                    bounds.insert(GenericBound::TraitBound(
-                                        PolyTrait {
-                                            trait_: Type::ResolvedPath {
-                                                path: new_trait_path,
-                                                did: *did,
-                                            },
-                                            generic_params: Vec::new(),
-                                        },
-                                        hir::TraitBoundModifier::None,
-                                    ));
-
-                                    // Remove any existing 'plain' bound (e.g., 'T: Iterator`) so
-                                    // that we don't see a
-                                    // duplicate bound like `T: Iterator + Iterator<Item=u8>`
-                                    // on the docs page.
-                                    bounds.remove(&GenericBound::TraitBound(
-                                        PolyTrait {
-                                            trait_: *trait_.clone(),
-                                            generic_params: Vec::new(),
-                                        },
-                                        hir::TraitBoundModifier::None,
-                                    ));
-                                    // Avoid creating any new duplicate bounds later in the outer
-                                    // loop
-                                    ty_to_traits
-                                        .entry(*ty.clone())
-                                        .or_default()
-                                        .insert(*trait_.clone());
+                            let mut new_trait = trait_.clone();
+
+                            if self.is_fn_trait(trait_) && left_name == sym::Output {
+                                ty_to_fn
+                                    .entry(*ty.clone())
+                                    .and_modify(|e| *e = (e.0.clone(), Some(rhs.clone())))
+                                    .or_insert((None, Some(rhs)));
+                                continue;
+                            }
+
+                            let args = &mut new_trait
+                                .segments
+                                .last_mut()
+                                .expect("segments were empty")
+                                .args;
+
+                            match args {
+                                // Convert something like '<T as Iterator::Item> = u8'
+                                // to 'T: Iterator<Item=u8>'
+                                GenericArgs::AngleBracketed { ref mut bindings, .. } => {
+                                    bindings.push(TypeBinding {
+                                        name: left_name,
+                                        kind: TypeBindingKind::Equality { ty: rhs },
+                                    });
+                                }
+                                GenericArgs::Parenthesized { .. } => {
+                                    existing_predicates.push(WherePredicate::EqPredicate {
+                                        lhs: lhs.clone(),
+                                        rhs,
+                                    });
+                                    continue; // If something other than a Fn ends up
+                                    // with parenthesis, leave it alone
                                 }
-                                _ => panic!("Unexpected trait {:?} for {:?}", trait_, item_def_id),
                             }
+
+                            let bounds = ty_to_bounds.entry(*ty.clone()).or_default();
+
+                            bounds.insert(GenericBound::TraitBound(
+                                PolyTrait { trait_: new_trait, generic_params: Vec::new() },
+                                hir::TraitBoundModifier::None,
+                            ));
+
+                            // Remove any existing 'plain' bound (e.g., 'T: Iterator`) so
+                            // that we don't see a
+                            // duplicate bound like `T: Iterator + Iterator<Item=u8>`
+                            // on the docs page.
+                            bounds.remove(&GenericBound::TraitBound(
+                                PolyTrait { trait_: trait_.clone(), generic_params: Vec::new() },
+                                hir::TraitBoundModifier::None,
+                            ));
+                            // Avoid creating any new duplicate bounds later in the outer
+                            // loop
+                            ty_to_traits.entry(*ty.clone()).or_default().insert(trait_.clone());
                         }
                         _ => panic!("Unexpected LHS {:?} for {:?}", lhs, item_def_id),
                     }
@@ -664,11 +632,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
         Generics { params: generic_params, where_predicates: existing_predicates }
     }
 
-    // Ensure that the predicates are in a consistent order. The precise
-    // ordering doesn't actually matter, but it's important that
-    // a given set of predicates always appears in the same order -
-    // both for visual consistency between 'rustdoc' runs, and to
-    // make writing tests much easier
+    /// Ensure that the predicates are in a consistent order. The precise
+    /// ordering doesn't actually matter, but it's important that
+    /// a given set of predicates always appears in the same order -
+    /// both for visual consistency between 'rustdoc' runs, and to
+    /// make writing tests much easier
     #[inline]
     fn sort_where_predicates(&self, mut predicates: &mut Vec<WherePredicate>) {
         // We should never have identical bounds - and if we do,
@@ -677,11 +645,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
         self.unstable_debug_sort(&mut predicates);
     }
 
-    // Ensure that the bounds are in a consistent order. The precise
-    // ordering doesn't actually matter, but it's important that
-    // a given set of bounds always appears in the same order -
-    // both for visual consistency between 'rustdoc' runs, and to
-    // make writing tests much easier
+    /// Ensure that the bounds are in a consistent order. The precise
+    /// ordering doesn't actually matter, but it's important that
+    /// a given set of bounds always appears in the same order -
+    /// both for visual consistency between 'rustdoc' runs, and to
+    /// make writing tests much easier
     #[inline]
     fn sort_where_bounds(&self, mut bounds: &mut Vec<GenericBound>) {
         // We should never have identical bounds - and if we do,
@@ -690,47 +658,43 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
         self.unstable_debug_sort(&mut bounds);
     }
 
-    // This might look horrendously hacky, but it's actually not that bad.
-    //
-    // For performance reasons, we use several different FxHashMaps
-    // in the process of computing the final set of where predicates.
-    // However, the iteration order of a HashMap is completely unspecified.
-    // In fact, the iteration of an FxHashMap can even vary between platforms,
-    // since FxHasher has different behavior for 32-bit and 64-bit platforms.
-    //
-    // Obviously, it's extremely undesirable for documentation rendering
-    // to be dependent on the platform it's run on. Apart from being confusing
-    // to end users, it makes writing tests much more difficult, as predicates
-    // can appear in any order in the final result.
-    //
-    // To solve this problem, we sort WherePredicates and GenericBounds
-    // by their Debug string. The thing to keep in mind is that we don't really
-    // care what the final order is - we're synthesizing an impl or bound
-    // ourselves, so any order can be considered equally valid. By sorting the
-    // predicates and bounds, however, we ensure that for a given codebase, all
-    // auto-trait impls always render in exactly the same way.
-    //
-    // Using the Debug implementation for sorting prevents us from needing to
-    // write quite a bit of almost entirely useless code (e.g., how should two
-    // Types be sorted relative to each other). It also allows us to solve the
-    // problem for both WherePredicates and GenericBounds at the same time. This
-    // approach is probably somewhat slower, but the small number of items
-    // involved (impls rarely have more than a few bounds) means that it
-    // shouldn't matter in practice.
+    /// This might look horrendously hacky, but it's actually not that bad.
+    ///
+    /// For performance reasons, we use several different FxHashMaps
+    /// in the process of computing the final set of where predicates.
+    /// However, the iteration order of a HashMap is completely unspecified.
+    /// In fact, the iteration of an FxHashMap can even vary between platforms,
+    /// since FxHasher has different behavior for 32-bit and 64-bit platforms.
+    ///
+    /// Obviously, it's extremely undesirable for documentation rendering
+    /// to be dependent on the platform it's run on. Apart from being confusing
+    /// to end users, it makes writing tests much more difficult, as predicates
+    /// can appear in any order in the final result.
+    ///
+    /// To solve this problem, we sort WherePredicates and GenericBounds
+    /// by their Debug string. The thing to keep in mind is that we don't really
+    /// care what the final order is - we're synthesizing an impl or bound
+    /// ourselves, so any order can be considered equally valid. By sorting the
+    /// predicates and bounds, however, we ensure that for a given codebase, all
+    /// auto-trait impls always render in exactly the same way.
+    ///
+    /// Using the Debug implementation for sorting prevents us from needing to
+    /// write quite a bit of almost entirely useless code (e.g., how should two
+    /// Types be sorted relative to each other). It also allows us to solve the
+    /// problem for both WherePredicates and GenericBounds at the same time. This
+    /// approach is probably somewhat slower, but the small number of items
+    /// involved (impls rarely have more than a few bounds) means that it
+    /// shouldn't matter in practice.
     fn unstable_debug_sort<T: Debug>(&self, vec: &mut Vec<T>) {
         vec.sort_by_cached_key(|x| format!("{:?}", x))
     }
 
-    fn is_fn_ty(&self, ty: &Type) -> bool {
+    fn is_fn_trait(&self, path: &Path) -> bool {
         let tcx = self.cx.tcx;
-        match ty {
-            &Type::ResolvedPath { did, .. } => {
-                did == tcx.require_lang_item(LangItem::Fn, None)
-                    || did == tcx.require_lang_item(LangItem::FnMut, None)
-                    || did == tcx.require_lang_item(LangItem::FnOnce, None)
-            }
-            _ => false,
-        }
+        let did = path.def_id();
+        did == tcx.require_lang_item(LangItem::Fn, None)
+            || did == tcx.require_lang_item(LangItem::FnMut, None)
+            || did == tcx.require_lang_item(LangItem::FnOnce, None)
     }
 }
 
@@ -741,7 +705,7 @@ fn region_name(region: Region<'_>) -> Option<Symbol> {
     }
 }
 
-// Replaces all ReVars in a type with ty::Region's, using the provided map
+/// Replaces all [`ty::RegionVid`]s in a type with [`ty::Region`]s, using the provided map.
 struct RegionReplacer<'a, 'tcx> {
     vid_to_region: &'a FxHashMap<ty::RegionVid, ty::Region<'tcx>>,
     tcx: TyCtxt<'tcx>,
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 8135d4a2085dd..340d9f311b150 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -114,7 +114,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                             .clean(self.cx),
                         // FIXME(eddyb) compute both `trait_` and `for_` from
                         // the post-inference `trait_ref`, as it's more accurate.
-                        trait_: Some(trait_ref.clean(self.cx).get_trait_type().unwrap()),
+                        trait_: Some(trait_ref.clean(self.cx)),
                         for_: ty.clean(self.cx),
                         items: self
                             .cx
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 4a888b22332ee..ebf61fd104843 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -446,20 +446,26 @@ crate fn build_impl(
         ),
     };
     let polarity = tcx.impl_polarity(did);
-    let trait_ = associated_trait.clean(cx).map(|bound| match bound {
-        clean::GenericBound::TraitBound(polyt, _) => polyt.trait_,
-        clean::GenericBound::Outlives(..) => unreachable!(),
-    });
-    if trait_.def_id() == tcx.lang_items().deref_trait() {
+    let trait_ = associated_trait.clean(cx);
+    if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() {
         super::build_deref_target_impls(cx, &trait_items, ret);
     }
 
     // Return if the trait itself or any types of the generic parameters are doc(hidden).
-    let mut stack: Vec<&Type> = trait_.iter().collect();
-    stack.push(&for_);
+    let mut stack: Vec<&Type> = vec![&for_];
+
+    if let Some(did) = trait_.as_ref().map(|t| t.def_id()) {
+        if tcx.get_attrs(did).lists(sym::doc).has_word(sym::hidden) {
+            return;
+        }
+    }
+    if let Some(generics) = trait_.as_ref().and_then(|t| t.generics()) {
+        stack.extend(generics);
+    }
+
     while let Some(ty) = stack.pop() {
         if let Some(did) = ty.def_id() {
-            if cx.tcx.get_attrs(did).lists(sym::doc).has_word(sym::hidden) {
+            if tcx.get_attrs(did).lists(sym::doc).has_word(sym::hidden) {
                 return;
             }
         }
@@ -468,14 +474,14 @@ crate fn build_impl(
         }
     }
 
-    if let Some(trait_did) = trait_.def_id() {
-        record_extern_trait(cx, trait_did);
+    if let Some(did) = trait_.as_ref().map(|t| t.def_id()) {
+        record_extern_trait(cx, did);
     }
 
     let (merged_attrs, cfg) = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs);
     trace!("merged_attrs={:?}", merged_attrs);
 
-    trace!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
+    trace!("build_impl: impl {:?} for {:?}", trait_.as_ref().map(|t| t.def_id()), for_.def_id());
     ret.push(clean::Item::from_def_id_and_attrs_and_parts(
         did,
         None,
@@ -526,7 +532,6 @@ fn build_module(
                         item.ident.name,
                         clean::ImportSource {
                             path: clean::Path {
-                                global: false,
                                 res,
                                 segments: vec![clean::PathSegment {
                                     name: prim_ty.as_sym(),
@@ -621,11 +626,10 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
                 ref mut bounds,
                 ..
             } if *s == kw::SelfUpper => {
-                bounds.retain(|bound| match *bound {
-                    clean::GenericBound::TraitBound(
-                        clean::PolyTrait { trait_: clean::ResolvedPath { did, .. }, .. },
-                        _,
-                    ) => did != trait_did,
+                bounds.retain(|bound| match bound {
+                    clean::GenericBound::TraitBound(clean::PolyTrait { trait_, .. }, _) => {
+                        trait_.def_id() != trait_did
+                    }
                     _ => true,
                 });
             }
@@ -633,18 +637,12 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
         }
     }
 
-    g.where_predicates.retain(|pred| match *pred {
+    g.where_predicates.retain(|pred| match pred {
         clean::WherePredicate::BoundPredicate {
-            ty:
-                clean::QPath {
-                    self_type: box clean::Generic(ref s),
-                    trait_: box clean::ResolvedPath { did, .. },
-                    name: ref _name,
-                    ..
-                },
-            ref bounds,
+            ty: clean::QPath { self_type: box clean::Generic(ref s), trait_, name: _, .. },
+            bounds,
             ..
-        } => !(bounds.is_empty() || *s == kw::SelfUpper && did == trait_did),
+        } => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did),
         _ => true,
     });
     g
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 49fc93f3feabc..668088bb0a59a 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -152,8 +152,8 @@ impl Clean<GenericBound> for hir::GenericBound<'_> {
     }
 }
 
-impl Clean<Type> for (ty::TraitRef<'_>, &[TypeBinding]) {
-    fn clean(&self, cx: &mut DocContext<'_>) -> Type {
+impl Clean<Path> for (ty::TraitRef<'_>, &[TypeBinding]) {
+    fn clean(&self, cx: &mut DocContext<'_>) -> Path {
         let (trait_ref, bounds) = *self;
         let kind = cx.tcx.def_kind(trait_ref.def_id).into();
         if !matches!(kind, ItemType::Trait | ItemType::TraitAlias) {
@@ -168,16 +168,13 @@ impl Clean<Type> for (ty::TraitRef<'_>, &[TypeBinding]) {
 
         debug!("ty::TraitRef\n  subst: {:?}\n", trait_ref.substs);
 
-        ResolvedPath { path, did: trait_ref.def_id }
+        path
     }
 }
 
-impl<'tcx> Clean<GenericBound> for ty::TraitRef<'tcx> {
-    fn clean(&self, cx: &mut DocContext<'_>) -> GenericBound {
-        GenericBound::TraitBound(
-            PolyTrait { trait_: (*self, &[][..]).clean(cx), generic_params: vec![] },
-            hir::TraitBoundModifier::None,
-        )
+impl Clean<Path> for ty::TraitRef<'tcx> {
+    fn clean(&self, cx: &mut DocContext<'_>) -> Path {
+        (*self, &[][..]).clean(cx)
     }
 }
 
@@ -384,16 +381,13 @@ impl<'tcx> Clean<WherePredicate> for ty::ProjectionPredicate<'tcx> {
 impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
     fn clean(&self, cx: &mut DocContext<'_>) -> Type {
         let lifted = self.lift_to_tcx(cx.tcx).unwrap();
-        let trait_ = match lifted.trait_ref(cx.tcx).clean(cx) {
-            GenericBound::TraitBound(t, _) => t.trait_,
-            GenericBound::Outlives(_) => panic!("cleaning a trait got a lifetime"),
-        };
+        let trait_ = lifted.trait_ref(cx.tcx).clean(cx);
         let self_type = self.self_ty().clean(cx);
         Type::QPath {
             name: cx.tcx.associated_item(self.item_def_id).ident.name,
             self_def_id: self_type.def_id(),
             self_type: box self_type,
-            trait_: box trait_,
+            trait_,
         }
     }
 }
@@ -896,10 +890,11 @@ impl Clean<bool> for hir::IsAuto {
     }
 }
 
-impl Clean<Type> for hir::TraitRef<'_> {
-    fn clean(&self, cx: &mut DocContext<'_>) -> Type {
+impl Clean<Path> for hir::TraitRef<'_> {
+    fn clean(&self, cx: &mut DocContext<'_>) -> Path {
         let path = self.path.clean(cx);
-        resolve_type(cx, path)
+        register_res(cx, path.res);
+        path
     }
 }
 
@@ -1105,9 +1100,8 @@ impl Clean<Item> for ty::AssocItem {
                             if *name != my_name {
                                 return None;
                             }
-                            match **trait_ {
-                                ResolvedPath { did, .. } if did == self.container.id() => {}
-                                _ => return None,
+                            if trait_.def_id() != self.container.id() {
+                                return None;
                             }
                             match **self_type {
                                 Generic(ref s) if *s == kw::SelfUpper => {}
@@ -1273,19 +1267,18 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
                 return normalized_value.clean(cx);
             }
 
-            let segments = if p.is_global() { &p.segments[1..] } else { &p.segments };
-            let trait_segments = &segments[..segments.len() - 1];
+            let trait_segments = &p.segments[..p.segments.len() - 1];
             let trait_def = cx.tcx.associated_item(p.res.def_id()).container.id();
-            let trait_path = self::Path {
-                global: p.is_global(),
+            let trait_ = self::Path {
                 res: Res::Def(DefKind::Trait, trait_def),
                 segments: trait_segments.clean(cx),
             };
+            register_res(cx, trait_.res);
             Type::QPath {
                 name: p.segments.last().expect("segments were empty").ident.name,
                 self_def_id: Some(DefId::local(qself.hir_id.owner.local_def_index)),
                 self_type: box qself.clean(cx),
-                trait_: box resolve_type(cx, trait_path),
+                trait_,
             }
         }
         hir::QPath::TypeRelative(ref qself, ref segment) => {
@@ -1296,12 +1289,13 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
                 ty::Error(_) => return Type::Infer,
                 _ => bug!("clean: expected associated type, found `{:?}`", ty),
             };
-            let trait_path = hir::Path { span, res, segments: &[] }.clean(cx);
+            let trait_ = hir::Path { span, res, segments: &[] }.clean(cx);
+            register_res(cx, trait_.res);
             Type::QPath {
                 name: segment.ident.name,
                 self_def_id: res.opt_def_id(),
                 self_type: box qself.clean(cx),
-                trait_: box resolve_type(cx, trait_path),
+                trait_,
             }
         }
         hir::QPath::LangItem(..) => bug!("clean: requiring documentation of lang item"),
@@ -1470,10 +1464,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
                     let empty = cx.tcx.intern_substs(&[]);
                     let path = external_path(cx, did, false, vec![], empty);
                     inline::record_extern_fqn(cx, did, ItemType::Trait);
-                    let bound = PolyTrait {
-                        trait_: ResolvedPath { path, did },
-                        generic_params: Vec::new(),
-                    };
+                    let bound = PolyTrait { trait_: path, generic_params: Vec::new() };
                     bounds.push(bound);
                 }
 
@@ -1486,10 +1477,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
                 }
 
                 let path = external_path(cx, did, false, bindings, substs);
-                bounds.insert(
-                    0,
-                    PolyTrait { trait_: ResolvedPath { path, did }, generic_params: Vec::new() },
-                );
+                bounds.insert(0, PolyTrait { trait_: path, generic_params: Vec::new() });
 
                 DynTrait(bounds, lifetime)
             }
@@ -1728,11 +1716,7 @@ impl Clean<Variant> for hir::VariantData<'_> {
 
 impl Clean<Path> for hir::Path<'_> {
     fn clean(&self, cx: &mut DocContext<'_>) -> Path {
-        Path {
-            global: self.is_global(),
-            res: self.res,
-            segments: if self.is_global() { &self.segments[1..] } else { &self.segments }.clean(cx),
-        }
+        Path { res: self.res, segments: self.segments.clean(cx) }
     }
 }
 
@@ -1898,7 +1882,7 @@ fn clean_impl(impl_: &hir::Impl<'_>, hir_id: hir::HirId, cx: &mut DocContext<'_>
 
     // If this impl block is an implementation of the Deref trait, then we
     // need to try inlining the target's inherent impl blocks as well.
-    if trait_.def_id() == tcx.lang_items().deref_trait() {
+    if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() {
         build_deref_target_impls(cx, &items, &mut ret);
     }
 
@@ -1907,7 +1891,7 @@ fn clean_impl(impl_: &hir::Impl<'_>, hir_id: hir::HirId, cx: &mut DocContext<'_>
         DefKind::TyAlias => Some(tcx.type_of(did).clean(cx)),
         _ => None,
     });
-    let mut make_item = |trait_: Option<Type>, for_: Type, items: Vec<Item>| {
+    let mut make_item = |trait_: Option<Path>, for_: Type, items: Vec<Item>| {
         let kind = ImplItem(Impl {
             span: types::rustc_span(tcx.hir().local_def_id(hir_id).to_def_id(), tcx),
             unsafety: impl_.unsafety,
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index 257af6ab91016..4c81e75e8d630 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -99,17 +99,13 @@ crate fn merge_bounds(
             clean::GenericBound::TraitBound(ref mut tr, _) => tr,
             clean::GenericBound::Outlives(..) => return false,
         };
-        let (did, path) = match trait_ref.trait_ {
-            clean::ResolvedPath { did, ref mut path, .. } => (did, path),
-            _ => return false,
-        };
         // If this QPath's trait `trait_did` is the same as, or a supertrait
         // of, the bound's trait `did` then we can keep going, otherwise
         // this is just a plain old equality bound.
-        if !trait_is_same_or_supertrait(cx, did, trait_did) {
+        if !trait_is_same_or_supertrait(cx, trait_ref.trait_.def_id(), trait_did) {
             return false;
         }
-        let last = path.segments.last_mut().expect("segments were empty");
+        let last = trait_ref.trait_.segments.last_mut().expect("segments were empty");
         match last.args {
             PP::AngleBracketed { ref mut bindings, .. } => {
                 bindings.push(clean::TypeBinding {
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 248ff339514ed..2e09768a8ef75 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -120,8 +120,7 @@ crate struct Crate {
     crate module: Item,
     crate externs: Vec<ExternalCrate>,
     crate primitives: ThinVec<(DefId, PrimitiveType)>,
-    // These are later on moved into `CACHEKEY`, leaving the map empty.
-    // Only here so that they can be filtered through the rustdoc passes.
+    /// Only here so that they can be filtered through the rustdoc passes.
     crate external_traits: Rc<RefCell<FxHashMap<DefId, TraitWithExtraInfo>>>,
     crate collapsed: bool,
 }
@@ -1114,7 +1113,7 @@ impl GenericBound {
         let path = external_path(cx, did, false, vec![], empty);
         inline::record_extern_fqn(cx, did, ItemType::Trait);
         GenericBound::TraitBound(
-            PolyTrait { trait_: ResolvedPath { path, did }, generic_params: Vec::new() },
+            PolyTrait { trait_: path, generic_params: Vec::new() },
             hir::TraitBoundModifier::Maybe,
         )
     }
@@ -1122,7 +1121,7 @@ impl GenericBound {
     crate fn is_sized_bound(&self, cx: &DocContext<'_>) -> bool {
         use rustc_hir::TraitBoundModifier as TBM;
         if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self {
-            if trait_.def_id() == cx.tcx.lang_items().sized_trait() {
+            if Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait() {
                 return true;
             }
         }
@@ -1136,7 +1135,7 @@ impl GenericBound {
         None
     }
 
-    crate fn get_trait_type(&self) -> Option<Type> {
+    crate fn get_trait_path(&self) -> Option<Path> {
         if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self {
             Some(trait_.clone())
         } else {
@@ -1368,53 +1367,53 @@ crate struct TraitAlias {
 /// A trait reference, which may have higher ranked lifetimes.
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 crate struct PolyTrait {
-    crate trait_: Type,
+    crate trait_: Path,
     crate generic_params: Vec<GenericParamDef>,
 }
 
-/// A representation of a type suitable for hyperlinking purposes. Ideally, one can get the original
-/// type out of the AST/`TyCtxt` given one of these, if more information is needed. Most
-/// importantly, it does not preserve mutability or boxes.
+/// Rustdoc's representation of types, mostly based on the [`hir::Ty`].
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 crate enum Type {
-    /// Structs/enums/traits (most that would be an `hir::TyKind::Path`).
-    ResolvedPath {
-        path: Path,
-        did: DefId,
-    },
-    /// `dyn for<'a> Trait<'a> + Send + 'static`
+    /// A named type, which could be a trait.
+    ///
+    /// This is mostly Rustdoc's version of [`hir::Path`]. It has to be different because Rustdoc's [`PathSegment`] can contain cleaned generics.
+    ResolvedPath { path: Path, did: DefId },
+    /// A `dyn Trait` object: `dyn for<'a> Trait<'a> + Send + 'static`
     DynTrait(Vec<PolyTrait>, Option<Lifetime>),
-    /// For parameterized types, so the consumer of the JSON don't go
-    /// looking for types which don't exist anywhere.
+    /// A type parameter.
     Generic(Symbol),
-    /// Primitives are the fixed-size numeric types (plus int/usize/float), char,
-    /// arrays, slices, and tuples.
+    /// A primitive (aka, builtin) type.
     Primitive(PrimitiveType),
-    /// `extern "ABI" fn`
+    /// A function pointer: `extern "ABI" fn(...) -> ...`
     BareFunction(Box<BareFunctionDecl>),
+    /// A tuple type: `(i32, &str)`.
     Tuple(Vec<Type>),
+    /// A slice type (does *not* include the `&`): `[i32]`
     Slice(Box<Type>),
-    /// The `String` field is about the size or the constant representing the array's length.
+    /// An array type.
+    ///
+    /// The `String` field is a stringified version of the array's length parameter.
     Array(Box<Type>, String),
+    /// A raw pointer type: `*const i32`, `*mut i32`
     RawPointer(Mutability, Box<Type>),
-    BorrowedRef {
-        lifetime: Option<Lifetime>,
-        mutability: Mutability,
-        type_: Box<Type>,
-    },
+    /// A reference type: `&i32`, `&'a mut Foo`
+    BorrowedRef { lifetime: Option<Lifetime>, mutability: Mutability, type_: Box<Type> },
 
-    // `<Type as Trait>::Name`
+    /// A qualified path to an associated item: `<Type as Trait>::Name`
     QPath {
         name: Symbol,
         self_type: Box<Type>,
+        /// FIXME: This is a hack that should be removed; see [this discussion][1].
+        ///
+        /// [1]: https://github.com/rust-lang/rust/pull/85479#discussion_r635729093
         self_def_id: Option<DefId>,
-        trait_: Box<Type>,
+        trait_: Path,
     },
 
-    // `_`
+    /// A type that is inferred: `_`
     Infer,
 
-    // `impl TraitA + TraitB + ...`
+    /// An `impl Trait`: `impl TraitA + TraitB + ...`
     ImplTrait(Vec<GenericBound>),
 }
 
@@ -1481,34 +1480,8 @@ impl Type {
     }
 
     crate fn generics(&self) -> Option<Vec<&Type>> {
-        match *self {
-            ResolvedPath { ref path, .. } => path.segments.last().and_then(|seg| {
-                if let GenericArgs::AngleBracketed { ref args, .. } = seg.args {
-                    Some(
-                        args.iter()
-                            .filter_map(|arg| match arg {
-                                GenericArg::Type(ty) => Some(ty),
-                                _ => None,
-                            })
-                            .collect(),
-                    )
-                } else {
-                    None
-                }
-            }),
-            _ => None,
-        }
-    }
-
-    crate fn bindings(&self) -> Option<&[TypeBinding]> {
-        match *self {
-            ResolvedPath { ref path, .. } => path.segments.last().and_then(|seg| {
-                if let GenericArgs::AngleBracketed { ref bindings, .. } = seg.args {
-                    Some(&**bindings)
-                } else {
-                    None
-                }
-            }),
+        match self {
+            ResolvedPath { path, .. } => path.generics(),
             _ => None,
         }
     }
@@ -1526,17 +1499,13 @@ impl Type {
             QPath { self_type, trait_, name, .. } => (self_type, trait_, name),
             _ => return None,
         };
-        let trait_did = match **trait_ {
-            ResolvedPath { did, .. } => did,
-            _ => return None,
-        };
-        Some((&self_, trait_did, *name))
+        Some((&self_, trait_.def_id(), *name))
     }
 
     fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
         let t: PrimitiveType = match *self {
             ResolvedPath { did, .. } => return Some(did),
-            DynTrait(ref bounds, _) => return bounds[0].trait_.inner_def_id(cache),
+            DynTrait(ref bounds, _) => return Some(bounds[0].trait_.def_id()),
             Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()),
             BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference,
             BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache),
@@ -1568,8 +1537,12 @@ impl GetDefId for Type {
     }
 }
 
-/// N.B. this has to be different from `hir::PrimTy` because it also includes types that aren't
-/// paths, like `Unit`.
+/// A primitive (aka, builtin) type.
+///
+/// This represents things like `i32`, `str`, etc.
+///
+/// N.B. This has to be different from [`hir::PrimTy`] because it also includes types that aren't
+/// paths, like [`Self::Unit`].
 #[derive(Clone, PartialEq, Eq, Hash, Copy, Debug)]
 crate enum PrimitiveType {
     Isize,
@@ -1967,12 +1940,15 @@ impl Span {
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 crate struct Path {
-    crate global: bool,
     crate res: Res,
     crate segments: Vec<PathSegment>,
 }
 
 impl Path {
+    crate fn def_id(&self) -> DefId {
+        self.res.def_id()
+    }
+
     crate fn last(&self) -> Symbol {
         self.segments.last().expect("segments were empty").name
     }
@@ -1982,8 +1958,11 @@ impl Path {
     }
 
     crate fn whole_name(&self) -> String {
-        String::from(if self.global { "::" } else { "" })
-            + &self.segments.iter().map(|s| s.name.to_string()).collect::<Vec<_>>().join("::")
+        self.segments
+            .iter()
+            .map(|s| if s.name == kw::PathRoot { String::new() } else { s.name.to_string() })
+            .intersperse("::".into())
+            .collect()
     }
 
     /// Checks if this is a `T::Name` path for an associated type.
@@ -1995,6 +1974,33 @@ impl Path {
             _ => false,
         }
     }
+
+    crate fn generics(&self) -> Option<Vec<&Type>> {
+        self.segments.last().and_then(|seg| {
+            if let GenericArgs::AngleBracketed { ref args, .. } = seg.args {
+                Some(
+                    args.iter()
+                        .filter_map(|arg| match arg {
+                            GenericArg::Type(ty) => Some(ty),
+                            _ => None,
+                        })
+                        .collect(),
+                )
+            } else {
+                None
+            }
+        })
+    }
+
+    crate fn bindings(&self) -> Option<&[TypeBinding]> {
+        self.segments.last().and_then(|seg| {
+            if let GenericArgs::AngleBracketed { ref bindings, .. } = seg.args {
+                Some(&**bindings)
+            } else {
+                None
+            }
+        })
+    }
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
@@ -2138,7 +2144,7 @@ crate struct Impl {
     crate span: Span,
     crate unsafety: hir::Unsafety,
     crate generics: Generics,
-    crate trait_: Option<Type>,
+    crate trait_: Option<Path>,
     crate for_: Type,
     crate items: Vec<Item>,
     crate negative_polarity: bool,
@@ -2149,7 +2155,8 @@ crate struct Impl {
 impl Impl {
     crate fn provided_trait_methods(&self, tcx: TyCtxt<'_>) -> FxHashSet<Symbol> {
         self.trait_
-            .def_id()
+            .as_ref()
+            .map(|t| t.def_id())
             .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.ident.name).collect())
             .unwrap_or_default()
     }
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 33d460d587a51..1449b8acf0cd7 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -2,7 +2,7 @@ use crate::clean::auto_trait::AutoTraitFinder;
 use crate::clean::blanket_impl::BlanketImplFinder;
 use crate::clean::{
     inline, Clean, Crate, ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item,
-    ItemKind, Lifetime, Path, PathSegment, PolyTrait, Primitive, PrimitiveType, ResolvedPath, Type,
+    ItemKind, Lifetime, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Type,
     TypeBinding, Visibility,
 };
 use crate::core::DocContext;
@@ -148,7 +148,6 @@ pub(super) fn external_path(
     let def_kind = cx.tcx.def_kind(did);
     let name = cx.tcx.item_name(did);
     Path {
-        global: false,
         res: Res::Def(def_kind, did),
         segments: vec![PathSegment {
             name,
@@ -157,39 +156,8 @@ pub(super) fn external_path(
     }
 }
 
-crate fn strip_type(ty: Type) -> Type {
-    match ty {
-        Type::ResolvedPath { path, did } => Type::ResolvedPath { path: strip_path(&path), did },
-        Type::DynTrait(mut bounds, lt) => {
-            let first = bounds.remove(0);
-            let stripped_trait = strip_type(first.trait_);
-
-            bounds.insert(
-                0,
-                PolyTrait { trait_: stripped_trait, generic_params: first.generic_params },
-            );
-            Type::DynTrait(bounds, lt)
-        }
-        Type::Tuple(inner_tys) => {
-            Type::Tuple(inner_tys.iter().map(|t| strip_type(t.clone())).collect())
-        }
-        Type::Slice(inner_ty) => Type::Slice(Box::new(strip_type(*inner_ty))),
-        Type::Array(inner_ty, s) => Type::Array(Box::new(strip_type(*inner_ty)), s),
-        Type::RawPointer(m, inner_ty) => Type::RawPointer(m, Box::new(strip_type(*inner_ty))),
-        Type::BorrowedRef { lifetime, mutability, type_ } => {
-            Type::BorrowedRef { lifetime, mutability, type_: Box::new(strip_type(*type_)) }
-        }
-        Type::QPath { name, self_type, trait_, self_def_id } => Type::QPath {
-            name,
-            self_def_id,
-            self_type: Box::new(strip_type(*self_type)),
-            trait_: Box::new(strip_type(*trait_)),
-        },
-        _ => ty,
-    }
-}
-
-crate fn strip_path(path: &Path) -> Path {
+/// Remove the generic arguments from a path.
+crate fn strip_path_generics(path: Path) -> Path {
     let segments = path
         .segments
         .iter()
@@ -199,7 +167,7 @@ crate fn strip_path(path: &Path) -> Path {
         })
         .collect();
 
-    Path { global: path.global, res: path.res, segments }
+    Path { res: path.res, segments }
 }
 
 crate fn qpath_to_string(p: &hir::QPath<'_>) -> String {
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index bcfcc3d70395c..dd1cf2a866dd4 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -201,7 +201,9 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
         // masked crate then remove it completely.
         if let clean::ImplItem(ref i) = *item.kind {
             if self.cache.masked_crates.contains(&item.def_id.krate())
-                || i.trait_.def_id().map_or(false, |d| self.cache.masked_crates.contains(&d.krate))
+                || i.trait_
+                    .as_ref()
+                    .map_or(false, |t| self.cache.masked_crates.contains(&t.def_id().krate))
                 || i.for_.def_id().map_or(false, |d| self.cache.masked_crates.contains(&d.krate))
             {
                 return None;
@@ -221,11 +223,11 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
 
         // Collect all the implementors of traits.
         if let clean::ImplItem(ref i) = *item.kind {
-            if let Some(did) = i.trait_.def_id() {
+            if let Some(trait_) = &i.trait_ {
                 if i.blanket_impl.is_none() {
                     self.cache
                         .implementors
-                        .entry(did)
+                        .entry(trait_.def_id())
                         .or_default()
                         .push(Impl { impl_item: item.clone() });
                 }
@@ -400,12 +402,8 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
                     }
                     clean::DynTrait(ref bounds, _)
                     | clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => {
-                        if let Some(did) = bounds[0].trait_.def_id() {
-                            self.cache.parent_stack.push(did);
-                            true
-                        } else {
-                            false
-                        }
+                        self.cache.parent_stack.push(bounds[0].trait_.def_id());
+                        true
                     }
                     ref t => {
                         let prim_did = t
@@ -439,9 +437,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
                 }
                 clean::DynTrait(ref bounds, _)
                 | clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => {
-                    if let Some(did) = bounds[0].trait_.def_id() {
-                        dids.insert(did);
-                    }
+                    dids.insert(bounds[0].trait_.def_id());
                 }
                 ref t => {
                     let did = t
diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs
index 6060b0560cf3c..4f0c5a9edee71 100644
--- a/src/librustdoc/formats/mod.rs
+++ b/src/librustdoc/formats/mod.rs
@@ -7,14 +7,12 @@ use rustc_hir::def_id::DefId;
 crate use renderer::{run_format, FormatRenderer};
 
 use crate::clean;
-use crate::clean::types::GetDefId;
-use crate::formats::cache::Cache;
 
 /// Specifies whether rendering directly implemented trait items or ones from a certain Deref
 /// impl.
 crate enum AssocItemRender<'a> {
     All,
-    DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type, deref_mut_: bool },
+    DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
 }
 
 /// For different handling of associated items from the Deref target of a type rather than the type
@@ -40,10 +38,6 @@ impl Impl {
     }
 
     crate fn trait_did(&self) -> Option<DefId> {
-        self.inner_impl().trait_.def_id()
-    }
-
-    crate fn trait_did_full(&self, cache: &Cache) -> Option<DefId> {
-        self.inner_impl().trait_.def_id_full(cache)
+        self.inner_impl().trait_.as_ref().map(|t| t.def_id())
     }
 }
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index bcd78b2adc085..9cd10481ef03d 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -18,9 +18,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::CRATE_DEF_INDEX;
 use rustc_target::spec::abi::Abi;
 
-use crate::clean::{
-    self, utils::find_nearest_parent_module, ExternalCrate, GetDefId, ItemId, PrimitiveType,
-};
+use crate::clean::{self, utils::find_nearest_parent_module, ExternalCrate, ItemId, PrimitiveType};
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
 use crate::html::render::cache::ExternalLocation;
@@ -914,15 +912,10 @@ fn fmt_type<'cx>(
             }
         }
         clean::QPath { ref name, ref self_type, ref trait_, ref self_def_id } => {
-            let should_show_cast = match *trait_ {
-                box clean::ResolvedPath { ref path, .. } => {
-                    !path.segments.is_empty()
-                        && self_def_id
-                            .zip(trait_.def_id())
-                            .map_or(!self_type.is_self_type(), |(id, trait_)| id != trait_)
-                }
-                _ => true,
-            };
+            let should_show_cast = !trait_.segments.is_empty()
+                && self_def_id
+                    .zip(Some(trait_.def_id()))
+                    .map_or(!self_type.is_self_type(), |(id, trait_)| id != trait_);
             if f.alternate() {
                 if should_show_cast {
                     write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))?
@@ -936,36 +929,31 @@ fn fmt_type<'cx>(
                     write!(f, "{}::", self_type.print(cx))?
                 }
             };
-            match *trait_ {
-                // It's pretty unsightly to look at `<A as B>::C` in output, and
-                // we've got hyperlinking on our side, so try to avoid longer
-                // notation as much as possible by making `C` a hyperlink to trait
-                // `B` to disambiguate.
-                //
-                // FIXME: this is still a lossy conversion and there should probably
-                //        be a better way of representing this in general? Most of
-                //        the ugliness comes from inlining across crates where
-                //        everything comes in as a fully resolved QPath (hard to
-                //        look at).
-                box clean::ResolvedPath { did, .. } => {
-                    match href(did, cx) {
-                        Ok((ref url, _, ref path)) if !f.alternate() => {
-                            write!(
-                                f,
-                                "<a class=\"type\" href=\"{url}#{shortty}.{name}\" \
+            // It's pretty unsightly to look at `<A as B>::C` in output, and
+            // we've got hyperlinking on our side, so try to avoid longer
+            // notation as much as possible by making `C` a hyperlink to trait
+            // `B` to disambiguate.
+            //
+            // FIXME: this is still a lossy conversion and there should probably
+            //        be a better way of representing this in general? Most of
+            //        the ugliness comes from inlining across crates where
+            //        everything comes in as a fully resolved QPath (hard to
+            //        look at).
+            match href(trait_.def_id(), cx) {
+                Ok((ref url, _, ref path)) if !f.alternate() => {
+                    write!(
+                        f,
+                        "<a class=\"type\" href=\"{url}#{shortty}.{name}\" \
                                     title=\"type {path}::{name}\">{name}</a>",
-                                url = url,
-                                shortty = ItemType::AssocType,
-                                name = name,
-                                path = path.join("::")
-                            )?;
-                        }
-                        _ => write!(f, "{}", name)?,
-                    }
-                    Ok(())
+                        url = url,
+                        shortty = ItemType::AssocType,
+                        name = name,
+                        path = path.join("::")
+                    )?;
                 }
-                _ => write!(f, "{}", name),
+                _ => write!(f, "{}", name)?,
             }
+            Ok(())
         }
     }
 }
@@ -979,6 +967,15 @@ impl clean::Type {
     }
 }
 
+impl clean::Path {
+    crate fn print<'b, 'a: 'b, 'tcx: 'a>(
+        &'a self,
+        cx: &'a Context<'tcx>,
+    ) -> impl fmt::Display + 'b + Captures<'tcx> {
+        display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx))
+    }
+}
+
 impl clean::Impl {
     crate fn print<'a, 'tcx: 'a>(
         &'a self,
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
index edd1d8b98fc64..9c05c80d55dfe 100644
--- a/src/librustdoc/html/render/cache.rs
+++ b/src/librustdoc/html/render/cache.rs
@@ -226,16 +226,13 @@ fn get_index_type(clean_type: &clean::Type) -> RenderType {
 fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<Symbol> {
     match *clean_type {
         clean::ResolvedPath { ref path, .. } => {
-            let segments = &path.segments;
-            let path_segment = segments.iter().last().unwrap_or_else(|| {
-                panic!(
-                "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
-                clean_type, accept_generic
-            )
-            });
+            let path_segment = path.segments.last().unwrap();
             Some(path_segment.name)
         }
-        clean::DynTrait(ref bounds, _) => get_index_type_name(&bounds[0].trait_, accept_generic),
+        clean::DynTrait(ref bounds, _) => {
+            let path = &bounds[0].trait_;
+            Some(path.segments.last().unwrap().name)
+        }
         clean::Generic(s) if accept_generic => Some(s),
         clean::Primitive(ref p) => Some(p.as_sym()),
         clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
@@ -324,7 +321,8 @@ crate fn get_real_types<'tcx>(
         }
         if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
             for bound in bound.get_bounds().unwrap_or(&[]) {
-                if let Some(ty) = bound.get_trait_type() {
+                if let Some(path) = bound.get_trait_path() {
+                    let ty = Type::ResolvedPath { did: path.def_id(), path };
                     let adds = get_real_types(generics, &ty, tcx, recurse + 1, res);
                     nb_added += adds;
                     if adds == 0 && !ty.is_full_generic() {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 1f27357f6c6ea..6b213da50a79c 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -687,18 +687,12 @@ fn short_item_info(
 
 // Render the list of items inside one of the sections "Trait Implementations",
 // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
-fn render_impls(
-    cx: &Context<'_>,
-    w: &mut Buffer,
-    traits: &[&&Impl],
-    containing_item: &clean::Item,
-) {
-    let cache = cx.cache();
+fn render_impls(cx: &Context<'_>, w: &mut Buffer, impls: &[&&Impl], containing_item: &clean::Item) {
     let tcx = cx.tcx();
-    let mut impls = traits
+    let mut rendered_impls = impls
         .iter()
         .map(|i| {
-            let did = i.trait_did_full(cache).unwrap();
+            let did = i.trait_did().unwrap();
             let provided_trait_methods = i.inner_impl().provided_trait_methods(tcx);
             let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_trait_methods);
             let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() };
@@ -722,8 +716,8 @@ fn render_impls(
             buffer.into_inner()
         })
         .collect::<Vec<_>>();
-    impls.sort();
-    w.write_str(&impls.join(""));
+    rendered_impls.sort();
+    w.write_str(&rendered_impls.join(""));
 }
 
 fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
@@ -1068,13 +1062,11 @@ fn render_assoc_items(
         return;
     }
     if !traits.is_empty() {
-        let deref_impl = traits.iter().find(|t| {
-            t.inner_impl().trait_.def_id_full(cache) == cx.tcx().lang_items().deref_trait()
-        });
+        let deref_impl =
+            traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
         if let Some(impl_) = deref_impl {
-            let has_deref_mut = traits.iter().any(|t| {
-                t.inner_impl().trait_.def_id_full(cache) == cx.tcx().lang_items().deref_mut_trait()
-            });
+            let has_deref_mut =
+                traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
             render_deref_methods(w, cx, impl_, containing_item, has_deref_mut);
         }
         let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
@@ -1192,45 +1184,39 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) ->
 
 fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
     let mut out = Buffer::html();
-    let mut trait_ = String::new();
 
     if let Some(did) = decl.output.def_id_full(cx.cache()) {
         if let Some(impls) = cx.cache().impls.get(&did) {
             for i in impls {
                 let impl_ = i.inner_impl();
-                if impl_.trait_.def_id().map_or(false, |d| {
-                    cx.cache().traits.get(&d).map(|t| t.is_notable).unwrap_or(false)
-                }) {
-                    if out.is_empty() {
-                        write!(
-                            &mut out,
-                            "<div class=\"notable\">Notable traits for {}</div>\
-                             <code class=\"content\">",
-                            impl_.for_.print(cx)
-                        );
-                        trait_.push_str(&impl_.for_.print(cx).to_string());
-                    }
+                if let Some(trait_) = &impl_.trait_ {
+                    let trait_did = trait_.def_id();
 
-                    //use the "where" class here to make it small
-                    write!(
-                        &mut out,
-                        "<span class=\"where fmt-newline\">{}</span>",
-                        impl_.print(false, cx)
-                    );
-                    let t_did = impl_.trait_.def_id_full(cx.cache()).unwrap();
-                    for it in &impl_.items {
-                        if let clean::TypedefItem(ref tydef, _) = *it.kind {
-                            out.push_str("<span class=\"where fmt-newline\">    ");
-                            assoc_type(
+                    if cx.cache().traits.get(&trait_did).map_or(false, |t| t.is_notable) {
+                        if out.is_empty() {
+                            write!(
                                 &mut out,
-                                it,
-                                &[],
-                                Some(&tydef.type_),
-                                AssocItemLink::GotoSource(t_did.into(), &FxHashSet::default()),
-                                "",
-                                cx,
+                                "<div class=\"notable\">Notable traits for {}</div>\
+                             <code class=\"content\">",
+                                impl_.for_.print(cx)
                             );
-                            out.push_str(";</span>");
+                        }
+
+                        //use the "where" class here to make it small
+                        write!(
+                            &mut out,
+                            "<span class=\"where fmt-newline\">{}</span>",
+                            impl_.print(false, cx)
+                        );
+                        for it in &impl_.items {
+                            if let clean::TypedefItem(ref tydef, _) = *it.kind {
+                                out.push_str("<span class=\"where fmt-newline\">    ");
+                                let empty_set = FxHashSet::default();
+                                let src_link =
+                                    AssocItemLink::GotoSource(trait_did.into(), &empty_set);
+                                assoc_type(&mut out, it, &[], Some(&tydef.type_), src_link, "", cx);
+                                out.push_str(";</span>");
+                            }
                         }
                     }
                 }
@@ -1273,7 +1259,7 @@ fn render_impl(
 ) {
     let cache = cx.cache();
     let traits = &cache.traits;
-    let trait_ = i.trait_did_full(cache).map(|did| &traits[&did]);
+    let trait_ = i.trait_did().map(|did| &traits[&did]);
     let mut close_tags = String::new();
 
     // For trait implementations, the `interesting` output contains all methods that have doc
@@ -1503,7 +1489,7 @@ fn render_impl(
             if i.items.iter().any(|m| m.name == n) {
                 continue;
             }
-            let did = i.trait_.as_ref().unwrap().def_id_full(cx.cache()).unwrap();
+            let did = i.trait_.as_ref().unwrap().def_id();
             let provided_methods = i.provided_trait_methods(cx.tcx());
             let assoc_link = AssocItemLink::GotoSource(did.into(), &provided_methods);
 
@@ -1887,9 +1873,9 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
         }
 
         if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
-            if let Some(impl_) = v.iter().filter(|i| i.inner_impl().trait_.is_some()).find(|i| {
-                i.inner_impl().trait_.def_id_full(cache) == cx.tcx().lang_items().deref_trait()
-            }) {
+            if let Some(impl_) =
+                v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
+            {
                 sidebar_deref_methods(cx, out, impl_, v);
             }
 
@@ -1987,9 +1973,7 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &V
                 }
             }
         }
-        let deref_mut = v.iter().filter(|i| i.inner_impl().trait_.is_some()).any(|i| {
-            i.inner_impl().trait_.def_id_full(c) == cx.tcx().lang_items().deref_mut_trait()
-        });
+        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
         let inner_impl = target
             .def_id_full(c)
             .or_else(|| {
@@ -2056,10 +2040,10 @@ fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clea
 
 fn get_id_for_impl_on_foreign_type(
     for_: &clean::Type,
-    trait_: &clean::Type,
+    trait_: &clean::Path,
     cx: &Context<'_>,
 ) -> String {
-    small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cx), for_.print(cx),))
+    small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cx), for_.print(cx)))
 }
 
 fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
@@ -2370,6 +2354,15 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
     let mut visited = FxHashSet::default();
     let mut work = VecDeque::new();
 
+    let mut process_path = |did: DefId| {
+        let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone());
+        let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
+
+        if let Some(path) = fqp {
+            out.push(path.join("::"));
+        }
+    };
+
     work.push_back(first_ty);
 
     while let Some(ty) = work.pop_front() {
@@ -2378,14 +2371,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
         }
 
         match ty {
-            clean::Type::ResolvedPath { did, .. } => {
-                let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone());
-                let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
-
-                if let Some(path) = fqp {
-                    out.push(path.join("::"));
-                }
-            }
+            clean::Type::ResolvedPath { did, .. } => process_path(did),
             clean::Type::Tuple(tys) => {
                 work.extend(tys.into_iter());
             }
@@ -2403,7 +2389,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
             }
             clean::Type::QPath { self_type, trait_, .. } => {
                 work.push_back(*self_type);
-                work.push_back(*trait_);
+                process_path(trait_.def_id());
             }
             _ => {}
         }
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index ea81b041c3bc6..1f2479a302f95 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -363,8 +363,11 @@ impl FromWithTcx<clean::GenericBound> for GenericBound {
         use clean::GenericBound::*;
         match bound {
             TraitBound(clean::PolyTrait { trait_, generic_params }, modifier) => {
+                // FIXME: should `trait_` be a clean::Path equivalent in JSON?
+                let trait_ =
+                    clean::ResolvedPath { did: trait_.def_id(), path: trait_ }.into_tcx(tcx);
                 GenericBound::TraitBound {
-                    trait_: trait_.into_tcx(tcx),
+                    trait_,
                     generic_params: generic_params.into_iter().map(|x| x.into_tcx(tcx)).collect(),
                     modifier: from_trait_bound_modifier(modifier),
                 }
@@ -394,15 +397,12 @@ impl FromWithTcx<clean::Type> for Type {
                 param_names: Vec::new(),
             },
             DynTrait(mut bounds, lt) => {
-                let (path, id) = match bounds.remove(0).trait_ {
-                    ResolvedPath { path, did, .. } => (path, did),
-                    _ => unreachable!(),
-                };
+                let first_trait = bounds.remove(0).trait_;
 
                 Type::ResolvedPath {
-                    name: path.whole_name(),
-                    id: from_item_id(id.into()),
-                    args: path
+                    name: first_trait.whole_name(),
+                    id: from_item_id(first_trait.def_id().into()),
+                    args: first_trait
                         .segments
                         .last()
                         .map(|args| Box::new(args.clone().args.into_tcx(tcx))),
@@ -434,11 +434,15 @@ impl FromWithTcx<clean::Type> for Type {
                 mutable: mutability == ast::Mutability::Mut,
                 type_: Box::new((*type_).into_tcx(tcx)),
             },
-            QPath { name, self_type, trait_, .. } => Type::QualifiedPath {
-                name: name.to_string(),
-                self_type: Box::new((*self_type).into_tcx(tcx)),
-                trait_: Box::new((*trait_).into_tcx(tcx)),
-            },
+            QPath { name, self_type, trait_, .. } => {
+                // FIXME: should `trait_` be a clean::Path equivalent in JSON?
+                let trait_ = ResolvedPath { did: trait_.def_id(), path: trait_ }.into_tcx(tcx);
+                Type::QualifiedPath {
+                    name: name.to_string(),
+                    self_type: Box::new((*self_type).into_tcx(tcx)),
+                    trait_: Box::new(trait_),
+                }
+            }
         }
     }
 }
@@ -507,6 +511,11 @@ impl FromWithTcx<clean::Impl> for Impl {
             blanket_impl,
             span: _span,
         } = impl_;
+        // FIXME: should `trait_` be a clean::Path equivalent in JSON?
+        let trait_ = trait_.map(|path| {
+            let did = path.def_id();
+            clean::ResolvedPath { path, did }.into_tcx(tcx)
+        });
         Impl {
             is_unsafe: unsafety == rustc_hir::Unsafety::Unsafe,
             generics: generics.into_tcx(tcx),
@@ -514,7 +523,7 @@ impl FromWithTcx<clean::Impl> for Impl {
                 .into_iter()
                 .map(|x| x.to_string())
                 .collect(),
-            trait_: trait_.map(|x| x.into_tcx(tcx)),
+            trait_,
             for_: for_.into_tcx(tcx),
             items: ids(items),
             negative: negative_polarity,
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index eefe50caa345e..319dd7b42b0ee 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -57,7 +57,9 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
     // scan through included items ahead of time to splice in Deref targets to the "valid" sets
     for it in &new_items {
         if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind {
-            if cleaner.keep_impl(for_) && trait_.def_id() == cx.tcx.lang_items().deref_trait() {
+            if cleaner.keep_impl(for_)
+                && trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait()
+            {
                 let target = items
                     .iter()
                     .find_map(|item| match *item.kind {
@@ -78,7 +80,9 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
     new_items.retain(|it| {
         if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind {
             cleaner.keep_impl(for_)
-                || trait_.as_ref().map_or(false, |t| cleaner.keep_impl(t))
+                || trait_
+                    .as_ref()
+                    .map_or(false, |t| cleaner.keep_impl_with_def_id(t.def_id().into()))
                 || blanket_impl.is_some()
         } else {
             true
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 90300dbd16b13..8b1fd662f85fd 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -134,7 +134,7 @@ impl<'a> DocFolder for ImplStripper<'a> {
                     return None;
                 }
             }
-            if let Some(did) = imp.trait_.def_id() {
+            if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) {
                 if did.is_local() && !self.retained.contains(&did.into()) {
                     debug!("ImplStripper: impl item for stripped trait; removing");
                     return None;