diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index f469b2daef5eb..b1d9cea277397 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -1,12 +1,10 @@
-use std::borrow::Cow;
-
 use crate::base::{DummyResult, ExtCtxt, MacResult};
 use crate::expand::{parse_ast_fragment, AstFragmentKind};
 use crate::mbe::{
     macro_parser::{MatcherLoc, NamedParseResult, ParseResult::*, TtParser},
     macro_rules::{try_match_macro, Tracker},
 };
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{self, Token, TokenKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast_pretty::pprust;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage};
@@ -14,6 +12,7 @@ use rustc_parse::parser::{Parser, Recovery};
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
+use std::borrow::Cow;
 
 use super::macro_rules::{parser_from_cx, NoopTracker};
 
@@ -63,6 +62,13 @@ pub(super) fn failed_to_match_macro<'cx>(
         err.note(format!("while trying to match {remaining_matcher}"));
     }
 
+    if let MatcherLoc::Token { token: expected_token } = &remaining_matcher
+        && (matches!(expected_token.kind, TokenKind::Interpolated(_))
+            || matches!(token.kind, TokenKind::Interpolated(_)))
+    {
+        err.note("captured metavariables except for `$tt`, `$ident` and `$lifetime` cannot be compared to other tokens");
+    }
+
     // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
     if let Some((arg, comma_span)) = arg.add_comma() {
         for lhs in lhses {
diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs
index 07fa7c55df660..4110b176b41b1 100644
--- a/compiler/rustc_hir_typeck/src/inherited.rs
+++ b/compiler/rustc_hir_typeck/src/inherited.rs
@@ -4,7 +4,6 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::HirIdMap;
-use rustc_infer::infer;
 use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt};
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -73,40 +72,16 @@ impl<'tcx> Deref for Inherited<'tcx> {
     }
 }
 
-/// A temporary returned by `Inherited::build(...)`. This is necessary
-/// for multiple `InferCtxt` to share the same `typeck_results`
-/// without using `Rc` or something similar.
-pub struct InheritedBuilder<'tcx> {
-    infcx: infer::InferCtxtBuilder<'tcx>,
-    typeck_results: RefCell<ty::TypeckResults<'tcx>>,
-}
-
 impl<'tcx> Inherited<'tcx> {
-    pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> {
+    pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
         let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner;
 
-        InheritedBuilder {
-            infcx: tcx
-                .infer_ctxt()
-                .ignoring_regions()
-                .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)),
-            typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)),
-        }
-    }
-}
-
-impl<'tcx> InheritedBuilder<'tcx> {
-    pub fn enter<F, R>(mut self, f: F) -> R
-    where
-        F: FnOnce(&Inherited<'tcx>) -> R,
-    {
-        f(&Inherited::new(self.infcx.build(), self.typeck_results))
-    }
-}
-
-impl<'tcx> Inherited<'tcx> {
-    fn new(infcx: InferCtxt<'tcx>, typeck_results: RefCell<ty::TypeckResults<'tcx>>) -> Self {
-        let tcx = infcx.tcx;
+        let infcx = tcx
+            .infer_ctxt()
+            .ignoring_regions()
+            .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id))
+            .build();
+        let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
 
         Inherited {
             typeck_results,
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index e397dfd45706b..70124a7736406 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -45,13 +45,14 @@ mod rvalue_scopes;
 mod upvar;
 mod writeback;
 
-pub use diverges::Diverges;
-pub use expectation::Expectation;
-pub use fn_ctxt::*;
-pub use inherited::{Inherited, InheritedBuilder};
+pub use fn_ctxt::FnCtxt;
+pub use inherited::Inherited;
 
 use crate::check::check_fn;
 use crate::coercion::DynamicCoerceMany;
+use crate::diverges::Diverges;
+use crate::expectation::Expectation;
+use crate::fn_ctxt::RawTy;
 use crate::gather_locals::GatherLocalsVisitor;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::{
@@ -105,10 +106,9 @@ pub struct LocalTy<'tcx> {
 /// (notably closures), `typeck_results(def_id)` would wind up
 /// redirecting to the owning function.
 fn primary_body_of(
-    tcx: TyCtxt<'_>,
-    id: hir::HirId,
+    node: Node<'_>,
 ) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> {
-    match tcx.hir().get(id) {
+    match node {
         Node::Item(item) => match item.kind {
             hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => {
                 Some((body, Some(ty), None))
@@ -142,8 +142,7 @@ fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
     }
 
     if let Some(def_id) = def_id.as_local() {
-        let id = tcx.hir().local_def_id_to_hir_id(def_id);
-        primary_body_of(tcx, id).is_some()
+        primary_body_of(tcx.hir().get_by_def_id(def_id)).is_some()
     } else {
         false
     }
@@ -198,143 +197,140 @@ fn typeck_with_fallback<'tcx>(
     }
 
     let id = tcx.hir().local_def_id_to_hir_id(def_id);
+    let node = tcx.hir().get(id);
     let span = tcx.hir().span(id);
 
     // Figure out what primary body this item has.
-    let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| {
+    let (body_id, body_ty, fn_sig) = primary_body_of(node).unwrap_or_else(|| {
         span_bug!(span, "can't type-check body of {:?}", def_id);
     });
     let body = tcx.hir().body(body_id);
 
-    let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
-        let param_env = tcx.param_env(def_id);
-        let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) {
-            param_env.without_const()
+    let param_env = tcx.param_env(def_id);
+    let param_env = if tcx.has_attr(def_id.to_def_id(), sym::rustc_do_not_const_check) {
+        param_env.without_const()
+    } else {
+        param_env
+    };
+    let inh = Inherited::new(tcx, def_id);
+    let mut fcx = FnCtxt::new(&inh, param_env, def_id);
+
+    if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
+        let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
+            fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None)
         } else {
-            param_env
+            tcx.fn_sig(def_id).subst_identity()
         };
-        let mut fcx = FnCtxt::new(&inh, param_env, def_id);
-
-        if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
-            let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
-                fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None)
-            } else {
-                tcx.fn_sig(def_id).subst_identity()
-            };
 
-            check_abi(tcx, id, span, fn_sig.abi());
+        check_abi(tcx, id, span, fn_sig.abi());
 
-            // Compute the function signature from point of view of inside the fn.
-            let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
-            let fn_sig = fcx.normalize(body.value.span, fn_sig);
+        // Compute the function signature from point of view of inside the fn.
+        let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
+        let fn_sig = fcx.normalize(body.value.span, fn_sig);
 
-            check_fn(&mut fcx, fn_sig, decl, def_id, body, None);
-        } else {
-            let expected_type = body_ty
-                .and_then(|ty| match ty.kind {
-                    hir::TyKind::Infer => Some(fcx.astconv().ast_ty_to_ty(ty)),
-                    _ => None,
-                })
-                .unwrap_or_else(|| match tcx.hir().get(id) {
-                    Node::AnonConst(_) => match tcx.hir().get(tcx.hir().parent_id(id)) {
-                        Node::Expr(&hir::Expr {
-                            kind: hir::ExprKind::ConstBlock(ref anon_const),
-                            ..
-                        }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
-                            kind: TypeVariableOriginKind::TypeInference,
-                            span,
-                        }),
-                        Node::Ty(&hir::Ty {
-                            kind: hir::TyKind::Typeof(ref anon_const), ..
-                        }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
-                            kind: TypeVariableOriginKind::TypeInference,
-                            span,
-                        }),
-                        Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
-                        | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
-                            let operand_ty =
-                                asm.operands.iter().find_map(|(op, _op_sp)| match op {
-                                    hir::InlineAsmOperand::Const { anon_const }
-                                        if anon_const.hir_id == id =>
-                                    {
-                                        // Inline assembly constants must be integers.
-                                        Some(fcx.next_int_var())
-                                    }
-                                    hir::InlineAsmOperand::SymFn { anon_const }
-                                        if anon_const.hir_id == id =>
-                                    {
-                                        Some(fcx.next_ty_var(TypeVariableOrigin {
-                                            kind: TypeVariableOriginKind::MiscVariable,
-                                            span,
-                                        }))
-                                    }
-                                    _ => None,
-                                });
-                            operand_ty.unwrap_or_else(fallback)
+        check_fn(&mut fcx, fn_sig, decl, def_id, body, None);
+    } else {
+        let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = body_ty {
+            Some(fcx.next_ty_var(TypeVariableOrigin {
+                kind: TypeVariableOriginKind::TypeInference,
+                span,
+            }))
+        } else if let Node::AnonConst(_) = node {
+            match tcx.hir().get(tcx.hir().parent_id(id)) {
+                Node::Expr(&hir::Expr {
+                    kind: hir::ExprKind::ConstBlock(ref anon_const), ..
+                }) if anon_const.hir_id == id => Some(fcx.next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::TypeInference,
+                    span,
+                })),
+                Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), .. })
+                    if anon_const.hir_id == id =>
+                {
+                    Some(fcx.next_ty_var(TypeVariableOrigin {
+                        kind: TypeVariableOriginKind::TypeInference,
+                        span,
+                    }))
+                }
+                Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
+                | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
+                    asm.operands.iter().find_map(|(op, _op_sp)| match op {
+                        hir::InlineAsmOperand::Const { anon_const } if anon_const.hir_id == id => {
+                            // Inline assembly constants must be integers.
+                            Some(fcx.next_int_var())
+                        }
+                        hir::InlineAsmOperand::SymFn { anon_const } if anon_const.hir_id == id => {
+                            Some(fcx.next_ty_var(TypeVariableOrigin {
+                                kind: TypeVariableOriginKind::MiscVariable,
+                                span,
+                            }))
                         }
-                        _ => fallback(),
-                    },
-                    _ => fallback(),
-                });
+                        _ => None,
+                    })
+                }
+                _ => None,
+            }
+        } else {
+            None
+        };
+        let expected_type = expected_type.unwrap_or_else(fallback);
 
-            let expected_type = fcx.normalize(body.value.span, expected_type);
-            fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
+        let expected_type = fcx.normalize(body.value.span, expected_type);
+        fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
 
-            // Gather locals in statics (because of block expressions).
-            GatherLocalsVisitor::new(&fcx).visit_body(body);
+        // Gather locals in statics (because of block expressions).
+        GatherLocalsVisitor::new(&fcx).visit_body(body);
 
-            fcx.check_expr_coercable_to_type(&body.value, expected_type, None);
+        fcx.check_expr_coercable_to_type(&body.value, expected_type, None);
 
-            fcx.write_ty(id, expected_type);
-        };
+        fcx.write_ty(id, expected_type);
+    };
 
-        fcx.type_inference_fallback();
-
-        // Even though coercion casts provide type hints, we check casts after fallback for
-        // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
-        fcx.check_casts();
-        fcx.select_obligations_where_possible(|_| {});
-
-        // Closure and generator analysis may run after fallback
-        // because they don't constrain other type variables.
-        // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
-        let prev_constness = fcx.param_env.constness();
-        fcx.param_env = fcx.param_env.without_const();
-        fcx.closure_analyze(body);
-        fcx.param_env = fcx.param_env.with_constness(prev_constness);
-        assert!(fcx.deferred_call_resolutions.borrow().is_empty());
-        // Before the generator analysis, temporary scopes shall be marked to provide more
-        // precise information on types to be captured.
-        fcx.resolve_rvalue_scopes(def_id.to_def_id());
-
-        for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
-            let ty = fcx.normalize(span, ty);
-            fcx.require_type_is_sized(ty, span, code);
-        }
+    fcx.type_inference_fallback();
+
+    // Even though coercion casts provide type hints, we check casts after fallback for
+    // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
+    fcx.check_casts();
+    fcx.select_obligations_where_possible(|_| {});
+
+    // Closure and generator analysis may run after fallback
+    // because they don't constrain other type variables.
+    // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
+    let prev_constness = fcx.param_env.constness();
+    fcx.param_env = fcx.param_env.without_const();
+    fcx.closure_analyze(body);
+    fcx.param_env = fcx.param_env.with_constness(prev_constness);
+    assert!(fcx.deferred_call_resolutions.borrow().is_empty());
+    // Before the generator analysis, temporary scopes shall be marked to provide more
+    // precise information on types to be captured.
+    fcx.resolve_rvalue_scopes(def_id.to_def_id());
+
+    for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
+        let ty = fcx.normalize(span, ty);
+        fcx.require_type_is_sized(ty, span, code);
+    }
 
-        fcx.select_obligations_where_possible(|_| {});
+    fcx.select_obligations_where_possible(|_| {});
 
-        debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
+    debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
 
-        // This must be the last thing before `report_ambiguity_errors`.
-        fcx.resolve_generator_interiors(def_id.to_def_id());
+    // This must be the last thing before `report_ambiguity_errors`.
+    fcx.resolve_generator_interiors(def_id.to_def_id());
 
-        debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
+    debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
 
-        if let None = fcx.infcx.tainted_by_errors() {
-            fcx.report_ambiguity_errors();
-        }
+    if let None = fcx.infcx.tainted_by_errors() {
+        fcx.report_ambiguity_errors();
+    }
 
-        if let None = fcx.infcx.tainted_by_errors() {
-            fcx.check_transmutes();
-        }
+    if let None = fcx.infcx.tainted_by_errors() {
+        fcx.check_transmutes();
+    }
 
-        fcx.check_asms();
+    fcx.check_asms();
 
-        fcx.infcx.skip_region_resolution();
+    fcx.infcx.skip_region_resolution();
 
-        fcx.resolve_type_vars_in_body(body)
-    });
+    let typeck_results = fcx.resolve_type_vars_in_body(body);
 
     // Consistency check our TypeckResults instance can hold all ItemLocalIds
     // it will need to hold.
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index e5d2fb2ea2807..043892410ceaa 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -110,7 +110,7 @@ pub fn create_session(
     add_configuration(&mut cfg, &mut sess, &*codegen_backend);
 
     let mut check_cfg = config::to_crate_check_config(check_cfg);
-    check_cfg.fill_well_known();
+    check_cfg.fill_well_known(&sess.target);
 
     sess.parse_sess.config = cfg;
     sess.parse_sess.check_config = check_cfg;
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 5eba208e3ed73..f6888e55ad431 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1849,20 +1849,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         &mut self,
         path_str: &str,
         ns: Namespace,
-        mut parent_scope: ParentScope<'a>,
+        parent_scope: ParentScope<'a>,
     ) -> Option<Res> {
         let mut segments =
             Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
         if let Some(segment) = segments.first_mut() {
-            if segment.ident.name == kw::Crate {
-                // FIXME: `resolve_path` always resolves `crate` to the current crate root, but
-                // rustdoc wants it to resolve to the `parent_scope`'s crate root. This trick of
-                // replacing `crate` with `self` and changing the current module should achieve
-                // the same effect.
-                segment.ident.name = kw::SelfLower;
-                parent_scope.module =
-                    self.expect_module(parent_scope.module.def_id().krate.as_def_id());
-            } else if segment.ident.name == kw::Empty {
+            if segment.ident.name == kw::Empty {
                 segment.ident.name = kw::PathRoot;
             }
         }
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index d4e4ace889b03..485c3f554625a 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1137,7 +1137,7 @@ impl CrateCheckConfig {
     }
 
     /// Fills a `CrateCheckConfig` with well-known configuration values.
-    fn fill_well_known_values(&mut self) {
+    fn fill_well_known_values(&mut self, current_target: &Target) {
         if !self.well_known_values {
             return;
         }
@@ -1229,6 +1229,7 @@ impl CrateCheckConfig {
             for target in TARGETS
                 .iter()
                 .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
+                .chain(iter::once(current_target.clone()))
             {
                 values_target_os.insert(Symbol::intern(&target.options.os));
                 values_target_family
@@ -1243,9 +1244,9 @@ impl CrateCheckConfig {
         }
     }
 
-    pub fn fill_well_known(&mut self) {
+    pub fn fill_well_known(&mut self, current_target: &Target) {
         self.fill_well_known_names();
-        self.fill_well_known_values();
+        self.fill_well_known_values(current_target);
     }
 }
 
diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs
index 89feb361ddc11..0be274a3822d3 100644
--- a/library/alloc/src/collections/vec_deque/drain.rs
+++ b/library/alloc/src/collections/vec_deque/drain.rs
@@ -52,36 +52,22 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> {
         }
     }
 
-    // Only returns pointers to the slices, as that's
-    // all we need to drop them. May only be called if `self.remaining != 0`.
+    // Only returns pointers to the slices, as that's all we need
+    // to drop them. May only be called if `self.remaining != 0`.
     unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) {
         unsafe {
             let deque = self.deque.as_ref();
-            // FIXME: This is doing almost exactly the same thing as the else branch in `VecDeque::slice_ranges`.
-            // Unfortunately, we can't just call `slice_ranges` here, as the deque's `len` is currently
-            // just `drain_start`, so the range check would (almost) always panic. Between temporarily
-            // adjusting the deques `len` to call `slice_ranges`, and just copy pasting the `slice_ranges`
-            // implementation, this seemed like the less hacky solution, though it might be good to
-            // find a better one in the future.
-
-            // because `self.remaining != 0`, we know that `self.idx < deque.original_len`, so it's a valid
-            // logical index.
-            let wrapped_start = deque.to_physical_idx(self.idx);
-
-            let head_len = deque.capacity() - wrapped_start;
-
-            let (a_range, b_range) = if head_len >= self.remaining {
-                (wrapped_start..wrapped_start + self.remaining, 0..0)
-            } else {
-                let tail_len = self.remaining - head_len;
-                (wrapped_start..deque.capacity(), 0..tail_len)
-            };
-
-            // SAFETY: the range `self.idx..self.idx+self.remaining` lies strictly inside
-            // the range `0..deque.original_len`. because of this, and because of the fact
-            // that we acquire `a_range` and `b_range` exactly like `slice_ranges` would,
-            // it's guaranteed that `a_range` and `b_range` represent valid ranges into
-            // the deques buffer.
+
+            // We know that `self.idx + self.remaining <= deque.len <= usize::MAX`, so this won't overflow.
+            let logical_remaining_range = self.idx..self.idx + self.remaining;
+
+            // SAFETY: `logical_remaining_range` represents the
+            // range into the logical buffer of elements that
+            // haven't been drained yet, so they're all initialized,
+            // and `slice::range(start..end, end) == start..end`,
+            // so the preconditions for `slice_ranges` are met.
+            let (a_range, b_range) =
+                deque.slice_ranges(logical_remaining_range.clone(), logical_remaining_range.end);
             (deque.buffer_range(a_range), deque.buffer_range(b_range))
         }
     }
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 8317ac431a5e1..48e907e402ccf 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -1156,7 +1156,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[inline]
     #[stable(feature = "deque_extras_15", since = "1.5.0")]
     pub fn as_slices(&self) -> (&[T], &[T]) {
-        let (a_range, b_range) = self.slice_ranges(..);
+        let (a_range, b_range) = self.slice_ranges(.., self.len);
         // SAFETY: `slice_ranges` always returns valid ranges into
         // the physical buffer.
         unsafe { (&*self.buffer_range(a_range), &*self.buffer_range(b_range)) }
@@ -1190,7 +1190,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     #[inline]
     #[stable(feature = "deque_extras_15", since = "1.5.0")]
     pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
-        let (a_range, b_range) = self.slice_ranges(..);
+        let (a_range, b_range) = self.slice_ranges(.., self.len);
         // SAFETY: `slice_ranges` always returns valid ranges into
         // the physical buffer.
         unsafe { (&mut *self.buffer_range(a_range), &mut *self.buffer_range(b_range)) }
@@ -1232,19 +1232,28 @@ impl<T, A: Allocator> VecDeque<T, A> {
 
     /// Given a range into the logical buffer of the deque, this function
     /// return two ranges into the physical buffer that correspond to
-    /// the given range.
-    fn slice_ranges<R>(&self, range: R) -> (Range<usize>, Range<usize>)
+    /// the given range. The `len` parameter should usually just be `self.len`;
+    /// the reason it's passed explicitly is that if the deque is wrapped in
+    /// a `Drain`, then `self.len` is not actually the length of the deque.
+    ///
+    /// # Safety
+    ///
+    /// This function is always safe to call. For the resulting ranges to be valid
+    /// ranges into the physical buffer, the caller must ensure that the result of
+    /// calling `slice::range(range, ..len)` represents a valid range into the
+    /// logical buffer, and that all elements in that range are initialized.
+    fn slice_ranges<R>(&self, range: R, len: usize) -> (Range<usize>, Range<usize>)
     where
         R: RangeBounds<usize>,
     {
-        let Range { start, end } = slice::range(range, ..self.len);
+        let Range { start, end } = slice::range(range, ..len);
         let len = end - start;
 
         if len == 0 {
             (0..0, 0..0)
         } else {
-            // `slice::range` guarantees that `start <= end <= self.len`.
-            // because `len != 0`, we know that `start < end`, so `start < self.len`
+            // `slice::range` guarantees that `start <= end <= len`.
+            // because `len != 0`, we know that `start < end`, so `start < len`
             // and the indexing is valid.
             let wrapped_start = self.to_physical_idx(start);
 
@@ -1290,7 +1299,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     where
         R: RangeBounds<usize>,
     {
-        let (a_range, b_range) = self.slice_ranges(range);
+        let (a_range, b_range) = self.slice_ranges(range, self.len);
         // SAFETY: The ranges returned by `slice_ranges`
         // are valid ranges into the physical buffer, so
         // it's ok to pass them to `buffer_range` and
@@ -1330,7 +1339,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
     where
         R: RangeBounds<usize>,
     {
-        let (a_range, b_range) = self.slice_ranges(range);
+        let (a_range, b_range) = self.slice_ranges(range, self.len);
         // SAFETY: The ranges returned by `slice_ranges`
         // are valid ranges into the physical buffer, so
         // it's ok to pass them to `buffer_range` and
diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs
index 29fc0835d7666..2b8782ddf4482 100644
--- a/library/std/src/sys/common/mod.rs
+++ b/library/std/src/sys/common/mod.rs
@@ -12,6 +12,7 @@
 
 pub mod alloc;
 pub mod small_c_string;
+pub mod thread_local;
 
 #[cfg(test)]
 mod tests;
diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs
new file mode 100644
index 0000000000000..2addcc4a759d0
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/fast_local.rs
@@ -0,0 +1,276 @@
+#[doc(hidden)]
+#[macro_export]
+#[allow_internal_unstable(
+    thread_local_internals,
+    cfg_target_thread_local,
+    thread_local,
+    libstd_thread_internals
+)]
+#[allow_internal_unsafe]
+macro_rules! __thread_local_inner {
+    // used to generate the `LocalKey` value for const-initialized thread locals
+    (@key $t:ty, const $init:expr) => {{
+        #[cfg_attr(not(windows), inline)] // see comments below
+        #[deny(unsafe_op_in_unsafe_fn)]
+        unsafe fn __getit(
+            _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+        ) -> $crate::option::Option<&'static $t> {
+            const INIT_EXPR: $t = $init;
+            // If the platform has support for `#[thread_local]`, use it.
+            #[thread_local]
+            static mut VAL: $t = INIT_EXPR;
+
+            // If a dtor isn't needed we can do something "very raw" and
+            // just get going.
+            if !$crate::mem::needs_drop::<$t>() {
+                unsafe {
+                    return $crate::option::Option::Some(&VAL)
+                }
+            }
+
+            // 0 == dtor not registered
+            // 1 == dtor registered, dtor not run
+            // 2 == dtor registered and is running or has run
+            #[thread_local]
+            static mut STATE: $crate::primitive::u8 = 0;
+
+            unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) {
+                let ptr = ptr as *mut $t;
+
+                unsafe {
+                    $crate::debug_assert_eq!(STATE, 1);
+                    STATE = 2;
+                    $crate::ptr::drop_in_place(ptr);
+                }
+            }
+
+            unsafe {
+                match STATE {
+                    // 0 == we haven't registered a destructor, so do
+                    //   so now.
+                    0 => {
+                        $crate::thread::__LocalKeyInner::<$t>::register_dtor(
+                            $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8,
+                            destroy,
+                        );
+                        STATE = 1;
+                        $crate::option::Option::Some(&VAL)
+                    }
+                    // 1 == the destructor is registered and the value
+                    //   is valid, so return the pointer.
+                    1 => $crate::option::Option::Some(&VAL),
+                    // otherwise the destructor has already run, so we
+                    // can't give access.
+                    _ => $crate::option::Option::None,
+                }
+            }
+        }
+
+        unsafe {
+            $crate::thread::LocalKey::new(__getit)
+        }
+    }};
+
+    // used to generate the `LocalKey` value for `thread_local!`
+    (@key $t:ty, $init:expr) => {
+        {
+            #[inline]
+            fn __init() -> $t { $init }
+
+            // When reading this function you might ask "why is this inlined
+            // everywhere other than Windows?", and that's a very reasonable
+            // question to ask. The short story is that it segfaults rustc if
+            // this function is inlined. The longer story is that Windows looks
+            // to not support `extern` references to thread locals across DLL
+            // boundaries. This appears to at least not be supported in the ABI
+            // that LLVM implements.
+            //
+            // Because of this we never inline on Windows, but we do inline on
+            // other platforms (where external references to thread locals
+            // across DLLs are supported). A better fix for this would be to
+            // inline this function on Windows, but only for "statically linked"
+            // components. For example if two separately compiled rlibs end up
+            // getting linked into a DLL then it's fine to inline this function
+            // across that boundary. It's only not fine to inline this function
+            // across a DLL boundary. Unfortunately rustc doesn't currently
+            // have this sort of logic available in an attribute, and it's not
+            // clear that rustc is even equipped to answer this (it's more of a
+            // Cargo question kinda). This means that, unfortunately, Windows
+            // gets the pessimistic path for now where it's never inlined.
+            //
+            // The issue of "should enable on Windows sometimes" is #84933
+            #[cfg_attr(not(windows), inline)]
+            unsafe fn __getit(
+                init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+            ) -> $crate::option::Option<&'static $t> {
+                #[thread_local]
+                static __KEY: $crate::thread::__LocalKeyInner<$t> =
+                    $crate::thread::__LocalKeyInner::<$t>::new();
+
+                // FIXME: remove the #[allow(...)] marker when macros don't
+                // raise warning for missing/extraneous unsafe blocks anymore.
+                // See https://github.com/rust-lang/rust/issues/74838.
+                #[allow(unused_unsafe)]
+                unsafe {
+                    __KEY.get(move || {
+                        if let $crate::option::Option::Some(init) = init {
+                            if let $crate::option::Option::Some(value) = init.take() {
+                                return value;
+                            } else if $crate::cfg!(debug_assertions) {
+                                $crate::unreachable!("missing default value");
+                            }
+                        }
+                        __init()
+                    })
+                }
+            }
+
+            unsafe {
+                $crate::thread::LocalKey::new(__getit)
+            }
+        }
+    };
+    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+            $crate::__thread_local_inner!(@key $t, $($init)*);
+    }
+}
+
+#[doc(hidden)]
+pub mod fast {
+    use super::super::lazy::LazyKeyInner;
+    use crate::cell::Cell;
+    use crate::sys::thread_local_dtor::register_dtor;
+    use crate::{fmt, mem, panic};
+
+    #[derive(Copy, Clone)]
+    enum DtorState {
+        Unregistered,
+        Registered,
+        RunningOrHasRun,
+    }
+
+    // This data structure has been carefully constructed so that the fast path
+    // only contains one branch on x86. That optimization is necessary to avoid
+    // duplicated tls lookups on OSX.
+    //
+    // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
+    pub struct Key<T> {
+        // If `LazyKeyInner::get` returns `None`, that indicates either:
+        //   * The value has never been initialized
+        //   * The value is being recursively initialized
+        //   * The value has already been destroyed or is being destroyed
+        // To determine which kind of `None`, check `dtor_state`.
+        //
+        // This is very optimizer friendly for the fast path - initialized but
+        // not yet dropped.
+        inner: LazyKeyInner<T>,
+
+        // Metadata to keep track of the state of the destructor. Remember that
+        // this variable is thread-local, not global.
+        dtor_state: Cell<DtorState>,
+    }
+
+    impl<T> fmt::Debug for Key<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Key").finish_non_exhaustive()
+        }
+    }
+
+    impl<T> Key<T> {
+        pub const fn new() -> Key<T> {
+            Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }
+        }
+
+        // note that this is just a publicly-callable function only for the
+        // const-initialized form of thread locals, basically a way to call the
+        // free `register_dtor` function defined elsewhere in std.
+        pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+            unsafe {
+                register_dtor(a, dtor);
+            }
+        }
+
+        pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
+            // SAFETY: See the definitions of `LazyKeyInner::get` and
+            // `try_initialize` for more information.
+            //
+            // The caller must ensure no mutable references are ever active to
+            // the inner cell or the inner T when this is called.
+            // The `try_initialize` is dependant on the passed `init` function
+            // for this.
+            unsafe {
+                match self.inner.get() {
+                    Some(val) => Some(val),
+                    None => self.try_initialize(init),
+                }
+            }
+        }
+
+        // `try_initialize` is only called once per fast thread local variable,
+        // except in corner cases where thread_local dtors reference other
+        // thread_local's, or it is being recursively initialized.
+        //
+        // Macos: Inlining this function can cause two `tlv_get_addr` calls to
+        // be performed for every call to `Key::get`.
+        // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
+        #[inline(never)]
+        unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
+            // SAFETY: See comment above (this function doc).
+            if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } {
+                // SAFETY: See comment above (this function doc).
+                Some(unsafe { self.inner.initialize(init) })
+            } else {
+                None
+            }
+        }
+
+        // `try_register_dtor` is only called once per fast thread local
+        // variable, except in corner cases where thread_local dtors reference
+        // other thread_local's, or it is being recursively initialized.
+        unsafe fn try_register_dtor(&self) -> bool {
+            match self.dtor_state.get() {
+                DtorState::Unregistered => {
+                    // SAFETY: dtor registration happens before initialization.
+                    // Passing `self` as a pointer while using `destroy_value<T>`
+                    // is safe because the function will build a pointer to a
+                    // Key<T>, which is the type of self and so find the correct
+                    // size.
+                    unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) };
+                    self.dtor_state.set(DtorState::Registered);
+                    true
+                }
+                DtorState::Registered => {
+                    // recursively initialized
+                    true
+                }
+                DtorState::RunningOrHasRun => false,
+            }
+        }
+    }
+
+    unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
+        let ptr = ptr as *mut Key<T>;
+
+        // SAFETY:
+        //
+        // The pointer `ptr` has been built just above and comes from
+        // `try_register_dtor` where it is originally a Key<T> coming from `self`,
+        // making it non-NUL and of the correct type.
+        //
+        // Right before we run the user destructor be sure to set the
+        // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
+        // causes future calls to `get` to run `try_initialize_drop` again,
+        // which will now fail, and return `None`.
+        //
+        // Wrap the call in a catch to ensure unwinding is caught in the event
+        // a panic takes place in a destructor.
+        if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
+            let value = (*ptr).inner.take();
+            (*ptr).dtor_state.set(DtorState::RunningOrHasRun);
+            drop(value);
+        })) {
+            rtabort!("thread local panicked on drop");
+        }
+    }
+}
diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs
new file mode 100644
index 0000000000000..1fee84a04349c
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/mod.rs
@@ -0,0 +1,109 @@
+//! The following module declarations are outside cfg_if because the internal
+//! `__thread_local_internal` macro does not seem to be exported properly when using cfg_if
+#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
+
+#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))]
+mod fast_local;
+#[cfg(all(
+    not(target_thread_local),
+    not(all(target_family = "wasm", not(target_feature = "atomics")))
+))]
+mod os_local;
+#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
+mod static_local;
+
+#[cfg(not(test))]
+cfg_if::cfg_if! {
+    if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] {
+        #[doc(hidden)]
+        pub use static_local::statik::Key;
+    } else if #[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] {
+        #[doc(hidden)]
+        pub use fast_local::fast::Key;
+    } else if #[cfg(all(not(target_thread_local), not(all(target_family = "wasm", not(target_feature = "atomics")))))] {
+        #[doc(hidden)]
+        pub use os_local::os::Key;
+    }
+}
+
+#[doc(hidden)]
+#[cfg(test)]
+pub use realstd::thread::__LocalKeyInner as Key;
+
+mod lazy {
+    use crate::cell::UnsafeCell;
+    use crate::hint;
+    use crate::mem;
+
+    pub struct LazyKeyInner<T> {
+        inner: UnsafeCell<Option<T>>,
+    }
+
+    impl<T> LazyKeyInner<T> {
+        pub const fn new() -> LazyKeyInner<T> {
+            LazyKeyInner { inner: UnsafeCell::new(None) }
+        }
+
+        pub unsafe fn get(&self) -> Option<&'static T> {
+            // SAFETY: The caller must ensure no reference is ever handed out to
+            // the inner cell nor mutable reference to the Option<T> inside said
+            // cell. This make it safe to hand a reference, though the lifetime
+            // of 'static is itself unsafe, making the get method unsafe.
+            unsafe { (*self.inner.get()).as_ref() }
+        }
+
+        /// The caller must ensure that no reference is active: this method
+        /// needs unique access.
+        pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T {
+            // Execute the initialization up front, *then* move it into our slot,
+            // just in case initialization fails.
+            let value = init();
+            let ptr = self.inner.get();
+
+            // SAFETY:
+            //
+            // note that this can in theory just be `*ptr = Some(value)`, but due to
+            // the compiler will currently codegen that pattern with something like:
+            //
+            //      ptr::drop_in_place(ptr)
+            //      ptr::write(ptr, Some(value))
+            //
+            // Due to this pattern it's possible for the destructor of the value in
+            // `ptr` (e.g., if this is being recursively initialized) to re-access
+            // TLS, in which case there will be a `&` and `&mut` pointer to the same
+            // value (an aliasing violation). To avoid setting the "I'm running a
+            // destructor" flag we just use `mem::replace` which should sequence the
+            // operations a little differently and make this safe to call.
+            //
+            // The precondition also ensures that we are the only one accessing
+            // `self` at the moment so replacing is fine.
+            unsafe {
+                let _ = mem::replace(&mut *ptr, Some(value));
+            }
+
+            // SAFETY: With the call to `mem::replace` it is guaranteed there is
+            // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked`
+            // will never be reached.
+            unsafe {
+                // After storing `Some` we want to get a reference to the contents of
+                // what we just stored. While we could use `unwrap` here and it should
+                // always work it empirically doesn't seem to always get optimized away,
+                // which means that using something like `try_with` can pull in
+                // panicking code and cause a large size bloat.
+                match *ptr {
+                    Some(ref x) => x,
+                    None => hint::unreachable_unchecked(),
+                }
+            }
+        }
+
+        /// The other methods hand out references while taking &self.
+        /// As such, callers of this method must ensure no `&` and `&mut` are
+        /// available and used at the same time.
+        #[allow(unused)]
+        pub unsafe fn take(&mut self) -> Option<T> {
+            // SAFETY: See doc comment for this method.
+            unsafe { (*self.inner.get()).take() }
+        }
+    }
+}
diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs
new file mode 100644
index 0000000000000..6f6560c4aa949
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/os_local.rs
@@ -0,0 +1,217 @@
+#[doc(hidden)]
+#[macro_export]
+#[allow_internal_unstable(
+    thread_local_internals,
+    cfg_target_thread_local,
+    thread_local,
+    libstd_thread_internals
+)]
+#[allow_internal_unsafe]
+macro_rules! __thread_local_inner {
+    // used to generate the `LocalKey` value for const-initialized thread locals
+    (@key $t:ty, const $init:expr) => {{
+        #[cfg_attr(not(windows), inline)] // see comments below
+        #[deny(unsafe_op_in_unsafe_fn)]
+        unsafe fn __getit(
+            _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+        ) -> $crate::option::Option<&'static $t> {
+            const INIT_EXPR: $t = $init;
+
+                        // On platforms without `#[thread_local]` we fall back to the
+            // same implementation as below for os thread locals.
+            #[inline]
+            const fn __init() -> $t { INIT_EXPR }
+            static __KEY: $crate::thread::__LocalKeyInner<$t> =
+                $crate::thread::__LocalKeyInner::new();
+            #[allow(unused_unsafe)]
+            unsafe {
+                __KEY.get(move || {
+                    if let $crate::option::Option::Some(init) = _init {
+                        if let $crate::option::Option::Some(value) = init.take() {
+                            return value;
+                        } else if $crate::cfg!(debug_assertions) {
+                            $crate::unreachable!("missing initial value");
+                        }
+                    }
+                    __init()
+                })
+            }
+        }
+
+        unsafe {
+            $crate::thread::LocalKey::new(__getit)
+        }
+    }};
+
+    // used to generate the `LocalKey` value for `thread_local!`
+    (@key $t:ty, $init:expr) => {
+        {
+            #[inline]
+            fn __init() -> $t { $init }
+
+            // When reading this function you might ask "why is this inlined
+            // everywhere other than Windows?", and that's a very reasonable
+            // question to ask. The short story is that it segfaults rustc if
+            // this function is inlined. The longer story is that Windows looks
+            // to not support `extern` references to thread locals across DLL
+            // boundaries. This appears to at least not be supported in the ABI
+            // that LLVM implements.
+            //
+            // Because of this we never inline on Windows, but we do inline on
+            // other platforms (where external references to thread locals
+            // across DLLs are supported). A better fix for this would be to
+            // inline this function on Windows, but only for "statically linked"
+            // components. For example if two separately compiled rlibs end up
+            // getting linked into a DLL then it's fine to inline this function
+            // across that boundary. It's only not fine to inline this function
+            // across a DLL boundary. Unfortunately rustc doesn't currently
+            // have this sort of logic available in an attribute, and it's not
+            // clear that rustc is even equipped to answer this (it's more of a
+            // Cargo question kinda). This means that, unfortunately, Windows
+            // gets the pessimistic path for now where it's never inlined.
+            //
+            // The issue of "should enable on Windows sometimes" is #84933
+            #[cfg_attr(not(windows), inline)]
+            unsafe fn __getit(
+                init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+            ) -> $crate::option::Option<&'static $t> {
+                static __KEY: $crate::thread::__LocalKeyInner<$t> =
+                    $crate::thread::__LocalKeyInner::new();
+
+                // FIXME: remove the #[allow(...)] marker when macros don't
+                // raise warning for missing/extraneous unsafe blocks anymore.
+                // See https://github.com/rust-lang/rust/issues/74838.
+                #[allow(unused_unsafe)]
+                unsafe {
+                    __KEY.get(move || {
+                        if let $crate::option::Option::Some(init) = init {
+                            if let $crate::option::Option::Some(value) = init.take() {
+                                return value;
+                            } else if $crate::cfg!(debug_assertions) {
+                                $crate::unreachable!("missing default value");
+                            }
+                        }
+                        __init()
+                    })
+                }
+            }
+
+            unsafe {
+                $crate::thread::LocalKey::new(__getit)
+            }
+        }
+    };
+    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+            $crate::__thread_local_inner!(@key $t, $($init)*);
+    }
+}
+
+#[doc(hidden)]
+pub mod os {
+    use super::super::lazy::LazyKeyInner;
+    use crate::cell::Cell;
+    use crate::sys_common::thread_local_key::StaticKey as OsStaticKey;
+    use crate::{fmt, marker, panic, ptr};
+
+    /// Use a regular global static to store this key; the state provided will then be
+    /// thread-local.
+    pub struct Key<T> {
+        // OS-TLS key that we'll use to key off.
+        os: OsStaticKey,
+        marker: marker::PhantomData<Cell<T>>,
+    }
+
+    impl<T> fmt::Debug for Key<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Key").finish_non_exhaustive()
+        }
+    }
+
+    unsafe impl<T> Sync for Key<T> {}
+
+    struct Value<T: 'static> {
+        inner: LazyKeyInner<T>,
+        key: &'static Key<T>,
+    }
+
+    impl<T: 'static> Key<T> {
+        #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
+        pub const fn new() -> Key<T> {
+            Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData }
+        }
+
+        /// It is a requirement for the caller to ensure that no mutable
+        /// reference is active when this method is called.
+        pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
+            // SAFETY: See the documentation for this method.
+            let ptr = unsafe { self.os.get() as *mut Value<T> };
+            if ptr.addr() > 1 {
+                // SAFETY: the check ensured the pointer is safe (its destructor
+                // is not running) + it is coming from a trusted source (self).
+                if let Some(ref value) = unsafe { (*ptr).inner.get() } {
+                    return Some(value);
+                }
+            }
+            // SAFETY: At this point we are sure we have no value and so
+            // initializing (or trying to) is safe.
+            unsafe { self.try_initialize(init) }
+        }
+
+        // `try_initialize` is only called once per os thread local variable,
+        // except in corner cases where thread_local dtors reference other
+        // thread_local's, or it is being recursively initialized.
+        unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
+            // SAFETY: No mutable references are ever handed out meaning getting
+            // the value is ok.
+            let ptr = unsafe { self.os.get() as *mut Value<T> };
+            if ptr.addr() == 1 {
+                // destructor is running
+                return None;
+            }
+
+            let ptr = if ptr.is_null() {
+                // If the lookup returned null, we haven't initialized our own
+                // local copy, so do that now.
+                let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self }));
+                // SAFETY: At this point we are sure there is no value inside
+                // ptr so setting it will not affect anyone else.
+                unsafe {
+                    self.os.set(ptr as *mut u8);
+                }
+                ptr
+            } else {
+                // recursive initialization
+                ptr
+            };
+
+            // SAFETY: ptr has been ensured as non-NUL just above an so can be
+            // dereferenced safely.
+            unsafe { Some((*ptr).inner.initialize(init)) }
+        }
+    }
+
+    unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
+        // SAFETY:
+        //
+        // The OS TLS ensures that this key contains a null value when this
+        // destructor starts to run. We set it back to a sentinel value of 1 to
+        // ensure that any future calls to `get` for this thread will return
+        // `None`.
+        //
+        // Note that to prevent an infinite loop we reset it back to null right
+        // before we return from the destructor ourselves.
+        //
+        // Wrap the call in a catch to ensure unwinding is caught in the event
+        // a panic takes place in a destructor.
+        if let Err(_) = panic::catch_unwind(|| unsafe {
+            let ptr = Box::from_raw(ptr as *mut Value<T>);
+            let key = ptr.key;
+            key.os.set(ptr::invalid_mut(1));
+            drop(ptr);
+            key.os.set(ptr::null_mut());
+        }) {
+            rtabort!("thread local panicked on drop");
+        }
+    }
+}
diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs
new file mode 100644
index 0000000000000..ec4f2a12b7ee4
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/static_local.rs
@@ -0,0 +1,115 @@
+#[doc(hidden)]
+#[macro_export]
+#[allow_internal_unstable(
+    thread_local_internals,
+    cfg_target_thread_local,
+    thread_local,
+    libstd_thread_internals
+)]
+#[allow_internal_unsafe]
+macro_rules! __thread_local_inner {
+    // used to generate the `LocalKey` value for const-initialized thread locals
+    (@key $t:ty, const $init:expr) => {{
+        #[inline] // see comments below
+        #[deny(unsafe_op_in_unsafe_fn)]
+        unsafe fn __getit(
+            _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+        ) -> $crate::option::Option<&'static $t> {
+            const INIT_EXPR: $t = $init;
+
+            // wasm without atomics maps directly to `static mut`, and dtors
+            // aren't implemented because thread dtors aren't really a thing
+            // on wasm right now
+            //
+            // FIXME(#84224) this should come after the `target_thread_local`
+            // block.
+            static mut VAL: $t = INIT_EXPR;
+            unsafe { $crate::option::Option::Some(&VAL) }
+        }
+
+        unsafe {
+            $crate::thread::LocalKey::new(__getit)
+        }
+    }};
+
+    // used to generate the `LocalKey` value for `thread_local!`
+    (@key $t:ty, $init:expr) => {
+        {
+            #[inline]
+            fn __init() -> $t { $init }
+            #[inline]
+            unsafe fn __getit(
+                init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+            ) -> $crate::option::Option<&'static $t> {
+                static __KEY: $crate::thread::__LocalKeyInner<$t> =
+                    $crate::thread::__LocalKeyInner::new();
+
+                // FIXME: remove the #[allow(...)] marker when macros don't
+                // raise warning for missing/extraneous unsafe blocks anymore.
+                // See https://github.com/rust-lang/rust/issues/74838.
+                #[allow(unused_unsafe)]
+                unsafe {
+                    __KEY.get(move || {
+                        if let $crate::option::Option::Some(init) = init {
+                            if let $crate::option::Option::Some(value) = init.take() {
+                                return value;
+                            } else if $crate::cfg!(debug_assertions) {
+                                $crate::unreachable!("missing default value");
+                            }
+                        }
+                        __init()
+                    })
+                }
+            }
+
+            unsafe {
+                $crate::thread::LocalKey::new(__getit)
+            }
+        }
+    };
+    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+            $crate::__thread_local_inner!(@key $t, $($init)*);
+    }
+}
+
+/// On some targets like wasm there's no threads, so no need to generate
+/// thread locals and we can instead just use plain statics!
+#[doc(hidden)]
+pub mod statik {
+    use super::super::lazy::LazyKeyInner;
+    use crate::fmt;
+
+    pub struct Key<T> {
+        inner: LazyKeyInner<T>,
+    }
+
+    unsafe impl<T> Sync for Key<T> {}
+
+    impl<T> fmt::Debug for Key<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Key").finish_non_exhaustive()
+        }
+    }
+
+    impl<T> Key<T> {
+        pub const fn new() -> Key<T> {
+            Key { inner: LazyKeyInner::new() }
+        }
+
+        pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> {
+            // SAFETY: The caller must ensure no reference is ever handed out to
+            // the inner cell nor mutable reference to the Option<T> inside said
+            // cell. This make it safe to hand a reference, though the lifetime
+            // of 'static is itself unsafe, making the get method unsafe.
+            let value = unsafe {
+                match self.inner.get() {
+                    Some(ref value) => value,
+                    None => self.inner.initialize(init),
+                }
+            };
+
+            Some(value)
+        }
+    }
+}
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index cf7c2e05a2e9d..7fdf03acc14d9 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -173,200 +173,6 @@ macro_rules! thread_local {
     );
 }
 
-#[doc(hidden)]
-#[unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
-#[macro_export]
-#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)]
-#[allow_internal_unsafe]
-macro_rules! __thread_local_inner {
-    // used to generate the `LocalKey` value for const-initialized thread locals
-    (@key $t:ty, const $init:expr) => {{
-        #[cfg_attr(not(windows), inline)] // see comments below
-        #[deny(unsafe_op_in_unsafe_fn)]
-        unsafe fn __getit(
-            _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
-        ) -> $crate::option::Option<&'static $t> {
-            const INIT_EXPR: $t = $init;
-
-            // wasm without atomics maps directly to `static mut`, and dtors
-            // aren't implemented because thread dtors aren't really a thing
-            // on wasm right now
-            //
-            // FIXME(#84224) this should come after the `target_thread_local`
-            // block.
-            #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-            {
-                static mut VAL: $t = INIT_EXPR;
-                unsafe { $crate::option::Option::Some(&VAL) }
-            }
-
-            // If the platform has support for `#[thread_local]`, use it.
-            #[cfg(all(
-                target_thread_local,
-                not(all(target_family = "wasm", not(target_feature = "atomics"))),
-            ))]
-            {
-                #[thread_local]
-                static mut VAL: $t = INIT_EXPR;
-
-                // If a dtor isn't needed we can do something "very raw" and
-                // just get going.
-                if !$crate::mem::needs_drop::<$t>() {
-                    unsafe {
-                        return $crate::option::Option::Some(&VAL)
-                    }
-                }
-
-                // 0 == dtor not registered
-                // 1 == dtor registered, dtor not run
-                // 2 == dtor registered and is running or has run
-                #[thread_local]
-                static mut STATE: $crate::primitive::u8 = 0;
-
-                unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) {
-                    let ptr = ptr as *mut $t;
-
-                    unsafe {
-                        $crate::debug_assert_eq!(STATE, 1);
-                        STATE = 2;
-                        $crate::ptr::drop_in_place(ptr);
-                    }
-                }
-
-                unsafe {
-                    match STATE {
-                        // 0 == we haven't registered a destructor, so do
-                        //   so now.
-                        0 => {
-                            $crate::thread::__FastLocalKeyInner::<$t>::register_dtor(
-                                $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8,
-                                destroy,
-                            );
-                            STATE = 1;
-                            $crate::option::Option::Some(&VAL)
-                        }
-                        // 1 == the destructor is registered and the value
-                        //   is valid, so return the pointer.
-                        1 => $crate::option::Option::Some(&VAL),
-                        // otherwise the destructor has already run, so we
-                        // can't give access.
-                        _ => $crate::option::Option::None,
-                    }
-                }
-            }
-
-            // On platforms without `#[thread_local]` we fall back to the
-            // same implementation as below for os thread locals.
-            #[cfg(all(
-                not(target_thread_local),
-                not(all(target_family = "wasm", not(target_feature = "atomics"))),
-            ))]
-            {
-                #[inline]
-                const fn __init() -> $t { INIT_EXPR }
-                static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
-                    $crate::thread::__OsLocalKeyInner::new();
-                #[allow(unused_unsafe)]
-                unsafe {
-                    __KEY.get(move || {
-                        if let $crate::option::Option::Some(init) = _init {
-                            if let $crate::option::Option::Some(value) = init.take() {
-                                return value;
-                            } else if $crate::cfg!(debug_assertions) {
-                                $crate::unreachable!("missing initial value");
-                            }
-                        }
-                        __init()
-                    })
-                }
-            }
-        }
-
-        unsafe {
-            $crate::thread::LocalKey::new(__getit)
-        }
-    }};
-
-    // used to generate the `LocalKey` value for `thread_local!`
-    (@key $t:ty, $init:expr) => {
-        {
-            #[inline]
-            fn __init() -> $t { $init }
-
-            // When reading this function you might ask "why is this inlined
-            // everywhere other than Windows?", and that's a very reasonable
-            // question to ask. The short story is that it segfaults rustc if
-            // this function is inlined. The longer story is that Windows looks
-            // to not support `extern` references to thread locals across DLL
-            // boundaries. This appears to at least not be supported in the ABI
-            // that LLVM implements.
-            //
-            // Because of this we never inline on Windows, but we do inline on
-            // other platforms (where external references to thread locals
-            // across DLLs are supported). A better fix for this would be to
-            // inline this function on Windows, but only for "statically linked"
-            // components. For example if two separately compiled rlibs end up
-            // getting linked into a DLL then it's fine to inline this function
-            // across that boundary. It's only not fine to inline this function
-            // across a DLL boundary. Unfortunately rustc doesn't currently
-            // have this sort of logic available in an attribute, and it's not
-            // clear that rustc is even equipped to answer this (it's more of a
-            // Cargo question kinda). This means that, unfortunately, Windows
-            // gets the pessimistic path for now where it's never inlined.
-            //
-            // The issue of "should enable on Windows sometimes" is #84933
-            #[cfg_attr(not(windows), inline)]
-            unsafe fn __getit(
-                init: $crate::option::Option<&mut $crate::option::Option<$t>>,
-            ) -> $crate::option::Option<&'static $t> {
-                #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-                static __KEY: $crate::thread::__StaticLocalKeyInner<$t> =
-                    $crate::thread::__StaticLocalKeyInner::new();
-
-                #[thread_local]
-                #[cfg(all(
-                    target_thread_local,
-                    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-                ))]
-                static __KEY: $crate::thread::__FastLocalKeyInner<$t> =
-                    $crate::thread::__FastLocalKeyInner::new();
-
-                #[cfg(all(
-                    not(target_thread_local),
-                    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-                ))]
-                static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
-                    $crate::thread::__OsLocalKeyInner::new();
-
-                // FIXME: remove the #[allow(...)] marker when macros don't
-                // raise warning for missing/extraneous unsafe blocks anymore.
-                // See https://github.com/rust-lang/rust/issues/74838.
-                #[allow(unused_unsafe)]
-                unsafe {
-                    __KEY.get(move || {
-                        if let $crate::option::Option::Some(init) = init {
-                            if let $crate::option::Option::Some(value) = init.take() {
-                                return value;
-                            } else if $crate::cfg!(debug_assertions) {
-                                $crate::unreachable!("missing default value");
-                            }
-                        }
-                        __init()
-                    })
-                }
-            }
-
-            unsafe {
-                $crate::thread::LocalKey::new(__getit)
-            }
-        }
-    };
-    ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
-        $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
-            $crate::__thread_local_inner!(@key $t, $($init)*);
-    }
-}
-
 /// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with).
 #[stable(feature = "thread_local_try_with", since = "1.26.0")]
 #[non_exhaustive]
@@ -779,376 +585,3 @@ impl<T: 'static> LocalKey<RefCell<T>> {
         self.with(|cell| cell.replace(value))
     }
 }
-
-mod lazy {
-    use crate::cell::UnsafeCell;
-    use crate::hint;
-    use crate::mem;
-
-    pub struct LazyKeyInner<T> {
-        inner: UnsafeCell<Option<T>>,
-    }
-
-    impl<T> LazyKeyInner<T> {
-        pub const fn new() -> LazyKeyInner<T> {
-            LazyKeyInner { inner: UnsafeCell::new(None) }
-        }
-
-        pub unsafe fn get(&self) -> Option<&'static T> {
-            // SAFETY: The caller must ensure no reference is ever handed out to
-            // the inner cell nor mutable reference to the Option<T> inside said
-            // cell. This make it safe to hand a reference, though the lifetime
-            // of 'static is itself unsafe, making the get method unsafe.
-            unsafe { (*self.inner.get()).as_ref() }
-        }
-
-        /// The caller must ensure that no reference is active: this method
-        /// needs unique access.
-        pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T {
-            // Execute the initialization up front, *then* move it into our slot,
-            // just in case initialization fails.
-            let value = init();
-            let ptr = self.inner.get();
-
-            // SAFETY:
-            //
-            // note that this can in theory just be `*ptr = Some(value)`, but due to
-            // the compiler will currently codegen that pattern with something like:
-            //
-            //      ptr::drop_in_place(ptr)
-            //      ptr::write(ptr, Some(value))
-            //
-            // Due to this pattern it's possible for the destructor of the value in
-            // `ptr` (e.g., if this is being recursively initialized) to re-access
-            // TLS, in which case there will be a `&` and `&mut` pointer to the same
-            // value (an aliasing violation). To avoid setting the "I'm running a
-            // destructor" flag we just use `mem::replace` which should sequence the
-            // operations a little differently and make this safe to call.
-            //
-            // The precondition also ensures that we are the only one accessing
-            // `self` at the moment so replacing is fine.
-            unsafe {
-                let _ = mem::replace(&mut *ptr, Some(value));
-            }
-
-            // SAFETY: With the call to `mem::replace` it is guaranteed there is
-            // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked`
-            // will never be reached.
-            unsafe {
-                // After storing `Some` we want to get a reference to the contents of
-                // what we just stored. While we could use `unwrap` here and it should
-                // always work it empirically doesn't seem to always get optimized away,
-                // which means that using something like `try_with` can pull in
-                // panicking code and cause a large size bloat.
-                match *ptr {
-                    Some(ref x) => x,
-                    None => hint::unreachable_unchecked(),
-                }
-            }
-        }
-
-        /// The other methods hand out references while taking &self.
-        /// As such, callers of this method must ensure no `&` and `&mut` are
-        /// available and used at the same time.
-        #[allow(unused)]
-        pub unsafe fn take(&mut self) -> Option<T> {
-            // SAFETY: See doc comment for this method.
-            unsafe { (*self.inner.get()).take() }
-        }
-    }
-}
-
-/// On some targets like wasm there's no threads, so no need to generate
-/// thread locals and we can instead just use plain statics!
-#[doc(hidden)]
-#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-pub mod statik {
-    use super::lazy::LazyKeyInner;
-    use crate::fmt;
-
-    pub struct Key<T> {
-        inner: LazyKeyInner<T>,
-    }
-
-    unsafe impl<T> Sync for Key<T> {}
-
-    impl<T> fmt::Debug for Key<T> {
-        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.debug_struct("Key").finish_non_exhaustive()
-        }
-    }
-
-    impl<T> Key<T> {
-        pub const fn new() -> Key<T> {
-            Key { inner: LazyKeyInner::new() }
-        }
-
-        pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> {
-            // SAFETY: The caller must ensure no reference is ever handed out to
-            // the inner cell nor mutable reference to the Option<T> inside said
-            // cell. This make it safe to hand a reference, though the lifetime
-            // of 'static is itself unsafe, making the get method unsafe.
-            let value = unsafe {
-                match self.inner.get() {
-                    Some(ref value) => value,
-                    None => self.inner.initialize(init),
-                }
-            };
-
-            Some(value)
-        }
-    }
-}
-
-#[doc(hidden)]
-#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics"))),))]
-pub mod fast {
-    use super::lazy::LazyKeyInner;
-    use crate::cell::Cell;
-    use crate::sys::thread_local_dtor::register_dtor;
-    use crate::{fmt, mem, panic};
-
-    #[derive(Copy, Clone)]
-    enum DtorState {
-        Unregistered,
-        Registered,
-        RunningOrHasRun,
-    }
-
-    // This data structure has been carefully constructed so that the fast path
-    // only contains one branch on x86. That optimization is necessary to avoid
-    // duplicated tls lookups on OSX.
-    //
-    // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
-    pub struct Key<T> {
-        // If `LazyKeyInner::get` returns `None`, that indicates either:
-        //   * The value has never been initialized
-        //   * The value is being recursively initialized
-        //   * The value has already been destroyed or is being destroyed
-        // To determine which kind of `None`, check `dtor_state`.
-        //
-        // This is very optimizer friendly for the fast path - initialized but
-        // not yet dropped.
-        inner: LazyKeyInner<T>,
-
-        // Metadata to keep track of the state of the destructor. Remember that
-        // this variable is thread-local, not global.
-        dtor_state: Cell<DtorState>,
-    }
-
-    impl<T> fmt::Debug for Key<T> {
-        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.debug_struct("Key").finish_non_exhaustive()
-        }
-    }
-
-    impl<T> Key<T> {
-        pub const fn new() -> Key<T> {
-            Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }
-        }
-
-        // note that this is just a publicly-callable function only for the
-        // const-initialized form of thread locals, basically a way to call the
-        // free `register_dtor` function defined elsewhere in std.
-        pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
-            unsafe {
-                register_dtor(a, dtor);
-            }
-        }
-
-        pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
-            // SAFETY: See the definitions of `LazyKeyInner::get` and
-            // `try_initialize` for more information.
-            //
-            // The caller must ensure no mutable references are ever active to
-            // the inner cell or the inner T when this is called.
-            // The `try_initialize` is dependant on the passed `init` function
-            // for this.
-            unsafe {
-                match self.inner.get() {
-                    Some(val) => Some(val),
-                    None => self.try_initialize(init),
-                }
-            }
-        }
-
-        // `try_initialize` is only called once per fast thread local variable,
-        // except in corner cases where thread_local dtors reference other
-        // thread_local's, or it is being recursively initialized.
-        //
-        // Macos: Inlining this function can cause two `tlv_get_addr` calls to
-        // be performed for every call to `Key::get`.
-        // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
-        #[inline(never)]
-        unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
-            // SAFETY: See comment above (this function doc).
-            if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } {
-                // SAFETY: See comment above (this function doc).
-                Some(unsafe { self.inner.initialize(init) })
-            } else {
-                None
-            }
-        }
-
-        // `try_register_dtor` is only called once per fast thread local
-        // variable, except in corner cases where thread_local dtors reference
-        // other thread_local's, or it is being recursively initialized.
-        unsafe fn try_register_dtor(&self) -> bool {
-            match self.dtor_state.get() {
-                DtorState::Unregistered => {
-                    // SAFETY: dtor registration happens before initialization.
-                    // Passing `self` as a pointer while using `destroy_value<T>`
-                    // is safe because the function will build a pointer to a
-                    // Key<T>, which is the type of self and so find the correct
-                    // size.
-                    unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) };
-                    self.dtor_state.set(DtorState::Registered);
-                    true
-                }
-                DtorState::Registered => {
-                    // recursively initialized
-                    true
-                }
-                DtorState::RunningOrHasRun => false,
-            }
-        }
-    }
-
-    unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
-        let ptr = ptr as *mut Key<T>;
-
-        // SAFETY:
-        //
-        // The pointer `ptr` has been built just above and comes from
-        // `try_register_dtor` where it is originally a Key<T> coming from `self`,
-        // making it non-NUL and of the correct type.
-        //
-        // Right before we run the user destructor be sure to set the
-        // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
-        // causes future calls to `get` to run `try_initialize_drop` again,
-        // which will now fail, and return `None`.
-        //
-        // Wrap the call in a catch to ensure unwinding is caught in the event
-        // a panic takes place in a destructor.
-        if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
-            let value = (*ptr).inner.take();
-            (*ptr).dtor_state.set(DtorState::RunningOrHasRun);
-            drop(value);
-        })) {
-            rtabort!("thread local panicked on drop");
-        }
-    }
-}
-
-#[doc(hidden)]
-#[cfg(all(
-    not(target_thread_local),
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-pub mod os {
-    use super::lazy::LazyKeyInner;
-    use crate::cell::Cell;
-    use crate::sys_common::thread_local_key::StaticKey as OsStaticKey;
-    use crate::{fmt, marker, panic, ptr};
-
-    /// Use a regular global static to store this key; the state provided will then be
-    /// thread-local.
-    pub struct Key<T> {
-        // OS-TLS key that we'll use to key off.
-        os: OsStaticKey,
-        marker: marker::PhantomData<Cell<T>>,
-    }
-
-    impl<T> fmt::Debug for Key<T> {
-        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.debug_struct("Key").finish_non_exhaustive()
-        }
-    }
-
-    unsafe impl<T> Sync for Key<T> {}
-
-    struct Value<T: 'static> {
-        inner: LazyKeyInner<T>,
-        key: &'static Key<T>,
-    }
-
-    impl<T: 'static> Key<T> {
-        #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
-        pub const fn new() -> Key<T> {
-            Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData }
-        }
-
-        /// It is a requirement for the caller to ensure that no mutable
-        /// reference is active when this method is called.
-        pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
-            // SAFETY: See the documentation for this method.
-            let ptr = unsafe { self.os.get() as *mut Value<T> };
-            if ptr.addr() > 1 {
-                // SAFETY: the check ensured the pointer is safe (its destructor
-                // is not running) + it is coming from a trusted source (self).
-                if let Some(ref value) = unsafe { (*ptr).inner.get() } {
-                    return Some(value);
-                }
-            }
-            // SAFETY: At this point we are sure we have no value and so
-            // initializing (or trying to) is safe.
-            unsafe { self.try_initialize(init) }
-        }
-
-        // `try_initialize` is only called once per os thread local variable,
-        // except in corner cases where thread_local dtors reference other
-        // thread_local's, or it is being recursively initialized.
-        unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
-            // SAFETY: No mutable references are ever handed out meaning getting
-            // the value is ok.
-            let ptr = unsafe { self.os.get() as *mut Value<T> };
-            if ptr.addr() == 1 {
-                // destructor is running
-                return None;
-            }
-
-            let ptr = if ptr.is_null() {
-                // If the lookup returned null, we haven't initialized our own
-                // local copy, so do that now.
-                let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self }));
-                // SAFETY: At this point we are sure there is no value inside
-                // ptr so setting it will not affect anyone else.
-                unsafe {
-                    self.os.set(ptr as *mut u8);
-                }
-                ptr
-            } else {
-                // recursive initialization
-                ptr
-            };
-
-            // SAFETY: ptr has been ensured as non-NUL just above an so can be
-            // dereferenced safely.
-            unsafe { Some((*ptr).inner.initialize(init)) }
-        }
-    }
-
-    unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
-        // SAFETY:
-        //
-        // The OS TLS ensures that this key contains a null value when this
-        // destructor starts to run. We set it back to a sentinel value of 1 to
-        // ensure that any future calls to `get` for this thread will return
-        // `None`.
-        //
-        // Note that to prevent an infinite loop we reset it back to null right
-        // before we return from the destructor ourselves.
-        //
-        // Wrap the call in a catch to ensure unwinding is caught in the event
-        // a panic takes place in a destructor.
-        if let Err(_) = panic::catch_unwind(|| unsafe {
-            let ptr = Box::from_raw(ptr as *mut Value<T>);
-            let key = ptr.key;
-            key.os.set(ptr::invalid_mut(1));
-            drop(ptr);
-            key.os.set(ptr::null_mut());
-        }) {
-            rtabort!("thread local panicked on drop");
-        }
-    }
-}
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 489af776798ad..b9aaf5f6e15db 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -203,44 +203,9 @@ pub use self::local::{AccessError, LocalKey};
 // by the elf linker. "static" is for single-threaded platforms where a global
 // static is sufficient.
 
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(not(test))]
-#[cfg(all(
-    target_thread_local,
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-#[doc(hidden)]
-pub use self::local::fast::Key as __FastLocalKeyInner;
-// when building for tests, use real std's type
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(test)]
-#[cfg(all(
-    target_thread_local,
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-pub use realstd::thread::__FastLocalKeyInner;
-
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(not(test))]
-#[cfg(all(
-    not(target_thread_local),
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
 #[doc(hidden)]
-pub use self::local::os::Key as __OsLocalKeyInner;
-// when building for tests, use real std's type
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(test)]
-#[cfg(all(
-    not(target_thread_local),
-    not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-pub use realstd::thread::__OsLocalKeyInner;
-
 #[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-#[doc(hidden)]
-pub use self::local::statik::Key as __StaticLocalKeyInner;
+pub use crate::sys::common::thread_local::Key as __LocalKeyInner;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Builder
diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
index 262cef3454ad3..3282ff720c478 100644
--- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md
+++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
@@ -213,7 +213,7 @@ See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
 
 ## Example
 
-```text
+```rust
 #![feature(naked_functions)]
 
 use std::arch::asm;
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 8dbfaf4bbc93d..0295de8437ef1 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -346,6 +346,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
                                     self.cache,
                                 ),
                                 aliases: item.attrs.get_doc_aliases(),
+                                deprecation: item.deprecation(self.tcx),
                             });
                         }
                     }
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index ed1eb66b97c36..63cd0e04a288a 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -17,10 +17,11 @@ use super::print_item::{full_path, item_path, print_item};
 use super::search_index::build_index;
 use super::write_shared::write_shared;
 use super::{
-    collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes,
-    LinkFromSrc, StylePath,
+    collect_spans_and_sources, scrape_examples_help,
+    sidebar::print_sidebar,
+    sidebar::{sidebar_module_like, Sidebar},
+    AllTypes, LinkFromSrc, StylePath,
 };
-
 use crate::clean::{self, types::ExternalLocation, ExternalCrate};
 use crate::config::{ModuleSorting, RenderOptions};
 use crate::docfs::{DocFS, PathError};
@@ -35,6 +36,7 @@ use crate::html::url_parts_builder::UrlPartsBuilder;
 use crate::html::{layout, sources, static_files};
 use crate::scrape_examples::AllCallLocations;
 use crate::try_err;
+use askama::Template;
 
 /// Major driving force in all rustdoc rendering. This contains information
 /// about where in the tree-like hierarchy rendering is occurring and controls
@@ -600,15 +602,18 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         };
         let all = shared.all.replace(AllTypes::new());
         let mut sidebar = Buffer::html();
-        write!(sidebar, "<h2 class=\"location\"><a href=\"#\">Crate {}</a></h2>", crate_name);
-
-        let mut items = Buffer::html();
-        sidebar_module_like(&mut items, all.item_sections());
-        if !items.is_empty() {
-            sidebar.push_str("<div class=\"sidebar-elems\">");
-            sidebar.push_buffer(items);
-            sidebar.push_str("</div>");
-        }
+
+        let blocks = sidebar_module_like(all.item_sections());
+        let bar = Sidebar {
+            title_prefix: "Crate ",
+            title: crate_name.as_str(),
+            is_crate: false,
+            version: "",
+            blocks: vec![blocks],
+            path: String::new(),
+        };
+
+        bar.render_into(&mut sidebar).unwrap();
 
         let v = layout::render(
             &shared.layout,
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index e6a040d02e565..da1f1cf5eccfc 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -30,6 +30,7 @@ mod tests;
 
 mod context;
 mod print_item;
+mod sidebar;
 mod span_map;
 mod write_shared;
 
@@ -46,14 +47,13 @@ use std::rc::Rc;
 use std::str;
 use std::string::ToString;
 
+use askama::Template;
 use rustc_ast_pretty::pprust;
 use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::Mutability;
 use rustc_middle::middle::stability;
-use rustc_middle::ty;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::{
     symbol::{sym, Symbol},
@@ -104,6 +104,7 @@ pub(crate) struct IndexItem {
     pub(crate) parent_idx: Option<usize>,
     pub(crate) search_type: Option<IndexItemFunctionType>,
     pub(crate) aliases: Box<[Symbol]>,
+    pub(crate) deprecation: Option<Deprecation>,
 }
 
 /// A type used for the search index.
@@ -417,7 +418,7 @@ fn document(
     if let Some(ref name) = item.name {
         info!("Documenting {}", name);
     }
-    document_item_info(w, cx, item, parent);
+    document_item_info(cx, item, parent).render_into(w).unwrap();
     if parent.is_none() {
         document_full_collapsible(w, item, cx, heading_offset);
     } else {
@@ -459,7 +460,7 @@ fn document_short(
     parent: &clean::Item,
     show_def_docs: bool,
 ) {
-    document_item_info(w, cx, item, Some(parent));
+    document_item_info(cx, item, Some(parent)).render_into(w).unwrap();
     if !show_def_docs {
         return;
     }
@@ -531,25 +532,23 @@ fn document_full_inner(
     }
 }
 
+#[derive(Template)]
+#[template(path = "item_info.html")]
+struct ItemInfo {
+    items: Vec<ShortItemInfo>,
+}
 /// Add extra information about an item such as:
 ///
 /// * Stability
 /// * Deprecated
 /// * Required features (through the `doc_cfg` feature)
 fn document_item_info(
-    w: &mut Buffer,
     cx: &mut Context<'_>,
     item: &clean::Item,
     parent: Option<&clean::Item>,
-) {
-    let item_infos = short_item_info(item, cx, parent);
-    if !item_infos.is_empty() {
-        w.write_str("<span class=\"item-info\">");
-        for info in item_infos {
-            w.write_str(&info);
-        }
-        w.write_str("</span>");
-    }
+) -> ItemInfo {
+    let items = short_item_info(item, cx, parent);
+    ItemInfo { items }
 }
 
 fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<String> {
@@ -567,7 +566,25 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<Strin
         cfg
     );
 
-    Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html()))
+    Some(cfg?.render_long_html())
+}
+
+#[derive(Template)]
+#[template(path = "short_item_info.html")]
+enum ShortItemInfo {
+    /// A message describing the deprecation of this item
+    Deprecation {
+        message: String,
+    },
+    /// The feature corresponding to an unstable item, and optionally
+    /// a tracking issue URL and number.
+    Unstable {
+        feature: String,
+        tracking: Option<(String, u32)>,
+    },
+    Portability {
+        message: String,
+    },
 }
 
 /// Render the stability, deprecation and portability information that is displayed at the top of
@@ -576,7 +593,7 @@ fn short_item_info(
     item: &clean::Item,
     cx: &mut Context<'_>,
     parent: Option<&clean::Item>,
-) -> Vec<String> {
+) -> Vec<ShortItemInfo> {
     let mut extra_info = vec![];
 
     if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) =
@@ -602,15 +619,10 @@ fn short_item_info(
         if let Some(note) = note {
             let note = note.as_str();
             let html = MarkdownItemInfo(note, &mut cx.id_map);
-            message.push_str(&format!(": {}", html.into_string()));
-        }
-        extra_info.push(format!(
-            "<div class=\"stab deprecated\">\
-                 <span class=\"emoji\">👎</span>\
-                 <span>{}</span>\
-             </div>",
-            message,
-        ));
+            message.push_str(": ");
+            message.push_str(&html.into_string());
+        }
+        extra_info.push(ShortItemInfo::Deprecation { message });
     }
 
     // Render unstable items. But don't render "rustc_private" crates (internal compiler crates).
@@ -621,26 +633,17 @@ fn short_item_info(
         .filter(|stab| stab.feature != sym::rustc_private)
         .map(|stab| (stab.level, stab.feature))
     {
-        let mut message = "<span class=\"emoji\">🔬</span>\
-             <span>This is a nightly-only experimental API."
-            .to_owned();
-
-        let mut feature = format!("<code>{}</code>", Escape(feature.as_str()));
-        if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) {
-            feature.push_str(&format!(
-                "&nbsp;<a href=\"{url}{issue}\">#{issue}</a>",
-                url = url,
-                issue = issue
-            ));
-        }
-
-        message.push_str(&format!(" ({})</span>", feature));
-
-        extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message));
+        let tracking = if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue)
+        {
+            Some((url.clone(), issue.get()))
+        } else {
+            None
+        };
+        extra_info.push(ShortItemInfo::Unstable { feature: feature.to_string(), tracking });
     }
 
-    if let Some(portability) = portability(item, parent) {
-        extra_info.push(portability);
+    if let Some(message) = portability(item, parent) {
+        extra_info.push(ShortItemInfo::Portability { message });
     }
 
     extra_info
@@ -1472,7 +1475,9 @@ fn render_impl(
                         // We need the stability of the item from the trait
                         // because impls can't have a stability.
                         if item.doc_value().is_some() {
-                            document_item_info(&mut info_buffer, cx, it, Some(parent));
+                            document_item_info(cx, it, Some(parent))
+                                .render_into(&mut info_buffer)
+                                .unwrap();
                             document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
                             short_documented = false;
                         } else {
@@ -1489,7 +1494,9 @@ fn render_impl(
                         }
                     }
                 } else {
-                    document_item_info(&mut info_buffer, cx, item, Some(parent));
+                    document_item_info(cx, item, Some(parent))
+                        .render_into(&mut info_buffer)
+                        .unwrap();
                     if rendering_params.show_def_docs {
                         document_full(&mut doc_buffer, item, cx, HeadingOffset::H5);
                         short_documented = false;
@@ -1862,161 +1869,17 @@ pub(crate) fn render_impl_summary(
     let is_trait = inner_impl.trait_.is_some();
     if is_trait {
         if let Some(portability) = portability(&i.impl_item, Some(parent)) {
-            write!(w, "<span class=\"item-info\">{}</span>", portability);
+            write!(
+                w,
+                "<span class=\"item-info\"><div class=\"stab portability\">{}</div></span>",
+                portability
+            );
         }
     }
 
     w.write_str("</section>");
 }
 
-fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
-    if it.is_struct()
-        || it.is_trait()
-        || it.is_primitive()
-        || it.is_union()
-        || it.is_enum()
-        || it.is_mod()
-        || it.is_typedef()
-    {
-        write!(
-            buffer,
-            "<h2 class=\"location\"><a href=\"#\">{}{}</a></h2>",
-            match *it.kind {
-                clean::ModuleItem(..) =>
-                    if it.is_crate() {
-                        "Crate "
-                    } else {
-                        "Module "
-                    },
-                _ => "",
-            },
-            it.name.as_ref().unwrap()
-        );
-    }
-
-    buffer.write_str("<div class=\"sidebar-elems\">");
-    if it.is_crate() {
-        write!(buffer, "<ul class=\"block\">");
-        if let Some(ref version) = cx.cache().crate_version {
-            write!(buffer, "<li class=\"version\">Version {}</li>", Escape(version));
-        }
-        write!(buffer, "<li><a id=\"all-types\" href=\"all.html\">All Items</a></li>");
-        buffer.write_str("</ul>");
-    }
-
-    match *it.kind {
-        clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s),
-        clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t),
-        clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it),
-        clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u),
-        clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e),
-        clean::TypedefItem(_) => sidebar_typedef(cx, buffer, it),
-        clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items),
-        clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it),
-        _ => {}
-    }
-
-    // The sidebar is designed to display sibling functions, modules and
-    // other miscellaneous information. since there are lots of sibling
-    // items (and that causes quadratic growth in large modules),
-    // we refactor common parts into a shared JavaScript file per module.
-    // still, we don't move everything into JS because we want to preserve
-    // as much HTML as possible in order to allow non-JS-enabled browsers
-    // to navigate the documentation (though slightly inefficiently).
-
-    if !it.is_mod() {
-        let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect();
-
-        write!(buffer, "<h2><a href=\"index.html\">In {}</a></h2>", path);
-    }
-
-    // Closes sidebar-elems div.
-    buffer.write_str("</div>");
-}
-
-fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
-    if used_links.insert(url.clone()) {
-        return url;
-    }
-    let mut add = 1;
-    while !used_links.insert(format!("{}-{}", url, add)) {
-        add += 1;
-    }
-    format!("{}-{}", url, add)
-}
-
-struct SidebarLink {
-    name: Symbol,
-    url: String,
-}
-
-impl fmt::Display for SidebarLink {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "<a href=\"#{}\">{}</a>", self.url, self.name)
-    }
-}
-
-impl PartialEq for SidebarLink {
-    fn eq(&self, other: &Self) -> bool {
-        self.url == other.url
-    }
-}
-
-impl Eq for SidebarLink {}
-
-impl PartialOrd for SidebarLink {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for SidebarLink {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.url.cmp(&other.url)
-    }
-}
-
-fn get_methods(
-    i: &clean::Impl,
-    for_deref: bool,
-    used_links: &mut FxHashSet<String>,
-    deref_mut: bool,
-    tcx: TyCtxt<'_>,
-) -> Vec<SidebarLink> {
-    i.items
-        .iter()
-        .filter_map(|item| match item.name {
-            Some(name) if !name.is_empty() && item.is_method() => {
-                if !for_deref || should_render_item(item, deref_mut, tcx) {
-                    Some(SidebarLink {
-                        name,
-                        url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
-                    })
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        })
-        .collect::<Vec<_>>()
-}
-
-fn get_associated_constants(
-    i: &clean::Impl,
-    used_links: &mut FxHashSet<String>,
-) -> Vec<SidebarLink> {
-    i.items
-        .iter()
-        .filter_map(|item| match item.name {
-            Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink {
-                name,
-                url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
-            }),
-            _ => None,
-        })
-        .collect::<Vec<_>>()
-}
-
 pub(crate) fn small_url_encode(s: String) -> String {
     // These characters don't need to be escaped in a URI.
     // See https://url.spec.whatwg.org/#query-percent-encode-set
@@ -2082,232 +1945,6 @@ pub(crate) fn small_url_encode(s: String) -> String {
     }
 }
 
-pub(crate) fn sidebar_render_assoc_items(
-    cx: &Context<'_>,
-    out: &mut Buffer,
-    id_map: &mut IdMap,
-    concrete: Vec<&Impl>,
-    synthetic: Vec<&Impl>,
-    blanket_impl: Vec<&Impl>,
-) {
-    let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
-        let mut links = FxHashSet::default();
-
-        let mut ret = impls
-            .iter()
-            .filter_map(|it| {
-                let trait_ = it.inner_impl().trait_.as_ref()?;
-                let encoded =
-                    id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
-
-                let i_display = format!("{:#}", trait_.print(cx));
-                let out = Escape(&i_display);
-                let prefix = match it.inner_impl().polarity {
-                    ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
-                    ty::ImplPolarity::Negative => "!",
-                };
-                let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
-                if links.insert(generated.clone()) { Some(generated) } else { None }
-            })
-            .collect::<Vec<String>>();
-        ret.sort();
-        ret
-    };
-
-    let concrete_format = format_impls(concrete, id_map);
-    let synthetic_format = format_impls(synthetic, id_map);
-    let blanket_format = format_impls(blanket_impl, id_map);
-
-    if !concrete_format.is_empty() {
-        print_sidebar_block(
-            out,
-            "trait-implementations",
-            "Trait Implementations",
-            concrete_format.iter(),
-        );
-    }
-
-    if !synthetic_format.is_empty() {
-        print_sidebar_block(
-            out,
-            "synthetic-implementations",
-            "Auto Trait Implementations",
-            synthetic_format.iter(),
-        );
-    }
-
-    if !blanket_format.is_empty() {
-        print_sidebar_block(
-            out,
-            "blanket-implementations",
-            "Blanket Implementations",
-            blanket_format.iter(),
-        );
-    }
-}
-
-fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
-    let did = it.item_id.expect_def_id();
-    let cache = cx.cache();
-
-    if let Some(v) = cache.impls.get(&did) {
-        let mut used_links = FxHashSet::default();
-        let mut id_map = IdMap::new();
-
-        {
-            let used_links_bor = &mut used_links;
-            let mut assoc_consts = v
-                .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
-                .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor))
-                .collect::<Vec<_>>();
-            if !assoc_consts.is_empty() {
-                // We want links' order to be reproducible so we don't use unstable sort.
-                assoc_consts.sort();
-
-                print_sidebar_block(
-                    out,
-                    "implementations",
-                    "Associated Constants",
-                    assoc_consts.iter(),
-                );
-            }
-            let mut methods = v
-                .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
-                .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx()))
-                .collect::<Vec<_>>();
-            if !methods.is_empty() {
-                // We want links' order to be reproducible so we don't use unstable sort.
-                methods.sort();
-
-                print_sidebar_block(out, "implementations", "Methods", methods.iter());
-            }
-        }
-
-        if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
-            if let Some(impl_) =
-                v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
-            {
-                let mut derefs = DefIdSet::default();
-                derefs.insert(did);
-                sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
-            }
-
-            let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
-                v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
-            let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
-                concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
-
-            sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl);
-        }
-    }
-}
-
-fn sidebar_deref_methods(
-    cx: &Context<'_>,
-    out: &mut Buffer,
-    impl_: &Impl,
-    v: &[Impl],
-    derefs: &mut DefIdSet,
-    used_links: &mut FxHashSet<String>,
-) {
-    let c = cx.cache();
-
-    debug!("found Deref: {:?}", impl_);
-    if let Some((target, real_target)) =
-        impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
-            clean::AssocTypeItem(box ref t, _) => Some(match *t {
-                clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
-                _ => (&t.type_, &t.type_),
-            }),
-            _ => None,
-        })
-    {
-        debug!("found target, real_target: {:?} {:?}", target, real_target);
-        if let Some(did) = target.def_id(c) &&
-            let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
-            // `impl Deref<Target = S> for S`
-            (did == type_did || !derefs.insert(did))
-        {
-            // Avoid infinite cycles
-            return;
-        }
-        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
-        let inner_impl = target
-            .def_id(c)
-            .or_else(|| {
-                target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
-            })
-            .and_then(|did| c.impls.get(&did));
-        if let Some(impls) = inner_impl {
-            debug!("found inner_impl: {:?}", impls);
-            let mut ret = impls
-                .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
-                .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
-                .collect::<Vec<_>>();
-            if !ret.is_empty() {
-                let id = if let Some(target_def_id) = real_target.def_id(c) {
-                    cx.deref_id_map.get(&target_def_id).expect("Deref section without derived id")
-                } else {
-                    "deref-methods"
-                };
-                let title = format!(
-                    "Methods from {}&lt;Target={}&gt;",
-                    Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
-                    Escape(&format!("{:#}", real_target.print(cx))),
-                );
-                // We want links' order to be reproducible so we don't use unstable sort.
-                ret.sort();
-                print_sidebar_block(out, id, &title, ret.iter());
-            }
-        }
-
-        // Recurse into any further impls that might exist for `target`
-        if let Some(target_did) = target.def_id(c) &&
-            let Some(target_impls) = c.impls.get(&target_did) &&
-            let Some(target_deref_impl) = target_impls.iter().find(|i| {
-                i.inner_impl()
-                    .trait_
-                    .as_ref()
-                    .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
-                    .unwrap_or(false)
-            })
-        {
-            sidebar_deref_methods(
-                cx,
-                out,
-                target_deref_impl,
-                target_impls,
-                derefs,
-                used_links,
-            );
-        }
-    }
-}
-
-fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
-    let mut sidebar = Buffer::new();
-    let fields = get_struct_fields_name(&s.fields);
-
-    if !fields.is_empty() {
-        match s.ctor_kind {
-            None => {
-                print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
-            }
-            Some(CtorKind::Fn) => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
-            Some(CtorKind::Const) => {}
-        }
-    }
-
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
 fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String {
     match trait_ {
         Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))),
@@ -2328,131 +1965,6 @@ fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String
     }
 }
 
-fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) {
-    write!(buf, "<h3><a href=\"#{}\">{}</a></h3>", id, title);
-}
-
-fn print_sidebar_block(
-    buf: &mut Buffer,
-    id: &str,
-    title: &str,
-    items: impl Iterator<Item = impl fmt::Display>,
-) {
-    print_sidebar_title(buf, id, title);
-    buf.push_str("<ul class=\"block\">");
-    for item in items {
-        write!(buf, "<li>{}</li>", item);
-    }
-    buf.push_str("</ul>");
-}
-
-fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
-    buf.write_str("<section>");
-
-    fn print_sidebar_section(
-        out: &mut Buffer,
-        items: &[clean::Item],
-        id: &str,
-        title: &str,
-        filter: impl Fn(&clean::Item) -> bool,
-        mapper: impl Fn(&str) -> String,
-    ) {
-        let mut items: Vec<&str> = items
-            .iter()
-            .filter_map(|m| match m.name {
-                Some(ref name) if filter(m) => Some(name.as_str()),
-                _ => None,
-            })
-            .collect::<Vec<_>>();
-
-        if !items.is_empty() {
-            items.sort_unstable();
-            print_sidebar_block(out, id, title, items.into_iter().map(mapper));
-        }
-    }
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "required-associated-types",
-        "Required Associated Types",
-        |m| m.is_ty_associated_type(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "provided-associated-types",
-        "Provided Associated Types",
-        |m| m.is_associated_type(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "required-associated-consts",
-        "Required Associated Constants",
-        |m| m.is_ty_associated_const(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "provided-associated-consts",
-        "Provided Associated Constants",
-        |m| m.is_associated_const(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "required-methods",
-        "Required Methods",
-        |m| m.is_ty_method(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::TyMethod),
-    );
-
-    print_sidebar_section(
-        buf,
-        &t.items,
-        "provided-methods",
-        "Provided Methods",
-        |m| m.is_method(),
-        |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::Method),
-    );
-
-    if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
-        let mut res = implementors
-            .iter()
-            .filter(|i| !i.is_on_local_type(cx))
-            .filter_map(|i| extract_for_impl_name(&i.impl_item, cx))
-            .collect::<Vec<_>>();
-
-        if !res.is_empty() {
-            res.sort();
-            print_sidebar_block(
-                buf,
-                "foreign-impls",
-                "Implementations on Foreign Types",
-                res.iter().map(|(name, id)| format!("<a href=\"#{}\">{}</a>", id, Escape(name))),
-            );
-        }
-    }
-
-    sidebar_assoc_items(cx, buf, it);
-
-    print_sidebar_title(buf, "implementors", "Implementors");
-    if t.is_auto(cx.tcx()) {
-        print_sidebar_title(buf, "synthetic-implementors", "Auto Implementors");
-    }
-
-    buf.push_str("</section>")
-}
-
 /// Returns the list of implementations for the primitive reference type, filtering out any
 /// implementations that are on concrete or partially generic types, only keeping implementations
 /// of the form `impl<T> Trait for &T`.
@@ -2483,89 +1995,6 @@ pub(crate) fn get_filtered_impls_for_reference<'a>(
     (concrete, synthetic, blanket_impl)
 }
 
-fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let mut sidebar = Buffer::new();
-
-    if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
-        sidebar_assoc_items(cx, &mut sidebar, it);
-    } else {
-        let shared = Rc::clone(&cx.shared);
-        let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
-
-        sidebar_render_assoc_items(
-            cx,
-            &mut sidebar,
-            &mut IdMap::new(),
-            concrete,
-            synthetic,
-            blanket_impl,
-        );
-    }
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
-fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let mut sidebar = Buffer::new();
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
-fn get_struct_fields_name(fields: &[clean::Item]) -> Vec<String> {
-    let mut fields = fields
-        .iter()
-        .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
-        .filter_map(|f| {
-            f.name.map(|name| format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
-        })
-        .collect::<Vec<_>>();
-    fields.sort();
-    fields
-}
-
-fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
-    let mut sidebar = Buffer::new();
-    let fields = get_struct_fields_name(&u.fields);
-
-    if !fields.is_empty() {
-        print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
-    }
-
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
-fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
-    let mut sidebar = Buffer::new();
-
-    let mut variants = e
-        .variants()
-        .filter_map(|v| {
-            v.name
-                .as_ref()
-                .map(|name| format!("<a href=\"#variant.{name}\">{name}</a>", name = name))
-        })
-        .collect::<Vec<_>>();
-    if !variants.is_empty() {
-        variants.sort_unstable();
-        print_sidebar_block(&mut sidebar, "variants", "Variants", variants.iter());
-    }
-
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub(crate) enum ItemSection {
     Reexports,
@@ -2719,54 +2148,6 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
     }
 }
 
-pub(crate) fn sidebar_module_like(buf: &mut Buffer, item_sections_in_use: FxHashSet<ItemSection>) {
-    use std::fmt::Write as _;
-
-    let mut sidebar = String::new();
-
-    for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) {
-        let _ = write!(sidebar, "<li><a href=\"#{}\">{}</a></li>", sec.id(), sec.name());
-    }
-
-    if !sidebar.is_empty() {
-        write!(
-            buf,
-            "<section>\
-                 <ul class=\"block\">{}</ul>\
-             </section>",
-            sidebar
-        );
-    }
-}
-
-fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
-    let item_sections_in_use: FxHashSet<_> = items
-        .iter()
-        .filter(|it| {
-            !it.is_stripped()
-                && it
-                    .name
-                    .or_else(|| {
-                        if let clean::ImportItem(ref i) = *it.kind &&
-                            let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
-                    })
-                    .is_some()
-        })
-        .map(|it| item_ty_to_section(it.type_()))
-        .collect();
-
-    sidebar_module_like(buf, item_sections_in_use);
-}
-
-fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
-    let mut sidebar = Buffer::new();
-    sidebar_assoc_items(cx, &mut sidebar, it);
-
-    if !sidebar.is_empty() {
-        write!(buf, "<section>{}</section>", sidebar.into_inner());
-    }
-}
-
 /// Returns a list of all paths used in the type.
 /// This is used to help deduplicate imported impls
 /// for reexported types. If any of the contained
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 08796f10d9238..577497868f687 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -470,10 +470,11 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) ->
 
     // The trailing space after each tag is to space it properly against the rest of the docs.
     if let Some(depr) = &item.deprecation(tcx) {
-        let mut message = "Deprecated";
-        if !stability::deprecation_in_effect(depr) {
-            message = "Deprecation planned";
-        }
+        let message = if stability::deprecation_in_effect(depr) {
+            "Deprecated"
+        } else {
+            "Deprecation planned"
+        };
         tags += &tag_html("deprecated", "", message);
     }
 
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index e22ac6ec19b00..146221f5806b4 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -40,6 +40,7 @@ pub(crate) fn build_index<'tcx>(
                 parent_idx: None,
                 search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
                 aliases: item.attrs.get_doc_aliases(),
+                deprecation: item.deprecation(tcx),
             });
         }
     }
@@ -251,7 +252,17 @@ pub(crate) fn build_index<'tcx>(
             )?;
             crate_data.serialize_field(
                 "q",
-                &self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
+                &self
+                    .items
+                    .iter()
+                    .enumerate()
+                    // Serialize as an array of item indices and full paths
+                    .filter_map(
+                        |(index, item)| {
+                            if item.path.is_empty() { None } else { Some((index, &item.path)) }
+                        },
+                    )
+                    .collect::<Vec<_>>(),
             )?;
             crate_data.serialize_field(
                 "d",
@@ -304,6 +315,16 @@ pub(crate) fn build_index<'tcx>(
                     })
                     .collect::<Vec<_>>(),
             )?;
+            crate_data.serialize_field(
+                "c",
+                &self
+                    .items
+                    .iter()
+                    .enumerate()
+                    // Serialize as an array of deprecated item indices
+                    .filter_map(|(index, item)| item.deprecation.map(|_| index))
+                    .collect::<Vec<_>>(),
+            )?;
             crate_data.serialize_field(
                 "p",
                 &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
new file mode 100644
index 0000000000000..94ad4753d7cb6
--- /dev/null
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -0,0 +1,561 @@
+use std::{borrow::Cow, rc::Rc};
+
+use askama::Template;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::{def::CtorKind, def_id::DefIdSet};
+use rustc_middle::ty::{self, TyCtxt};
+
+use crate::{
+    clean,
+    formats::{item_type::ItemType, Impl},
+    html::{format::Buffer, markdown::IdMap},
+};
+
+use super::{item_ty_to_section, Context, ItemSection};
+
+#[derive(Template)]
+#[template(path = "sidebar.html")]
+pub(super) struct Sidebar<'a> {
+    pub(super) title_prefix: &'static str,
+    pub(super) title: &'a str,
+    pub(super) is_crate: bool,
+    pub(super) version: &'a str,
+    pub(super) blocks: Vec<LinkBlock<'a>>,
+    pub(super) path: String,
+}
+
+impl<'a> Sidebar<'a> {
+    /// Only create a `<section>` if there are any blocks
+    /// which should actually be rendered.
+    pub fn should_render_blocks(&self) -> bool {
+        self.blocks.iter().any(LinkBlock::should_render)
+    }
+}
+
+/// A sidebar section such as 'Methods'.
+pub(crate) struct LinkBlock<'a> {
+    /// The name of this section, e.g. 'Methods'
+    /// as well as the link to it, e.g. `#implementations`.
+    /// Will be rendered inside an `<h3>` tag
+    heading: Link<'a>,
+    links: Vec<Link<'a>>,
+    /// Render the heading even if there are no links
+    force_render: bool,
+}
+
+impl<'a> LinkBlock<'a> {
+    pub fn new(heading: Link<'a>, links: Vec<Link<'a>>) -> Self {
+        Self { heading, links, force_render: false }
+    }
+
+    pub fn forced(heading: Link<'a>) -> Self {
+        Self { heading, links: vec![], force_render: true }
+    }
+
+    pub fn should_render(&self) -> bool {
+        self.force_render || !self.links.is_empty()
+    }
+}
+
+/// A link to an item. Content should not be escaped.
+#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
+pub(crate) struct Link<'a> {
+    /// The content for the anchor tag
+    name: Cow<'a, str>,
+    /// The id of an anchor within the page (without a `#` prefix)
+    href: Cow<'a, str>,
+}
+
+impl<'a> Link<'a> {
+    pub fn new(href: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>) -> Self {
+        Self { href: href.into(), name: name.into() }
+    }
+    pub fn empty() -> Link<'static> {
+        Link::new("", "")
+    }
+}
+
+pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
+    let blocks: Vec<LinkBlock<'_>> = match *it.kind {
+        clean::StructItem(ref s) => sidebar_struct(cx, it, s),
+        clean::TraitItem(ref t) => sidebar_trait(cx, it, t),
+        clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
+        clean::UnionItem(ref u) => sidebar_union(cx, it, u),
+        clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
+        clean::TypedefItem(_) => sidebar_typedef(cx, it),
+        clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
+        clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
+        _ => vec![],
+    };
+    // The sidebar is designed to display sibling functions, modules and
+    // other miscellaneous information. since there are lots of sibling
+    // items (and that causes quadratic growth in large modules),
+    // we refactor common parts into a shared JavaScript file per module.
+    // still, we don't move everything into JS because we want to preserve
+    // as much HTML as possible in order to allow non-JS-enabled browsers
+    // to navigate the documentation (though slightly inefficiently).
+    let (title_prefix, title) = if it.is_struct()
+        || it.is_trait()
+        || it.is_primitive()
+        || it.is_union()
+        || it.is_enum()
+        || it.is_mod()
+        || it.is_typedef()
+    {
+        (
+            match *it.kind {
+                clean::ModuleItem(..) if it.is_crate() => "Crate ",
+                clean::ModuleItem(..) => "Module ",
+                _ => "",
+            },
+            it.name.as_ref().unwrap().as_str(),
+        )
+    } else {
+        ("", "")
+    };
+    let version = if it.is_crate() {
+        cx.cache().crate_version.as_ref().map(String::as_str).unwrap_or_default()
+    } else {
+        ""
+    };
+    let path: String = if !it.is_mod() {
+        cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
+    } else {
+        "".into()
+    };
+    let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
+    sidebar.render_into(buffer).unwrap();
+}
+
+fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec<Link<'a>> {
+    let mut fields = fields
+        .iter()
+        .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
+        .filter_map(|f| {
+            f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str()))
+        })
+        .collect::<Vec<Link<'a>>>();
+    fields.sort();
+    fields
+}
+
+fn sidebar_struct<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    s: &'a clean::Struct,
+) -> Vec<LinkBlock<'a>> {
+    let fields = get_struct_fields_name(&s.fields);
+    let field_name = match s.ctor_kind {
+        Some(CtorKind::Fn) => Some("Tuple Fields"),
+        None => Some("Fields"),
+        _ => None,
+    };
+    let mut items = vec![];
+    if let Some(name) = field_name {
+        items.push(LinkBlock::new(Link::new("fields", name), fields));
+    }
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+fn sidebar_trait<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    t: &'a clean::Trait,
+) -> Vec<LinkBlock<'a>> {
+    fn filter_items<'a>(
+        items: &'a [clean::Item],
+        filt: impl Fn(&clean::Item) -> bool,
+        ty: &str,
+    ) -> Vec<Link<'a>> {
+        let mut res = items
+            .iter()
+            .filter_map(|m: &clean::Item| match m.name {
+                Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())),
+                _ => None,
+            })
+            .collect::<Vec<Link<'a>>>();
+        res.sort();
+        res
+    }
+
+    let req_assoc = filter_items(&t.items, |m| m.is_ty_associated_type(), "associatedtype");
+    let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
+    let req_assoc_const =
+        filter_items(&t.items, |m| m.is_ty_associated_const(), "associatedconstant");
+    let prov_assoc_const =
+        filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
+    let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
+    let prov_method = filter_items(&t.items, |m| m.is_method(), "method");
+    let mut foreign_impls = vec![];
+    if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
+        foreign_impls.extend(
+            implementors
+                .iter()
+                .filter(|i| !i.is_on_local_type(cx))
+                .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
+                .map(|(name, id)| Link::new(id, name)),
+        );
+        foreign_impls.sort();
+    }
+
+    let mut blocks: Vec<LinkBlock<'_>> = [
+        ("required-associated-types", "Required Associated Types", req_assoc),
+        ("provided-associated-types", "Provided Associated Types", prov_assoc),
+        ("required-associated-consts", "Required Associated Constants", req_assoc_const),
+        ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const),
+        ("required-methods", "Required Methods", req_method),
+        ("provided-methods", "Provided Methods", prov_method),
+        ("foreign-impls", "Implementations on Foreign Types", foreign_impls),
+    ]
+    .into_iter()
+    .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items))
+    .collect();
+    sidebar_assoc_items(cx, it, &mut blocks);
+    blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors")));
+    if t.is_auto(cx.tcx()) {
+        blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors")));
+    }
+    blocks
+}
+
+fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+    if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+        let mut items = vec![];
+        sidebar_assoc_items(cx, it, &mut items);
+        items
+    } else {
+        let shared = Rc::clone(&cx.shared);
+        let (concrete, synthetic, blanket_impl) =
+            super::get_filtered_impls_for_reference(&shared, it);
+
+        sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl).into()
+    }
+}
+
+fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+    let mut items = vec![];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+fn sidebar_union<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    u: &'a clean::Union,
+) -> Vec<LinkBlock<'a>> {
+    let fields = get_struct_fields_name(&u.fields);
+    let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+/// Adds trait implementations into the blocks of links
+fn sidebar_assoc_items<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    links: &mut Vec<LinkBlock<'a>>,
+) {
+    let did = it.item_id.expect_def_id();
+    let cache = cx.cache();
+
+    let mut assoc_consts = Vec::new();
+    let mut methods = Vec::new();
+    if let Some(v) = cache.impls.get(&did) {
+        let mut used_links = FxHashSet::default();
+        let mut id_map = IdMap::new();
+
+        {
+            let used_links_bor = &mut used_links;
+            assoc_consts.extend(
+                v.iter()
+                    .filter(|i| i.inner_impl().trait_.is_none())
+                    .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)),
+            );
+            // We want links' order to be reproducible so we don't use unstable sort.
+            assoc_consts.sort();
+
+            #[rustfmt::skip] // rustfmt makes the pipeline less readable
+            methods.extend(
+                v.iter()
+                    .filter(|i| i.inner_impl().trait_.is_none())
+                    .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())),
+            );
+
+            // We want links' order to be reproducible so we don't use unstable sort.
+            methods.sort();
+        }
+
+        let mut deref_methods = Vec::new();
+        let [concrete, synthetic, blanket] = if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
+            if let Some(impl_) =
+                v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
+            {
+                let mut derefs = DefIdSet::default();
+                derefs.insert(did);
+                sidebar_deref_methods(
+                    cx,
+                    &mut deref_methods,
+                    impl_,
+                    v,
+                    &mut derefs,
+                    &mut used_links,
+                );
+            }
+
+            let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+                v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
+            let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
+                concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
+
+            sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl)
+        } else {
+            std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![]))
+        };
+
+        let mut blocks = vec![
+            LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts),
+            LinkBlock::new(Link::new("implementations", "Methods"), methods),
+        ];
+        blocks.append(&mut deref_methods);
+        blocks.extend([concrete, synthetic, blanket]);
+        links.append(&mut blocks);
+    }
+}
+
+fn sidebar_deref_methods<'a>(
+    cx: &'a Context<'_>,
+    out: &mut Vec<LinkBlock<'a>>,
+    impl_: &Impl,
+    v: &[Impl],
+    derefs: &mut DefIdSet,
+    used_links: &mut FxHashSet<String>,
+) {
+    let c = cx.cache();
+
+    debug!("found Deref: {:?}", impl_);
+    if let Some((target, real_target)) =
+        impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
+            clean::AssocTypeItem(box ref t, _) => Some(match *t {
+                clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+                _ => (&t.type_, &t.type_),
+            }),
+            _ => None,
+        })
+    {
+        debug!("found target, real_target: {:?} {:?}", target, real_target);
+        if let Some(did) = target.def_id(c) &&
+            let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
+            // `impl Deref<Target = S> for S`
+            (did == type_did || !derefs.insert(did))
+        {
+            // Avoid infinite cycles
+            return;
+        }
+        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+        let inner_impl = target
+            .def_id(c)
+            .or_else(|| {
+                target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
+            })
+            .and_then(|did| c.impls.get(&did));
+        if let Some(impls) = inner_impl {
+            debug!("found inner_impl: {:?}", impls);
+            let mut ret = impls
+                .iter()
+                .filter(|i| i.inner_impl().trait_.is_none())
+                .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
+                .collect::<Vec<_>>();
+            if !ret.is_empty() {
+                let id = if let Some(target_def_id) = real_target.def_id(c) {
+                    Cow::Borrowed(
+                        cx.deref_id_map
+                            .get(&target_def_id)
+                            .expect("Deref section without derived id")
+                            .as_str(),
+                    )
+                } else {
+                    Cow::Borrowed("deref-methods")
+                };
+                let title = format!(
+                    "Methods from {:#}<Target={:#}>",
+                    impl_.inner_impl().trait_.as_ref().unwrap().print(cx),
+                    real_target.print(cx),
+                );
+                // We want links' order to be reproducible so we don't use unstable sort.
+                ret.sort();
+                out.push(LinkBlock::new(Link::new(id, title), ret));
+            }
+        }
+
+        // Recurse into any further impls that might exist for `target`
+        if let Some(target_did) = target.def_id(c) &&
+            let Some(target_impls) = c.impls.get(&target_did) &&
+            let Some(target_deref_impl) = target_impls.iter().find(|i| {
+                i.inner_impl()
+                    .trait_
+                    .as_ref()
+                    .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
+                    .unwrap_or(false)
+            })
+        {
+            sidebar_deref_methods(
+                cx,
+                out,
+                target_deref_impl,
+                target_impls,
+                derefs,
+                used_links,
+            );
+        }
+    }
+}
+
+fn sidebar_enum<'a>(
+    cx: &'a Context<'_>,
+    it: &'a clean::Item,
+    e: &'a clean::Enum,
+) -> Vec<LinkBlock<'a>> {
+    let mut variants = e
+        .variants()
+        .filter_map(|v| v.name)
+        .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
+        .collect::<Vec<_>>();
+    variants.sort_unstable();
+
+    let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+pub(crate) fn sidebar_module_like(
+    item_sections_in_use: FxHashSet<ItemSection>,
+) -> LinkBlock<'static> {
+    let item_sections = ItemSection::ALL
+        .iter()
+        .copied()
+        .filter(|sec| item_sections_in_use.contains(sec))
+        .map(|sec| Link::new(sec.id(), sec.name()))
+        .collect();
+    LinkBlock::new(Link::empty(), item_sections)
+}
+
+fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
+    let item_sections_in_use: FxHashSet<_> = items
+        .iter()
+        .filter(|it| {
+            !it.is_stripped()
+                && it
+                    .name
+                    .or_else(|| {
+                        if let clean::ImportItem(ref i) = *it.kind &&
+                            let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
+                    })
+                    .is_some()
+        })
+        .map(|it| item_ty_to_section(it.type_()))
+        .collect();
+
+    sidebar_module_like(item_sections_in_use)
+}
+
+fn sidebar_foreign_type<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec<LinkBlock<'a>> {
+    let mut items = vec![];
+    sidebar_assoc_items(cx, it, &mut items);
+    items
+}
+
+/// Renders the trait implementations for this type
+fn sidebar_render_assoc_items(
+    cx: &Context<'_>,
+    id_map: &mut IdMap,
+    concrete: Vec<&Impl>,
+    synthetic: Vec<&Impl>,
+    blanket_impl: Vec<&Impl>,
+) -> [LinkBlock<'static>; 3] {
+    let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
+        let mut links = FxHashSet::default();
+
+        let mut ret = impls
+            .iter()
+            .filter_map(|it| {
+                let trait_ = it.inner_impl().trait_.as_ref()?;
+                let encoded =
+                    id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+
+                let prefix = match it.inner_impl().polarity {
+                    ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
+                    ty::ImplPolarity::Negative => "!",
+                };
+                let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx)));
+                if links.insert(generated.clone()) { Some(generated) } else { None }
+            })
+            .collect::<Vec<Link<'static>>>();
+        ret.sort();
+        ret
+    };
+
+    let concrete = format_impls(concrete, id_map);
+    let synthetic = format_impls(synthetic, id_map);
+    let blanket = format_impls(blanket_impl, id_map);
+    [
+        LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete),
+        LinkBlock::new(
+            Link::new("synthetic-implementations", "Auto Trait Implementations"),
+            synthetic,
+        ),
+        LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket),
+    ]
+}
+
+fn get_next_url(used_links: &mut FxHashSet<String>, url: String) -> String {
+    if used_links.insert(url.clone()) {
+        return url;
+    }
+    let mut add = 1;
+    while !used_links.insert(format!("{}-{}", url, add)) {
+        add += 1;
+    }
+    format!("{}-{}", url, add)
+}
+
+fn get_methods<'a>(
+    i: &'a clean::Impl,
+    for_deref: bool,
+    used_links: &mut FxHashSet<String>,
+    deref_mut: bool,
+    tcx: TyCtxt<'_>,
+) -> Vec<Link<'a>> {
+    i.items
+        .iter()
+        .filter_map(|item| match item.name {
+            Some(ref name) if !name.is_empty() && item.is_method() => {
+                if !for_deref || super::should_render_item(item, deref_mut, tcx) {
+                    Some(Link::new(
+                        get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
+                        name.as_str(),
+                    ))
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        })
+        .collect::<Vec<_>>()
+}
+
+fn get_associated_constants<'a>(
+    i: &'a clean::Impl,
+    used_links: &mut FxHashSet<String>,
+) -> Vec<Link<'a>> {
+    i.items
+        .iter()
+        .filter_map(|item| match item.name {
+            Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new(
+                get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
+                name.as_str(),
+            )),
+            _ => None,
+        })
+        .collect::<Vec<_>>()
+}
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index b98bced41261c..3ba2ca75458d2 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -881,6 +881,13 @@ function initSearch(rawSearchIndex) {
                     return a - b;
                 }
 
+                // sort deprecated items later
+                a = aaa.item.deprecated;
+                b = bbb.item.deprecated;
+                if (a !== b) {
+                    return a - b;
+                }
+
                 // sort by crate (current crate comes first)
                 a = (aaa.item.crate !== preferredCrate);
                 b = (bbb.item.crate !== preferredCrate);
@@ -1244,6 +1251,7 @@ function initSearch(rawSearchIndex) {
                 parent: item.parent,
                 type: item.type,
                 is_alias: true,
+                deprecated: item.deprecated,
             };
         }
 
@@ -2064,10 +2072,11 @@ function initSearch(rawSearchIndex) {
              *   n: Array<string>,
              *   t: String,
              *   d: Array<string>,
-             *   q: Array<string>,
+             *   q: Array<[Number, string]>,
              *   i: Array<Number>,
              *   f: Array<RawFunctionSearchType>,
              *   p: Array<Object>,
+             *   c: Array<Number>
              * }}
              */
             const crateCorpus = rawSearchIndex[crate];
@@ -2086,6 +2095,7 @@ function initSearch(rawSearchIndex) {
                 type: null,
                 id: id,
                 normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
+                deprecated: null,
             };
             id += 1;
             searchIndex.push(crateRow);
@@ -2095,14 +2105,20 @@ function initSearch(rawSearchIndex) {
             const itemTypes = crateCorpus.t;
             // an array of (String) item names
             const itemNames = crateCorpus.n;
-            // an array of (String) full paths (or empty string for previous path)
-            const itemPaths = crateCorpus.q;
+            // an array of [(Number) item index,
+            //              (String) full path]
+            // an item whose index is not present will fall back to the previous present path
+            // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present,
+            // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11
+            const itemPaths = new Map(crateCorpus.q);
             // an array of (String) descriptions
             const itemDescs = crateCorpus.d;
             // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
             const itemParentIdxs = crateCorpus.i;
             // an array of (Object | null) the type of the function, if any
             const itemFunctionSearchTypes = crateCorpus.f;
+            // an array of (Number) indices for the deprecated items
+            const deprecatedItems = new Set(crateCorpus.c);
             // an array of [(Number) item type,
             //              (String) name]
             const paths = crateCorpus.p;
@@ -2142,12 +2158,13 @@ function initSearch(rawSearchIndex) {
                     crate: crate,
                     ty: itemTypes.charCodeAt(i) - charA,
                     name: itemNames[i],
-                    path: itemPaths[i] ? itemPaths[i] : lastPath,
+                    path: itemPaths.has(i) ? itemPaths.get(i) : lastPath,
                     desc: itemDescs[i],
                     parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
                     type: buildFunctionSearchType(itemFunctionSearchTypes[i], lowercasePaths),
                     id: id,
                     normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
+                    deprecated: deprecatedItems.has(i),
                 };
                 id += 1;
                 searchIndex.push(row);
diff --git a/src/librustdoc/html/templates/item_info.html b/src/librustdoc/html/templates/item_info.html
new file mode 100644
index 0000000000000..d2ea9bdae9c65
--- /dev/null
+++ b/src/librustdoc/html/templates/item_info.html
@@ -0,0 +1,7 @@
+{% if !items.is_empty() %}
+    <span class="item-info"> {# #}
+        {% for item in items %}
+            {{item|safe}} {# #}
+        {% endfor %}
+    </span>
+{% endif %}
diff --git a/src/librustdoc/html/templates/short_item_info.html b/src/librustdoc/html/templates/short_item_info.html
new file mode 100644
index 0000000000000..e3125af0e47ea
--- /dev/null
+++ b/src/librustdoc/html/templates/short_item_info.html
@@ -0,0 +1,23 @@
+{% match self %}
+    {% when Self::Deprecation with { message } %}
+        <div class="stab deprecated"> {# #}
+            <span class="emoji">👎</span> {# #}
+            <span>{{message}}</span> {# #}
+        </div> {# #}
+    {% when Self::Unstable with { feature, tracking } %}
+        <div class="stab unstable"> {# #}
+            <span class="emoji">🔬</span> {# #}
+            <span> {# #}
+                This is a nightly-only experimental API. ({# #}
+                <code>{{feature}}</code> {# #}
+                {% match tracking %}
+                    {% when Some with ((url, num)) %}
+                        &nbsp;<a href="{{url}}{{num}}">#{{num}}</a> {# #}
+                    {% when None %}
+                {% endmatch %}
+                ) {# #}
+            </span> {# #}
+        </div> {# #}
+    {% when Self::Portability with { message } %}
+        <div class="stab portability">{{message|safe}}</div> {# #}
+{% endmatch %}
diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html
new file mode 100644
index 0000000000000..01d476ad29f2f
--- /dev/null
+++ b/src/librustdoc/html/templates/sidebar.html
@@ -0,0 +1,37 @@
+{% if !title.is_empty() %}
+    <h2 class="location"> {# #}
+        <a href="#">{{title_prefix}}{{title}}</a> {# #}
+    </h2>
+{% endif %}
+<div class="sidebar-elems">
+    {% if is_crate %}
+        <ul class="block">
+            {% if !version.is_empty() %}
+                <li class="version">Version {{+ version}}</li>
+            {% endif %}
+            <li><a id="all-types" href="all.html">All Items</a></li> {# #}
+        </ul>
+    {% endif %}
+
+    {% if self.should_render_blocks() %}
+        <section>
+            {% for block in blocks %}
+                {% if block.should_render() %}
+                    {% if !block.heading.name.is_empty() %}
+                        <h3><a href="#{{block.heading.href|safe}}">{{block.heading.name}}</a></h3>
+                    {% endif %}
+                    {% if !block.links.is_empty() %}
+                        <ul class="block">
+                            {% for link in block.links %}
+                                <li><a href="#{{link.href|safe}}">{{link.name}}</a></li>
+                            {% endfor %}
+                        </ul>
+                    {% endif %}
+                {% endif %}
+            {% endfor %}
+        </section>
+    {% endif %}
+    {% if !path.is_empty() %}
+        <h2><a href="index.html">In {{+ path}}</a></h2>
+    {% endif %}
+</div>
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index df26b36b7b32a..4c4c003ca4691 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -369,10 +369,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
             Node::Item(item) => {
                 if let ItemKind::Fn(_, _, body_id) = &item.kind
                 && let output_ty = return_ty(cx, item.owner_id)
-                && Inherited::build(cx.tcx, item.owner_id.def_id).enter(|inherited| {
-                    let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.owner_id.def_id);
-                    fn_ctxt.can_coerce(ty, output_ty)
-                }) {
+                && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id)
+                && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id)
+                && fn_ctxt.can_coerce(ty, output_ty)
+                {
                     if has_lifetime(output_ty) && has_lifetime(ty) {
                         return false;
                     }
diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
index cddaf9450eabc..62efd13b8d909 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
@@ -33,38 +33,37 @@ pub(super) fn check_cast<'tcx>(
     let hir_id = e.hir_id;
     let local_def_id = hir_id.owner.def_id;
 
-    Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
-        let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id);
+    let inherited = Inherited::new(cx.tcx, local_def_id);
+    let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, local_def_id);
 
-        // If we already have errors, we can't be sure we can pointer cast.
+    // If we already have errors, we can't be sure we can pointer cast.
+    assert!(
+        !fn_ctxt.errors_reported_since_creation(),
+        "Newly created FnCtxt contained errors"
+    );
+
+    if let Ok(check) = cast::CastCheck::new(
+        &fn_ctxt,
+        e,
+        from_ty,
+        to_ty,
+        // We won't show any error to the user, so we don't care what the span is here.
+        DUMMY_SP,
+        DUMMY_SP,
+        hir::Constness::NotConst,
+    ) {
+        let res = check.do_check(&fn_ctxt);
+
+        // do_check's documentation says that it might return Ok and create
+        // errors in the fcx instead of returning Err in some cases. Those cases
+        // should be filtered out before getting here.
         assert!(
             !fn_ctxt.errors_reported_since_creation(),
-            "Newly created FnCtxt contained errors"
+            "`fn_ctxt` contained errors after cast check!"
         );
 
-        if let Ok(check) = cast::CastCheck::new(
-            &fn_ctxt,
-            e,
-            from_ty,
-            to_ty,
-            // We won't show any error to the user, so we don't care what the span is here.
-            DUMMY_SP,
-            DUMMY_SP,
-            hir::Constness::NotConst,
-        ) {
-            let res = check.do_check(&fn_ctxt);
-
-            // do_check's documentation says that it might return Ok and create
-            // errors in the fcx instead of returning Err in some cases. Those cases
-            // should be filtered out before getting here.
-            assert!(
-                !fn_ctxt.errors_reported_since_creation(),
-                "`fn_ctxt` contained errors after cast check!"
-            );
-
-            res.ok()
-        } else {
-            None
-        }
-    })
+        res.ok()
+    } else {
+        None
+    }
 }
diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs
index 6d6d3c89a3c54..868579b4b1a8c 100644
--- a/src/tools/tidy/src/pal.rs
+++ b/src/tools/tidy/src/pal.rs
@@ -62,8 +62,6 @@ const EXCEPTION_PATHS: &[&str] = &[
     "library/std/src/panic.rs",   // fuchsia-specific panic backtrace handling
     "library/std/src/personality.rs",
     "library/std/src/personality/",
-    "library/std/src/thread/mod.rs",
-    "library/std/src/thread/local.rs",
 ];
 
 pub fn check(path: &Path, bad: &mut bool) {
diff --git a/tests/rustdoc-ui/crate-reference-in-block-module.rs b/tests/rustdoc-ui/crate-reference-in-block-module.rs
new file mode 100644
index 0000000000000..aede030e072b3
--- /dev/null
+++ b/tests/rustdoc-ui/crate-reference-in-block-module.rs
@@ -0,0 +1,5 @@
+// check-pass
+fn main() {
+    /// [](crate)
+    struct X;
+}
diff --git a/tests/rustdoc-ui/crate-reference-in-block-module.stderr b/tests/rustdoc-ui/crate-reference-in-block-module.stderr
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/tests/ui/check-cfg/my-awesome-platform.json b/tests/ui/check-cfg/my-awesome-platform.json
new file mode 100644
index 0000000000000..5e9ab8f1a2d00
--- /dev/null
+++ b/tests/ui/check-cfg/my-awesome-platform.json
@@ -0,0 +1,12 @@
+{
+    "llvm-target": "x86_64-unknown-none-gnu",
+    "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
+    "arch": "x86_64",
+    "target-endian": "little",
+    "target-pointer-width": "64",
+    "target-c-int-width": "32",
+    "os": "ericos",
+    "linker-flavor": "ld.lld",
+    "linker": "rust-lld",
+    "executables": true
+}
diff --git a/tests/ui/check-cfg/values-target-json.rs b/tests/ui/check-cfg/values-target-json.rs
new file mode 100644
index 0000000000000..2ef5a44592b81
--- /dev/null
+++ b/tests/ui/check-cfg/values-target-json.rs
@@ -0,0 +1,21 @@
+// This test checks that we don't lint values defined by a custom target (target json)
+//
+// check-pass
+// needs-llvm-components: x86
+// compile-flags: --crate-type=lib --check-cfg=values() --target={{src-base}}/check-cfg/my-awesome-platform.json -Z unstable-options
+
+#![feature(lang_items, no_core, auto_traits)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[cfg(target_os = "linuz")]
+//~^ WARNING unexpected `cfg` condition value
+fn target_os_linux_misspell() {}
+
+#[cfg(target_os = "linux")]
+fn target_os_linux() {}
+
+#[cfg(target_os = "ericos")]
+fn target_os_ericos() {}
diff --git a/tests/ui/check-cfg/values-target-json.stderr b/tests/ui/check-cfg/values-target-json.stderr
new file mode 100644
index 0000000000000..b58d2970773b8
--- /dev/null
+++ b/tests/ui/check-cfg/values-target-json.stderr
@@ -0,0 +1,13 @@
+warning: unexpected `cfg` condition value
+  --> $DIR/values-target-json.rs:13:7
+   |
+LL | #[cfg(target_os = "linuz")]
+   |       ^^^^^^^^^^^^-------
+   |                   |
+   |                   help: did you mean: `"linux"`
+   |
+   = note: expected values for `target_os` are: aix, android, cuda, dragonfly, emscripten, ericos, espidf, freebsd, fuchsia, haiku, hermit, horizon, illumos, ios, l4re, linux, macos, netbsd, none, nto, openbsd, psp, redox, solaris, solid_asp3, tvos, uefi, unknown, vita, vxworks, wasi, watchos, windows, xous
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/macros/nonterminal-matching.stderr b/tests/ui/macros/nonterminal-matching.stderr
index 5bbd543909833..762ecc3207fa5 100644
--- a/tests/ui/macros/nonterminal-matching.stderr
+++ b/tests/ui/macros/nonterminal-matching.stderr
@@ -18,6 +18,7 @@ LL |     macro n(a $nt_item b) {
 ...
 LL | complex_nonterminal!(enum E {});
    | ------------------------------- in this macro invocation
+   = note: captured metavariables except for `$tt`, `$ident` and `$lifetime` cannot be compared to other tokens
    = note: this error originates in the macro `complex_nonterminal` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
diff --git a/tests/ui/threads-sendsync/issue-43733-2.rs b/tests/ui/threads-sendsync/issue-43733-2.rs
index 32baeec4359c1..8f7a9c0837510 100644
--- a/tests/ui/threads-sendsync/issue-43733-2.rs
+++ b/tests/ui/threads-sendsync/issue-43733-2.rs
@@ -21,7 +21,7 @@ impl<T> Key<T> {
 }
 
 #[cfg(target_thread_local)]
-use std::thread::__FastLocalKeyInner as Key;
+use std::thread::__LocalKeyInner as Key;
 
 static __KEY: Key<()> = Key::new();
 //~^ ERROR `UnsafeCell<Option<()>>` cannot be shared between threads
diff --git a/tests/ui/threads-sendsync/issue-43733.rs b/tests/ui/threads-sendsync/issue-43733.rs
index 935e02944b97f..0eadef3e3e8cb 100644
--- a/tests/ui/threads-sendsync/issue-43733.rs
+++ b/tests/ui/threads-sendsync/issue-43733.rs
@@ -1,8 +1,8 @@
 // ignore-wasm32
 // revisions: mir thir
 // [thir]compile-flags: -Z thir-unsafeck
-// normalize-stderr-test: "__FastLocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
-// normalize-stderr-test: "__OsLocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
+// normalize-stderr-test: "__LocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
+// normalize-stderr-test: "__LocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
 #![feature(thread_local)]
 #![feature(cfg_target_thread_local, thread_local_internals)]
 
@@ -12,10 +12,10 @@ type Foo = std::cell::RefCell<String>;
 
 #[cfg(target_thread_local)]
 #[thread_local]
-static __KEY: std::thread::__FastLocalKeyInner<Foo> = std::thread::__FastLocalKeyInner::new();
+static __KEY: std::thread::__LocalKeyInner<Foo> = std::thread::__LocalKeyInner::new();
 
 #[cfg(not(target_thread_local))]
-static __KEY: std::thread::__OsLocalKeyInner<Foo> = std::thread::__OsLocalKeyInner::new();
+static __KEY: std::thread::__LocalKeyInner<Foo> = std::thread::__LocalKeyInner::new();
 
 fn __getit(_: Option<&mut Option<RefCell<String>>>) -> std::option::Option<&'static Foo> {
     __KEY.get(Default::default)