diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index cc3a60c596ae7..b9c75ff2fcff6 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -84,49 +84,50 @@ impl<T: Clean<U>, U> Clean<Option<U>> for Option<T> {
     }
 }
 
+/// Collect all inner modules which are tagged as implementations of
+/// primitives.
+///
+/// Note that this loop only searches the top-level items of the crate,
+/// and this is intentional. If we were to search the entire crate for an
+/// item tagged with `#[doc(primitive)]` then we would also have to
+/// search the entirety of external modules for items tagged
+/// `#[doc(primitive)]`, which is a pretty inefficient process (decoding
+/// all that metadata unconditionally).
+///
+/// In order to keep the metadata load under control, the
+/// `#[doc(primitive)]` feature is explicitly designed to only allow the
+/// primitive tags to show up as the top level items in a crate.
+///
+/// Also note that this does not attempt to deal with modules tagged
+/// duplicately for the same primitive. This is handled later on when
+/// rendering by delegating everything to a hash map.
+crate fn as_primitive(cx: &DocContext<'_>, res: Res) -> Option<(DefId, PrimitiveType, Attributes)> {
+    if let Res::Def(DefKind::Mod, def_id) = res {
+        let attrs = cx.tcx.get_attrs(def_id).clean(cx);
+        let mut prim = None;
+        for attr in attrs.lists(sym::doc) {
+            if let Some(v) = attr.value_str() {
+                if attr.check_name(sym::primitive) {
+                    prim = PrimitiveType::from_symbol(v);
+                    if prim.is_some() {
+                        break;
+                    }
+                    // FIXME: should warn on unknown primitives?
+                }
+            }
+        }
+        return prim.map(|p| (def_id, p, attrs));
+    }
+    None
+}
+
 impl Clean<ExternalCrate> for CrateNum {
     fn clean(&self, cx: &DocContext<'_>) -> ExternalCrate {
         let root = DefId { krate: *self, index: CRATE_DEF_INDEX };
         let krate_span = cx.tcx.def_span(root);
         let krate_src = cx.sess().source_map().span_to_filename(krate_span);
 
-        // Collect all inner modules which are tagged as implementations of
-        // primitives.
-        //
-        // Note that this loop only searches the top-level items of the crate,
-        // and this is intentional. If we were to search the entire crate for an
-        // item tagged with `#[doc(primitive)]` then we would also have to
-        // search the entirety of external modules for items tagged
-        // `#[doc(primitive)]`, which is a pretty inefficient process (decoding
-        // all that metadata unconditionally).
-        //
-        // In order to keep the metadata load under control, the
-        // `#[doc(primitive)]` feature is explicitly designed to only allow the
-        // primitive tags to show up as the top level items in a crate.
-        //
-        // Also note that this does not attempt to deal with modules tagged
-        // duplicately for the same primitive. This is handled later on when
-        // rendering by delegating everything to a hash map.
-        let as_primitive = |res: Res| {
-            if let Res::Def(DefKind::Mod, def_id) = res {
-                let attrs = cx.tcx.get_attrs(def_id).clean(cx);
-                let mut prim = None;
-                for attr in attrs.lists(sym::doc) {
-                    if let Some(v) = attr.value_str() {
-                        if attr.check_name(sym::primitive) {
-                            prim = PrimitiveType::from_symbol(v);
-                            if prim.is_some() {
-                                break;
-                            }
-                            // FIXME: should warn on unknown primitives?
-                        }
-                    }
-                }
-                return prim.map(|p| (def_id, p, attrs));
-            }
-            None
-        };
-        let primitives = if root.is_local() {
+        let primitives: Vec<(DefId, PrimitiveType, Attributes)> = if root.is_local() {
             cx.tcx
                 .hir()
                 .krate()
@@ -137,14 +138,14 @@ impl Clean<ExternalCrate> for CrateNum {
                 .filter_map(|&id| {
                     let item = cx.tcx.hir().expect_item(id.id);
                     match item.kind {
-                        hir::ItemKind::Mod(_) => as_primitive(Res::Def(
-                            DefKind::Mod,
-                            cx.tcx.hir().local_def_id(id.id).to_def_id(),
-                        )),
+                        hir::ItemKind::Mod(_) => as_primitive(
+                            cx,
+                            Res::Def(DefKind::Mod, cx.tcx.hir().local_def_id(id.id).to_def_id()),
+                        ),
                         hir::ItemKind::Use(ref path, hir::UseKind::Single)
                             if item.vis.node.is_pub() =>
                         {
-                            as_primitive(path.res).map(|(_, prim, attrs)| {
+                            as_primitive(cx, path.res).map(|(_, prim, attrs)| {
                                 // Pretend the primitive is local.
                                 (cx.tcx.hir().local_def_id(id.id).to_def_id(), prim, attrs)
                             })
@@ -158,7 +159,7 @@ impl Clean<ExternalCrate> for CrateNum {
                 .item_children(root)
                 .iter()
                 .map(|item| item.res)
-                .filter_map(as_primitive)
+                .filter_map(|res| as_primitive(cx, res))
                 .collect()
         };
 
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 89549eae2cb0e..971f7fba3ac68 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1173,7 +1173,7 @@ impl GetDefId for Type {
     fn def_id(&self) -> Option<DefId> {
         match *self {
             ResolvedPath { did, .. } => Some(did),
-            Primitive(p) => cache().primitive_locations.get(&p).cloned(),
+            Primitive(p) => crate::core::primitives().get(&p).cloned(),
             BorrowedRef { type_: box Generic(..), .. } => {
                 Primitive(PrimitiveType::Reference).def_id()
             }
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index cbd0ca0de6414..96023f5a9555d 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -5,7 +5,8 @@ use rustc_driver::abort_on_err;
 use rustc_errors::emitter::{Emitter, EmitterWriter};
 use rustc_errors::json::JsonEmitter;
 use rustc_feature::UnstableFeatures;
-use rustc_hir::def::{Namespace::TypeNS, Res};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Namespace::TypeNS, Res};
 use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc_hir::HirId;
 use rustc_hir::{
@@ -29,9 +30,11 @@ use rustc_span::DUMMY_SP;
 use std::cell::RefCell;
 use std::mem;
 use std::rc::Rc;
+use std::sync::Arc;
 
 use crate::clean;
-use crate::clean::{AttributesExt, MAX_DEF_ID};
+use crate::clean::types::PrimitiveType;
+use crate::clean::{as_primitive, AttributesExt, MAX_DEF_ID};
 use crate::config::RenderInfo;
 use crate::config::{Options as RustdocOptions, RenderOptions};
 use crate::passes::{self, Condition::*, ConditionalPass};
@@ -39,6 +42,13 @@ use crate::passes::{self, Condition::*, ConditionalPass};
 pub use rustc_session::config::{CodegenOptions, DebuggingOptions, Input, Options};
 pub use rustc_session::search_paths::SearchPath;
 
+thread_local!(static PRIMITIVES: RefCell<Arc<FxHashMap<PrimitiveType, DefId>>> =
+    Default::default());
+
+crate fn primitives() -> Arc<FxHashMap<PrimitiveType, DefId>> {
+    PRIMITIVES.with(|c| c.borrow().clone())
+}
+
 pub type ExternalPaths = FxHashMap<DefId, (Vec<String>, clean::TypeKind)>;
 
 pub struct DocContext<'tcx> {
@@ -499,6 +509,65 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
                 };
                 debug!("crate: {:?}", tcx.hir().krate());
 
+                PRIMITIVES.with(|v| {
+                    let mut tmp = v.borrow_mut();
+                    let stored_primitives = Arc::make_mut(&mut *tmp);
+
+                    let mut externs = Vec::new();
+                    for &cnum in ctxt.tcx.crates().iter() {
+                        externs.push(cnum);
+                    }
+                    externs.sort_by(|a, b| a.cmp(&b));
+
+                    for krate in externs.iter().chain([LOCAL_CRATE].iter()) {
+                        let root = DefId { krate: *krate, index: CRATE_DEF_INDEX };
+                        let iter: Vec<(DefId, PrimitiveType)> = if root.is_local() {
+                            ctxt.tcx.hir_crate(*krate).item.module.item_ids.iter().filter_map(
+                                |&id| {
+                                    let item = ctxt.tcx.hir().expect_item(id.id);
+                                    match item.kind {
+                                        hir::ItemKind::Mod(_) => as_primitive(
+                                            &ctxt,
+                                            Res::Def(
+                                                DefKind::Mod,
+                                                ctxt.tcx.hir().local_def_id(id.id).to_def_id(),
+                                            ),
+                                        )
+                                        .map(|(did, prim, _)| (did, prim)),
+                                        hir::ItemKind::Use(ref path, hir::UseKind::Single)
+                                            if item.vis.node.is_pub() =>
+                                        {
+                                            as_primitive(&ctxt, path.res).map(|(_, prim, _)| {
+                                                // Pretend the primitive is local.
+                                                (
+                                                    ctxt.tcx
+                                                        .hir()
+                                                        .local_def_id(id.id)
+                                                        .to_def_id(),
+                                                    prim,
+                                                )
+                                            })
+                                        }
+                                        _ => None,
+                                    }
+                                },
+                            )
+                            .collect()
+                        } else {
+                            ctxt
+                                .tcx
+                                .item_children(root)
+                                .iter()
+                                .map(|item| item.res)
+                                .filter_map(|res| as_primitive(&ctxt, res).map(|(did, prim, _)| (did, prim)))
+                                .collect()
+                        };
+                        for (did, prim) in iter {
+                            stored_primitives.insert(prim, did);
+                        }
+                    }
+                });
+
                 let mut krate = clean::krate(&mut ctxt);
 
                 if let Some(ref m) = krate.module {
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 99b31473f87a3..f6a1f333316a3 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -11,6 +11,7 @@ use rustc_span::source_map::FileName;
 
 use crate::clean::{self, GetDefId};
 use crate::config::RenderInfo;
+use crate::core::primitives;
 use crate::fold::DocFolder;
 use crate::formats::item_type::ItemType;
 use crate::formats::Impl;
@@ -76,9 +77,6 @@ pub struct Cache {
     /// Cache of where external crate documentation can be found.
     pub extern_locations: FxHashMap<CrateNum, (String, PathBuf, ExternalLocation)>,
 
-    /// Cache of where documentation for primitives can be found.
-    pub primitive_locations: FxHashMap<clean::PrimitiveType, DefId>,
-
     // Note that external items for which `doc(hidden)` applies to are shown as
     // non-reachable while local items aren't. This is because we're reusing
     // the access levels from the privacy check pass.
@@ -182,19 +180,6 @@ impl Cache {
             cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
         }
 
-        // Cache where all known primitives have their documentation located.
-        //
-        // Favor linking to as local extern as possible, so iterate all crates in
-        // reverse topological order.
-        for &(_, ref e) in krate.externs.iter().rev() {
-            for &(def_id, prim, _) in &e.primitives {
-                cache.primitive_locations.insert(prim, def_id);
-            }
-        }
-        for &(def_id, prim, _) in &krate.primitives {
-            cache.primitive_locations.insert(prim, def_id);
-        }
-
         cache.stack.push(krate.name.clone());
         krate = cache.fold_crate(krate);
 
@@ -403,9 +388,8 @@ impl DocFolder for Cache {
                         true
                     }
                     ref t => {
-                        let prim_did = t
-                            .primitive_type()
-                            .and_then(|t| self.primitive_locations.get(&t).cloned());
+                        let prim_did =
+                            t.primitive_type().and_then(|t| primitives().get(&t).cloned());
                         match prim_did {
                             Some(did) => {
                                 self.parent_stack.push(did);
@@ -436,9 +420,8 @@ impl DocFolder for Cache {
                             dids.insert(did);
                         }
                         ref t => {
-                            let did = t
-                                .primitive_type()
-                                .and_then(|t| self.primitive_locations.get(&t).cloned());
+                            let did =
+                                t.primitive_type().and_then(|t| primitives().get(&t).cloned());
 
                             if let Some(did) = did {
                                 dids.insert(did);
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 699f8c36cba6a..f416fae6e0719 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -15,6 +15,7 @@ use rustc_span::def_id::DefId;
 use rustc_target::spec::abi::Abi;
 
 use crate::clean::{self, PrimitiveType};
+use crate::core::primitives;
 use crate::formats::cache::cache;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
@@ -559,10 +560,13 @@ fn primitive_link(
     prim: clean::PrimitiveType,
     name: &str,
 ) -> fmt::Result {
-    let m = cache();
     let mut needs_termination = false;
+
     if !f.alternate() {
-        match m.primitive_locations.get(&prim) {
+        let m = cache();
+        let primitives = primitives();
+
+        match primitives.get(&prim) {
             Some(&def_id) if def_id.is_local() => {
                 let len = CURRENT_DEPTH.with(|s| s.get());
                 let len = if len == 0 { 0 } else { len - 1 };
@@ -575,13 +579,21 @@ fn primitive_link(
                 needs_termination = true;
             }
             Some(&def_id) => {
-                let loc = match m.extern_locations[&def_id.krate] {
-                    (ref cname, _, ExternalLocation::Remote(ref s)) => Some((cname, s.to_string())),
-                    (ref cname, _, ExternalLocation::Local) => {
+                let loc = match m.extern_locations.get(&def_id.krate) {
+                    Some((ref cname, _, ExternalLocation::Remote(ref s))) => {
+                        Some((cname.as_str(), s.to_string()))
+                    }
+                    Some((ref cname, _, ExternalLocation::Local)) => {
+                        let len = CURRENT_DEPTH.with(|s| s.get());
+                        Some((cname.as_str(), "../".repeat(len)))
+                    }
+                    Some((.., ExternalLocation::Unknown)) => None,
+                    None => {
+                        // It means we're in core, since the primitive types aren't "accessible"
+                        // from it so we have to simulate it.
                         let len = CURRENT_DEPTH.with(|s| s.get());
-                        Some((cname, "../".repeat(len)))
+                        Some(("core", "../".repeat(len)))
                     }
-                    (.., ExternalLocation::Unknown) => None,
                 };
                 if let Some((cname, root)) = loc {
                     write!(
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
index 378efa1a1bed7..afe8be039072e 100644
--- a/src/librustdoc/html/render/cache.rs
+++ b/src/librustdoc/html/render/cache.rs
@@ -13,6 +13,7 @@ use crate::html::render::{plain_summary_line, shorten};
 use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
 
 /// Indicates where an external crate can be found.
+#[derive(Debug)]
 pub enum ExternalLocation {
     /// Remote URL root of the external crate
     Remote(String),
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 5fb2d9f6f917c..9b6df6cd66153 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -66,6 +66,7 @@ use serde::{Serialize, Serializer};
 use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind};
 use crate::config::RenderInfo;
 use crate::config::RenderOptions;
+use crate::core::primitives;
 use crate::docfs::{DocFS, PathError};
 use crate::doctree;
 use crate::error::Error;
@@ -3405,7 +3406,7 @@ fn render_deref_methods(
         render_assoc_items(w, cx, container_item, did, what, cache);
     } else {
         if let Some(prim) = target.primitive_type() {
-            if let Some(&did) = cache.primitive_locations.get(&prim) {
+            if let Some(&did) = primitives().get(&prim) {
                 render_assoc_items(w, cx, container_item, did, what, cache);
             }
         }
@@ -4058,7 +4059,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
                         .def_id()
                         .or(target
                             .primitive_type()
-                            .and_then(|prim| c.primitive_locations.get(&prim).cloned()))
+                            .and_then(|prim| primitives().get(&prim).cloned()))
                         .and_then(|did| c.impls.get(&did));
                     if let Some(impls) = inner_impl {
                         out.push_str("<a class=\"sidebar-title\" href=\"#deref-methods\">");
diff --git a/src/test/rustdoc-js/primitive-fn-like-search.js b/src/test/rustdoc-js/primitive-fn-like-search.js
new file mode 100644
index 0000000000000..6b052aa4aadb1
--- /dev/null
+++ b/src/test/rustdoc-js/primitive-fn-like-search.js
@@ -0,0 +1,13 @@
+// exact-check
+const QUERY = 'u32 -> u32';
+
+const EXPECTED = {
+    'others': [
+    ],
+    'returned': [
+        { 'path': 'primitive_fn_like_search', 'name': 'foo' },
+    ],
+    'in_args': [
+        { 'path': 'primitive_fn_like_search', 'name': 'foo' },
+    ],
+};
diff --git a/src/test/rustdoc-js/primitive-fn-like-search.rs b/src/test/rustdoc-js/primitive-fn-like-search.rs
new file mode 100644
index 0000000000000..fc5d7cb3f5931
--- /dev/null
+++ b/src/test/rustdoc-js/primitive-fn-like-search.rs
@@ -0,0 +1,5 @@
+pub struct Foo;
+
+pub trait Bar {}
+
+pub fn foo<T: Bar, D: ::std::fmt::Debug>(a: Foo, b: u32, c: T, d: D) -> u32 {0}