about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs36
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs7
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs1
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/callee.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/mono_item.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/callee.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/mono_item.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs14
-rw-r--r--compiler/rustc_const_eval/src/interpret/util.rs6
-rw-r--r--compiler/rustc_data_structures/src/sharded.rs6
-rw-r--r--compiler/rustc_data_structures/src/sync.rs36
-rw-r--r--compiler/rustc_data_structures/src/sync/worker_local.rs180
-rw-r--r--compiler/rustc_data_structures/src/tagged_ptr/copy.rs9
-rw-r--r--compiler/rustc_data_structures/src/tagged_ptr/drop.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs12
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs6
-rw-r--r--compiler/rustc_infer/src/infer/freshen.rs2
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs4
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs2
-rw-r--r--compiler/rustc_infer/src/infer/resolve.rs4
-rw-r--r--compiler/rustc_infer/src/traits/project.rs2
-rw-r--r--compiler/rustc_interface/src/util.rs6
-rw-r--r--compiler/rustc_lint/src/enum_intrinsics_non_enums.rs2
-rw-r--r--compiler/rustc_middle/src/hir/place.rs1
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs27
-rw-r--r--compiler/rustc_middle/src/thir.rs1
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs2
-rw-r--r--compiler/rustc_middle/src/traits/select.rs2
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_middle/src/ty/erase_regions.rs2
-rw-r--r--compiler/rustc_middle/src/ty/error.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs2
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs20
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs2
-rw-r--r--compiler/rustc_middle/src/ty/subst.rs4
-rw-r--r--compiler/rustc_middle/src/ty/util.rs2
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs12
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs4
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs4
-rw-r--r--compiler/rustc_mir_transform/src/inline/cycle.rs2
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/default.rs2
-rw-r--r--compiler/rustc_passes/messages.ftl43
-rw-r--r--compiler/rustc_passes/src/check_attr.rs32
-rw-r--r--compiler/rustc_passes/src/errors.rs160
-rw-r--r--compiler/rustc_passes/src/lib.rs2
-rw-r--r--compiler/rustc_passes/src/liveness.rs204
-rw-r--r--compiler/rustc_passes/src/stability.rs42
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs96
-rw-r--r--compiler/rustc_symbol_mangling/src/legacy.rs2
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonicalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs25
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs4
-rw-r--r--compiler/rustc_traits/src/normalize_erasing_regions.rs2
-rw-r--r--compiler/rustc_transmute/src/lib.rs4
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs4
-rw-r--r--compiler/rustc_type_ir/src/lib.rs4
-rw-r--r--compiler/rustc_type_ir/src/structural_impls.rs35
-rw-r--r--library/std/src/alloc.rs8
-rw-r--r--library/std/src/fs.rs5
-rw-r--r--library/std/src/prelude/v1.rs8
-rw-r--r--library/std/src/thread/local.rs22
-rw-r--r--src/bootstrap/bootstrap_test.py8
-rwxr-xr-xsrc/bootstrap/configure.py14
-rw-r--r--src/doc/rustdoc/src/scraped-examples.md4
-rw-r--r--src/doc/style-guide/src/statements.md37
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs30
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs2
-rw-r--r--src/tools/compiletest/src/runtest.rs10
-rw-r--r--tests/debuginfo/thread.rs2
-rw-r--r--tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir2
-rw-r--r--tests/rustdoc-ui/doc_cfg_hide.stderr2
-rw-r--r--tests/rustdoc-ui/invalid-doc-attr.rs2
-rw-r--r--tests/rustdoc-ui/invalid-doc-attr.stderr2
-rw-r--r--tests/rustdoc/issue-106142.rs14
-rw-r--r--tests/ui/attributes/invalid-doc-attr.rs2
-rw-r--r--tests/ui/attributes/invalid-doc-attr.stderr2
-rw-r--r--tests/ui/coherence/coherence-overlap-negative-impls.rs41
-rw-r--r--tests/ui/generic-associated-types/self-outlives-lint.rs2
-rw-r--r--tests/ui/methods/method-not-found-generic-arg-elision.rs4
-rw-r--r--tests/ui/methods/method-not-found-generic-arg-elision.stderr6
-rw-r--r--tests/ui/panics/fmt-only-once.rs21
-rw-r--r--tests/ui/panics/fmt-only-once.run.stderr3
-rw-r--r--tests/ui/specialization/issue-40582.rs35
-rw-r--r--tests/ui/specialization/specialization-default-items-drop-coherence.rs30
-rw-r--r--tests/ui/thread-local/thread-local-static-ref-use-after-free.rs46
103 files changed, 978 insertions, 538 deletions
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index c4771115cac..e6c4db9e2ae 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -10,15 +10,10 @@ use crate::tokenstream::{DelimSpan, Spacing, TokenTree};
 use crate::tokenstream::{LazyAttrTokenStream, TokenStream};
 use crate::util::comments;
 use crate::util::literal::escape_string_symbol;
-use rustc_data_structures::sync::WorkerLocal;
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
-use std::cell::Cell;
 use std::iter;
-#[cfg(debug_assertions)]
-use std::ops::BitXor;
-#[cfg(debug_assertions)]
 use std::sync::atomic::{AtomicU32, Ordering};
 use thin_vec::{thin_vec, ThinVec};
 
@@ -40,39 +35,16 @@ impl MarkedAttrs {
     }
 }
 
-pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
-
-#[cfg(debug_assertions)]
-static MAX_ATTR_ID: AtomicU32 = AtomicU32::new(u32::MAX);
+pub struct AttrIdGenerator(AtomicU32);
 
 impl AttrIdGenerator {
     pub fn new() -> Self {
-        // We use `(index as u32).reverse_bits()` to initialize the
-        // starting value of AttrId in each worker thread.
-        // The `index` is the index of the worker thread.
-        // This ensures that the AttrId generated in each thread is unique.
-        AttrIdGenerator(WorkerLocal::new(|index| {
-            let index: u32 = index.try_into().unwrap();
-
-            #[cfg(debug_assertions)]
-            {
-                let max_id = ((index + 1).next_power_of_two() - 1).bitxor(u32::MAX).reverse_bits();
-                MAX_ATTR_ID.fetch_min(max_id, Ordering::Release);
-            }
-
-            Cell::new(index.reverse_bits())
-        }))
+        AttrIdGenerator(AtomicU32::new(0))
     }
 
     pub fn mk_attr_id(&self) -> AttrId {
-        let id = self.0.get();
-
-        // Ensure the assigned attr_id does not overlap the bits
-        // representing the number of threads.
-        #[cfg(debug_assertions)]
-        assert!(id <= MAX_ATTR_ID.load(Ordering::Acquire));
-
-        self.0.set(id + 1);
+        let id = self.0.fetch_add(1, Ordering::Relaxed);
+        assert!(id != u32::MAX);
         AttrId::from_u32(id)
     }
 }
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 6863100d9ba..7a0a7da9695 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -858,13 +858,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let awaitee_arm = self.arm(awaitee_pat, loop_expr);
 
         // `match ::std::future::IntoFuture::into_future(<expr>) { ... }`
-        let into_future_span = self.mark_span_with_reason(
-            DesugaringKind::Await,
-            dot_await_span,
-            self.allow_into_future.clone(),
-        );
         let into_future_expr = self.expr_call_lang_item_fn(
-            into_future_span,
+            span,
             hir::LangItem::IntoFutureIntoFuture,
             arena_vec![self; expr],
             Some(expr_hir_id),
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index a9fd8db281b..3d154a93fb2 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -83,7 +83,6 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
             impl_trait_bounds: Vec::new(),
             allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
             allow_gen_future: Some([sym::gen_future, sym::closure_track_caller][..].into()),
-            allow_into_future: Some([sym::into_future][..].into()),
             generics_def_id_map: Default::default(),
         };
         lctx.with_hir_id_owner(owner, |lctx| f(lctx));
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index d07355a4154..b5b28bf8e31 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -136,7 +136,6 @@ struct LoweringContext<'a, 'hir> {
 
     allow_try_trait: Option<Lrc<[Symbol]>>,
     allow_gen_future: Option<Lrc<[Symbol]>>,
-    allow_into_future: Option<Lrc<[Symbol]>>,
 
     /// Mapping from generics `def_id`s to TAIT generics `def_id`s.
     /// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index 91c085d3d69..0b4d4ecf2e4 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -70,7 +70,7 @@ pub(crate) fn get_function_sig<'tcx>(
     default_call_conv: CallConv,
     inst: Instance<'tcx>,
 ) -> Signature {
-    assert!(!inst.substs.needs_infer());
+    assert!(!inst.substs.has_infer());
     clif_sig_from_fn_abi(
         tcx,
         default_call_conv,
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 98ba23c6f57..c181c73e4be 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -28,7 +28,7 @@ pub(crate) fn codegen_fn<'tcx>(
     module: &mut dyn Module,
     instance: Instance<'tcx>,
 ) -> CodegenedFunction {
-    debug_assert!(!instance.substs.needs_infer());
+    debug_assert!(!instance.substs.has_infer());
 
     let symbol_name = tcx.symbol_name(instance).name.to_string();
     let _timer = tcx.prof.generic_activity_with_arg("codegen fn", &*symbol_name);
diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs
index ba1e8656208..433b2585f82 100644
--- a/compiler/rustc_codegen_gcc/src/callee.rs
+++ b/compiler/rustc_codegen_gcc/src/callee.rs
@@ -17,7 +17,7 @@ use crate::context::CodegenCx;
 pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) -> Function<'gcc> {
     let tcx = cx.tcx();
 
-    assert!(!instance.substs.needs_infer());
+    assert!(!instance.substs.has_infer());
     assert!(!instance.substs.has_escaping_bound_vars());
 
     let sym = tcx.symbol_name(instance).name;
diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs
index c1f6340866c..342b830cedb 100644
--- a/compiler/rustc_codegen_gcc/src/mono_item.rs
+++ b/compiler/rustc_codegen_gcc/src/mono_item.rs
@@ -31,7 +31,7 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
 
     #[cfg_attr(not(feature="master"), allow(unused_variables))]
     fn predefine_fn(&self, instance: Instance<'tcx>, linkage: Linkage, visibility: Visibility, symbol_name: &str) {
-        assert!(!instance.substs.needs_infer());
+        assert!(!instance.substs.has_infer());
 
         let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
         self.linkage.set(base::linkage_to_gcc(linkage));
diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs
index 6ee2a05ffd7..30a0cf1d019 100644
--- a/compiler/rustc_codegen_llvm/src/callee.rs
+++ b/compiler/rustc_codegen_llvm/src/callee.rs
@@ -27,7 +27,7 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) ->
 
     debug!("get_fn(instance={:?})", instance);
 
-    assert!(!instance.substs.needs_infer());
+    assert!(!instance.substs.has_infer());
     assert!(!instance.substs.has_escaping_bound_vars());
 
     if let Some(&llfn) = cx.instances.borrow().get(&instance) {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index 824cffa28ba..2e9f89f4196 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -515,7 +515,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                         ty::Adt(def, ..) if !def.is_box() => {
                             // Again, only create type information if full debuginfo is enabled
                             if cx.sess().opts.debuginfo == DebugInfo::Full
-                                && !impl_self_ty.needs_subst()
+                                && !impl_self_ty.has_param()
                             {
                                 Some(type_di_node(cx, impl_self_ty))
                             } else {
diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs
index d0ae36349df..59bdc60830f 100644
--- a/compiler/rustc_codegen_llvm/src/mono_item.rs
+++ b/compiler/rustc_codegen_llvm/src/mono_item.rs
@@ -48,7 +48,7 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> {
         visibility: Visibility,
         symbol_name: &str,
     ) {
-        assert!(!instance.substs.needs_infer());
+        assert!(!instance.substs.has_infer());
 
         let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
         let lldecl = self.declare_fn(symbol_name, fn_abi);
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index e4f04b83d58..f706ecea975 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -152,7 +152,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     cx: &'a Bx::CodegenCx,
     instance: Instance<'tcx>,
 ) {
-    assert!(!instance.substs.needs_infer());
+    assert!(!instance.substs.has_infer());
 
     let llfn = cx.get_fn(instance);
 
@@ -304,7 +304,17 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                     bug!("spread argument isn't a tuple?!");
                 };
 
-                let place = PlaceRef::alloca(bx, bx.layout_of(arg_ty));
+                let layout = bx.layout_of(arg_ty);
+
+                // FIXME: support unsized params in "rust-call" ABI
+                if layout.is_unsized() {
+                    span_bug!(
+                        arg_decl.source_info.span,
+                        "\"rust-call\" ABI does not support unsized params",
+                    );
+                }
+
+                let place = PlaceRef::alloca(bx, layout);
                 for i in 0..tupled_arg_tys.len() {
                     let arg = &fx.fn_abi.args[idx];
                     idx += 1;
diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs
index 7cb19744987..22bdd4d2c3f 100644
--- a/compiler/rustc_const_eval/src/interpret/util.rs
+++ b/compiler/rustc_const_eval/src/interpret/util.rs
@@ -14,7 +14,7 @@ where
     T: TypeVisitable<TyCtxt<'tcx>>,
 {
     debug!("ensure_monomorphic_enough: ty={:?}", ty);
-    if !ty.needs_subst() {
+    if !ty.has_param() {
         return Ok(());
     }
 
@@ -27,7 +27,7 @@ where
         type BreakTy = FoundParam;
 
         fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-            if !ty.needs_subst() {
+            if !ty.has_param() {
                 return ControlFlow::Continue(());
             }
 
@@ -46,7 +46,7 @@ where
                         // are used and require substitution.
                         // Just in case there are closures or generators within this subst,
                         // recurse.
-                        if unused_params.is_used(index) && subst.needs_subst() {
+                        if unused_params.is_used(index) && subst.has_param() {
                             return subst.visit_with(self);
                         }
                     }
diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs
index bd7a86f6780..7ed70ba1e0f 100644
--- a/compiler/rustc_data_structures/src/sharded.rs
+++ b/compiler/rustc_data_structures/src/sharded.rs
@@ -1,14 +1,10 @@
 use crate::fx::{FxHashMap, FxHasher};
-use crate::sync::{Lock, LockGuard};
+use crate::sync::{CacheAligned, Lock, LockGuard};
 use std::borrow::Borrow;
 use std::collections::hash_map::RawEntryMut;
 use std::hash::{Hash, Hasher};
 use std::mem;
 
-#[derive(Default)]
-#[cfg_attr(parallel_compiler, repr(align(64)))]
-struct CacheAligned<T>(T);
-
 #[cfg(parallel_compiler)]
 // 32 shards is sufficient to reduce contention on an 8-core Ryzen 7 1700,
 // but this should be tested on higher core count CPUs. How the `Sharded` type gets used
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index ef1da85198f..e73ca56efa0 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -45,6 +45,9 @@ use std::hash::{BuildHasher, Hash};
 use std::ops::{Deref, DerefMut};
 use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
 
+mod worker_local;
+pub use worker_local::{Registry, WorkerLocal};
+
 pub use std::sync::atomic::Ordering;
 pub use std::sync::atomic::Ordering::SeqCst;
 
@@ -205,33 +208,6 @@ cfg_if! {
 
         use std::cell::Cell;
 
-        #[derive(Debug)]
-        pub struct WorkerLocal<T>(OneThread<T>);
-
-        impl<T> WorkerLocal<T> {
-            /// Creates a new worker local where the `initial` closure computes the
-            /// value this worker local should take for each thread in the thread pool.
-            #[inline]
-            pub fn new<F: FnMut(usize) -> T>(mut f: F) -> WorkerLocal<T> {
-                WorkerLocal(OneThread::new(f(0)))
-            }
-
-            /// Returns the worker-local value for each thread
-            #[inline]
-            pub fn into_inner(self) -> Vec<T> {
-                vec![OneThread::into_inner(self.0)]
-            }
-        }
-
-        impl<T> Deref for WorkerLocal<T> {
-            type Target = T;
-
-            #[inline(always)]
-            fn deref(&self) -> &T {
-                &self.0
-            }
-        }
-
         pub type MTLockRef<'a, T> = &'a mut MTLock<T>;
 
         #[derive(Debug, Default)]
@@ -351,8 +327,6 @@ cfg_if! {
             };
         }
 
-        pub use rayon_core::WorkerLocal;
-
         pub use rayon::iter::ParallelIterator;
         use rayon::iter::IntoParallelIterator;
 
@@ -383,6 +357,10 @@ pub fn assert_send<T: ?Sized + Send>() {}
 pub fn assert_send_val<T: ?Sized + Send>(_t: &T) {}
 pub fn assert_send_sync_val<T: ?Sized + Sync + Send>(_t: &T) {}
 
+#[derive(Default)]
+#[cfg_attr(parallel_compiler, repr(align(64)))]
+pub struct CacheAligned<T>(pub T);
+
 pub trait HashMapExt<K, V> {
     /// Same as HashMap::insert, but it may panic if there's already an
     /// entry for `key` with a value not equal to `value`
diff --git a/compiler/rustc_data_structures/src/sync/worker_local.rs b/compiler/rustc_data_structures/src/sync/worker_local.rs
new file mode 100644
index 00000000000..bfb04ba8a73
--- /dev/null
+++ b/compiler/rustc_data_structures/src/sync/worker_local.rs
@@ -0,0 +1,180 @@
+use crate::sync::Lock;
+use std::cell::Cell;
+use std::cell::OnceCell;
+use std::ops::Deref;
+use std::ptr;
+use std::sync::Arc;
+
+#[cfg(parallel_compiler)]
+use {crate::cold_path, crate::sync::CacheAligned};
+
+/// A pointer to the `RegistryData` which uniquely identifies a registry.
+/// This identifier can be reused if the registry gets freed.
+#[derive(Clone, Copy, PartialEq)]
+struct RegistryId(*const RegistryData);
+
+impl RegistryId {
+    #[inline(always)]
+    /// Verifies that the current thread is associated with the registry and returns its unique
+    /// index within the registry. This panics if the current thread is not associated with this
+    /// registry.
+    ///
+    /// Note that there's a race possible where the identifer in `THREAD_DATA` could be reused
+    /// so this can succeed from a different registry.
+    #[cfg(parallel_compiler)]
+    fn verify(self) -> usize {
+        let (id, index) = THREAD_DATA.with(|data| (data.registry_id.get(), data.index.get()));
+
+        if id == self {
+            index
+        } else {
+            cold_path(|| panic!("Unable to verify registry association"))
+        }
+    }
+}
+
+struct RegistryData {
+    thread_limit: usize,
+    threads: Lock<usize>,
+}
+
+/// Represents a list of threads which can access worker locals.
+#[derive(Clone)]
+pub struct Registry(Arc<RegistryData>);
+
+thread_local! {
+    /// The registry associated with the thread.
+    /// This allows the `WorkerLocal` type to clone the registry in its constructor.
+    static REGISTRY: OnceCell<Registry> = OnceCell::new();
+}
+
+struct ThreadData {
+    registry_id: Cell<RegistryId>,
+    index: Cell<usize>,
+}
+
+thread_local! {
+    /// A thread local which contains the identifer of `REGISTRY` but allows for faster access.
+    /// It also holds the index of the current thread.
+    static THREAD_DATA: ThreadData = const { ThreadData {
+        registry_id: Cell::new(RegistryId(ptr::null())),
+        index: Cell::new(0),
+    }};
+}
+
+impl Registry {
+    /// Creates a registry which can hold up to `thread_limit` threads.
+    pub fn new(thread_limit: usize) -> Self {
+        Registry(Arc::new(RegistryData { thread_limit, threads: Lock::new(0) }))
+    }
+
+    /// Gets the registry associated with the current thread. Panics if there's no such registry.
+    pub fn current() -> Self {
+        REGISTRY.with(|registry| registry.get().cloned().expect("No assocated registry"))
+    }
+
+    /// Registers the current thread with the registry so worker locals can be used on it.
+    /// Panics if the thread limit is hit or if the thread already has an associated registry.
+    pub fn register(&self) {
+        let mut threads = self.0.threads.lock();
+        if *threads < self.0.thread_limit {
+            REGISTRY.with(|registry| {
+                if registry.get().is_some() {
+                    drop(threads);
+                    panic!("Thread already has a registry");
+                }
+                registry.set(self.clone()).ok();
+                THREAD_DATA.with(|data| {
+                    data.registry_id.set(self.id());
+                    data.index.set(*threads);
+                });
+                *threads += 1;
+            });
+        } else {
+            drop(threads);
+            panic!("Thread limit reached");
+        }
+    }
+
+    /// Gets the identifer of this registry.
+    fn id(&self) -> RegistryId {
+        RegistryId(&*self.0)
+    }
+}
+
+/// Holds worker local values for each possible thread in a registry. You can only access the
+/// worker local value through the `Deref` impl on the registry associated with the thread it was
+/// created on. It will panic otherwise.
+pub struct WorkerLocal<T> {
+    #[cfg(not(parallel_compiler))]
+    local: T,
+    #[cfg(parallel_compiler)]
+    locals: Box<[CacheAligned<T>]>,
+    #[cfg(parallel_compiler)]
+    registry: Registry,
+}
+
+// This is safe because the `deref` call will return a reference to a `T` unique to each thread
+// or it will panic for threads without an associated local. So there isn't a need for `T` to do
+// it's own synchronization. The `verify` method on `RegistryId` has an issue where the the id
+// can be reused, but `WorkerLocal` has a reference to `Registry` which will prevent any reuse.
+#[cfg(parallel_compiler)]
+unsafe impl<T: Send> Sync for WorkerLocal<T> {}
+
+impl<T> WorkerLocal<T> {
+    /// Creates a new worker local where the `initial` closure computes the
+    /// value this worker local should take for each thread in the registry.
+    #[inline]
+    pub fn new<F: FnMut(usize) -> T>(mut initial: F) -> WorkerLocal<T> {
+        #[cfg(parallel_compiler)]
+        {
+            let registry = Registry::current();
+            WorkerLocal {
+                locals: (0..registry.0.thread_limit).map(|i| CacheAligned(initial(i))).collect(),
+                registry,
+            }
+        }
+        #[cfg(not(parallel_compiler))]
+        {
+            WorkerLocal { local: initial(0) }
+        }
+    }
+
+    /// Returns the worker-local values for each thread
+    #[inline]
+    pub fn into_inner(self) -> impl Iterator<Item = T> {
+        #[cfg(parallel_compiler)]
+        {
+            self.locals.into_vec().into_iter().map(|local| local.0)
+        }
+        #[cfg(not(parallel_compiler))]
+        {
+            std::iter::once(self.local)
+        }
+    }
+}
+
+impl<T> WorkerLocal<Vec<T>> {
+    /// Joins the elements of all the worker locals into one Vec
+    pub fn join(self) -> Vec<T> {
+        self.into_inner().into_iter().flat_map(|v| v).collect()
+    }
+}
+
+impl<T> Deref for WorkerLocal<T> {
+    type Target = T;
+
+    #[inline(always)]
+    #[cfg(not(parallel_compiler))]
+    fn deref(&self) -> &T {
+        &self.local
+    }
+
+    #[inline(always)]
+    #[cfg(parallel_compiler)]
+    fn deref(&self) -> &T {
+        // This is safe because `verify` will only return values less than
+        // `self.registry.thread_limit` which is the size of the `self.locals` array.
+        unsafe { &self.locals.get_unchecked(self.registry.id().verify()).0 }
+    }
+}
diff --git a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs
index 691e92f196a..e893a2c7813 100644
--- a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs
+++ b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs
@@ -82,11 +82,13 @@ where
     /// drop, use [`TaggedPtr`] instead.
     ///
     /// [`TaggedPtr`]: crate::tagged_ptr::TaggedPtr
+    #[inline]
     pub fn new(pointer: P, tag: T) -> Self {
         Self { packed: Self::pack(P::into_ptr(pointer), tag), tag_ghost: PhantomData }
     }
 
     /// Retrieves the pointer.
+    #[inline]
     pub fn pointer(self) -> P
     where
         P: Copy,
@@ -123,6 +125,7 @@ where
     /// according to `self.packed` encoding scheme.
     ///
     /// [`P::into_ptr`]: Pointer::into_ptr
+    #[inline]
     fn pack(ptr: NonNull<P::Target>, tag: T) -> NonNull<P::Target> {
         // Trigger assert!
         let () = Self::ASSERTION;
@@ -145,6 +148,7 @@ where
     }
 
     /// Retrieves the original raw pointer from `self.packed`.
+    #[inline]
     pub(super) fn pointer_raw(&self) -> NonNull<P::Target> {
         self.packed.map_addr(|addr| unsafe { NonZeroUsize::new_unchecked(addr.get() << T::BITS) })
     }
@@ -184,6 +188,7 @@ where
     P: Pointer + Copy,
     T: Tag,
 {
+    #[inline]
     fn clone(&self) -> Self {
         *self
     }
@@ -196,6 +201,7 @@ where
 {
     type Target = P::Target;
 
+    #[inline]
     fn deref(&self) -> &Self::Target {
         // Safety:
         // `pointer_raw` returns the original pointer from `P::into_ptr` which,
@@ -209,6 +215,7 @@ where
     P: Pointer + DerefMut,
     T: Tag,
 {
+    #[inline]
     fn deref_mut(&mut self) -> &mut Self::Target {
         // Safety:
         // `pointer_raw` returns the original pointer from `P::into_ptr` which,
@@ -235,6 +242,7 @@ where
     P: Pointer,
     T: Tag,
 {
+    #[inline]
     fn eq(&self, other: &Self) -> bool {
         self.packed == other.packed
     }
@@ -252,6 +260,7 @@ where
     P: Pointer,
     T: Tag,
 {
+    #[inline]
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.packed.hash(state);
     }
diff --git a/compiler/rustc_data_structures/src/tagged_ptr/drop.rs b/compiler/rustc_data_structures/src/tagged_ptr/drop.rs
index d418c06b7eb..4e42b5b4afe 100644
--- a/compiler/rustc_data_structures/src/tagged_ptr/drop.rs
+++ b/compiler/rustc_data_structures/src/tagged_ptr/drop.rs
@@ -30,16 +30,19 @@ where
     T: Tag,
 {
     /// Tags `pointer` with `tag`.
+    #[inline]
     pub fn new(pointer: P, tag: T) -> Self {
         TaggedPtr { raw: CopyTaggedPtr::new(pointer, tag) }
     }
 
     /// Retrieves the tag.
+    #[inline]
     pub fn tag(&self) -> T {
         self.raw.tag()
     }
 
     /// Sets the tag to a new value.
+    #[inline]
     pub fn set_tag(&mut self, tag: T) {
         self.raw.set_tag(tag)
     }
@@ -63,6 +66,8 @@ where
     T: Tag,
 {
     type Target = P::Target;
+
+    #[inline]
     fn deref(&self) -> &Self::Target {
         self.raw.deref()
     }
@@ -73,6 +78,7 @@ where
     P: Pointer + DerefMut,
     T: Tag,
 {
+    #[inline]
     fn deref_mut(&mut self) -> &mut Self::Target {
         self.raw.deref_mut()
     }
@@ -108,6 +114,7 @@ where
     P: Pointer,
     T: Tag,
 {
+    #[inline]
     fn eq(&self, other: &Self) -> bool {
         self.raw.eq(&other.raw)
     }
@@ -125,6 +132,7 @@ where
     P: Pointer,
     T: Tag,
 {
+    #[inline]
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.raw.hash(state);
     }
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 992316edb63..709dea43d84 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -2318,7 +2318,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let infcx = match self.infcx() {
             Some(infcx) => infcx,
             None => {
-                assert!(!self_ty.needs_infer());
+                assert!(!self_ty.has_infer());
                 infcx_ = tcx.infer_ctxt().ignoring_regions().build();
                 &infcx_
             }
@@ -2489,7 +2489,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let infcx = if let Some(infcx) = self.infcx() {
             infcx
         } else {
-            assert!(!qself_ty.needs_infer());
+            assert!(!qself_ty.has_infer());
             infcx_ = tcx.infer_ctxt().build();
             &infcx_
         };
@@ -3039,7 +3039,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 // the anon const, which is empty. This is why the
                 // `AlwaysApplicable` impl needs a `T: ?Sized` bound for
                 // this to compile if we were to normalize here.
-                if forbid_generic && ty.needs_subst() {
+                if forbid_generic && ty.has_param() {
                     let mut err = tcx.sess.struct_span_err(
                         path.span,
                         "generic `Self` types are currently not permitted in anonymous constants",
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index aab005dacf3..c066c396766 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1027,7 +1027,7 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b
                 packed && {
                     let ty = tcx.type_of(variant.fields.raw.last().unwrap().did).subst_identity();
                     let ty = tcx.erase_regions(ty);
-                    if ty.needs_infer() {
+                    if ty.has_infer() {
                         tcx.sess
                             .delay_span_bug(item.span, &format!("inference variables in {:?}", ty));
                         // Just treat unresolved type expression as if it needs drop.
@@ -1292,7 +1292,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
                     // Ignore dependent defaults -- that is, where the default of one type
                     // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
                     // be sure if it will error or not as user might always specify the other.
-                    if !ty.needs_subst() {
+                    if !ty.has_param() {
                         wfcx.register_wf_obligation(
                             tcx.def_span(param.def_id),
                             Some(WellFormedLoc::Ty(param.def_id.expect_local())),
@@ -1308,7 +1308,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
                     // for `struct Foo<const N: usize, const M: usize = { 1 - 2 }>`
                     // we should eagerly error.
                     let default_ct = tcx.const_param_default(param.def_id).subst_identity();
-                    if !default_ct.needs_subst() {
+                    if !default_ct.has_param() {
                         wfcx.register_wf_obligation(
                             tcx.def_span(param.def_id),
                             None,
@@ -1342,7 +1342,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
                 if is_our_default(param) {
                     let default_ty = tcx.type_of(param.def_id).subst_identity();
                     // ... and it's not a dependent default, ...
-                    if !default_ty.needs_subst() {
+                    if !default_ty.has_param() {
                         // ... then substitute it with the default.
                         return default_ty.into();
                     }
@@ -1355,7 +1355,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
                 if is_our_default(param) {
                     let default_ct = tcx.const_param_default(param.def_id).subst_identity();
                     // ... and it's not a dependent default, ...
-                    if !default_ct.needs_subst() {
+                    if !default_ct.has_param() {
                         // ... then substitute it with the default.
                         return default_ct.into();
                     }
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 47c47de8ced..89175c0ef74 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -457,7 +457,7 @@ fn emit_newtype_suggestion_for_raw_ptr(
     ptr_ty: &ty::TypeAndMut<'_>,
     diag: &mut Diagnostic,
 ) {
-    if !self_ty.needs_subst() {
+    if !self_ty.has_param() {
         let mut_key = ptr_ty.mutbl.prefix_str();
         let msg_sugg = "consider introducing a new wrapper type".to_owned();
         let sugg = vec![
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index 56f456e5557..c09457e1d65 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -366,7 +366,7 @@ fn check_predicates<'tcx>(
             wf::obligations(infcx, tcx.param_env(impl1_def_id), impl1_def_id, 0, arg, span)
                 .unwrap();
 
-        assert!(!obligations.needs_infer());
+        assert!(!obligations.has_infer());
         impl2_predicates
             .extend(traits::elaborate(tcx, obligations).map(|obligation| obligation.predicate))
     }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 3ba679df3ed..73a7bbebb65 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -168,7 +168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
                     if ocx.select_all_or_error().is_empty() {
                         let normalized_fn_sig = self.resolve_vars_if_possible(normalized_fn_sig);
-                        if !normalized_fn_sig.needs_infer() {
+                        if !normalized_fn_sig.has_infer() {
                             return normalized_fn_sig;
                         }
                     }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index eef2b5009c8..82fc1256bba 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1384,7 +1384,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
         let item_ty = self.tcx.type_of(item.def_id).subst_identity();
         // FIXME(compiler-errors): This check is *so* rudimentary
-        if item_ty.needs_subst() {
+        if item_ty.has_param() {
             return false;
         }
         if self.can_coerce(item_ty, expected_ty) {
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
index fa3887362d9..5136d895f22 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs
@@ -202,7 +202,7 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> {
         // If the type being assigned needs dropped, then the mutation counts as a borrow
         // since it is essentially doing `Drop::drop(&mut x); x = new_value;`.
         let ty = self.tcx.erase_regions(assignee_place.place.base_ty);
-        if ty.needs_infer() {
+        if ty.has_infer() {
             self.tcx.sess.delay_span_bug(
                 self.tcx.hir().span(assignee_place.hir_id),
                 &format!("inference variables in {ty}"),
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
index 5b432475fc3..915280a5bea 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
@@ -460,7 +460,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
                 // Avoid ICEs in needs_drop.
                 let ty = self.fcx.resolve_vars_if_possible(ty);
                 let ty = self.fcx.tcx.erase_regions(ty);
-                if ty.needs_infer() {
+                if ty.has_infer() {
                     self.fcx
                         .tcx
                         .sess
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 4fd778910ba..1a429142e01 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -2032,7 +2032,7 @@ impl<'tcx> Candidate<'tcx> {
                     // means they are safe to put into the
                     // `WhereClausePick`.
                     assert!(
-                        !trait_ref.skip_binder().substs.needs_infer()
+                        !trait_ref.skip_binder().substs.has_infer()
                             && !trait_ref.skip_binder().substs.has_placeholders()
                     );
 
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 9432a5840b2..817918257be 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -133,7 +133,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
 
     fn write_ty_to_typeck_results(&mut self, hir_id: hir::HirId, ty: Ty<'tcx>) {
         debug!("write_ty_to_typeck_results({:?}, {:?})", hir_id, ty);
-        assert!(!ty.needs_infer() && !ty.has_placeholders() && !ty.has_free_regions());
+        assert!(!ty.has_infer() && !ty.has_placeholders() && !ty.has_free_regions());
         self.typeck_results.node_types_mut().insert(hir_id, ty);
     }
 
@@ -508,7 +508,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             fcx_typeck_results.user_provided_types().items().map(|(local_id, c_ty)| {
                 let hir_id = hir::HirId { owner: common_hir_owner, local_id };
 
-                if cfg!(debug_assertions) && c_ty.needs_infer() {
+                if cfg!(debug_assertions) && c_ty.has_infer() {
                     span_bug!(
                         hir_id.to_span(self.fcx.tcx),
                         "writeback: `{:?}` has inference variables",
@@ -527,7 +527,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
 
         self.typeck_results.user_provided_sigs.extend(
             fcx_typeck_results.user_provided_sigs.items().map(|(&def_id, c_sig)| {
-                if cfg!(debug_assertions) && c_sig.needs_infer() {
+                if cfg!(debug_assertions) && c_sig.has_infer() {
                     span_bug!(
                         self.fcx.tcx.def_span(def_id),
                         "writeback: `{:?}` has inference variables",
@@ -618,7 +618,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         if let Some(substs) = self.fcx.typeck_results.borrow().node_substs_opt(hir_id) {
             let substs = self.resolve(substs, &span);
             debug!("write_substs_to_tcx({:?}, {:?})", hir_id, substs);
-            assert!(!substs.needs_infer() && !substs.has_placeholders());
+            assert!(!substs.has_infer() && !substs.has_placeholders());
             self.typeck_results.node_substs_mut().insert(hir_id, substs);
         }
     }
@@ -693,7 +693,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         {
             let hir_id = hir::HirId { owner: common_hir_owner, local_id };
 
-            if cfg!(debug_assertions) && container.needs_infer() {
+            if cfg!(debug_assertions) && container.has_infer() {
                 span_bug!(
                     hir_id.to_span(self.fcx.tcx),
                     "writeback: `{:?}` has inference variables",
@@ -711,7 +711,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     {
         let mut resolver = Resolver::new(self.fcx, span, self.body);
         let x = x.fold_with(&mut resolver);
-        if cfg!(debug_assertions) && x.needs_infer() {
+        if cfg!(debug_assertions) && x.has_infer() {
             span_bug!(span.to_span(self.fcx.tcx), "writeback: `{:?}` has inference variables", x);
         }
 
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index e6e1bc0393f..c29f652034f 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -564,12 +564,12 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
         let _inside_canonical_ctxt_guard = infcx.set_canonicalization_ctxt();
 
         let needs_canonical_flags = if canonicalize_region_mode.any() {
-            TypeFlags::NEEDS_INFER |
+            TypeFlags::HAS_INFER |
             TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS`
             TypeFlags::HAS_TY_PLACEHOLDER |
             TypeFlags::HAS_CT_PLACEHOLDER
         } else {
-            TypeFlags::NEEDS_INFER
+            TypeFlags::HAS_INFER
                 | TypeFlags::HAS_RE_PLACEHOLDER
                 | TypeFlags::HAS_TY_PLACEHOLDER
                 | TypeFlags::HAS_CT_PLACEHOLDER
@@ -600,7 +600,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
         // Once we have canonicalized `out_value`, it should not
         // contain anything that ties it to this inference context
         // anymore.
-        debug_assert!(!out_value.needs_infer() && !out_value.has_placeholders());
+        debug_assert!(!out_value.has_infer() && !out_value.has_placeholders());
 
         let canonical_variables =
             tcx.mk_canonical_var_infos(&canonicalizer.universe_canonicalized_variables());
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
index d89f63e5c53..0219167f6e5 100644
--- a/compiler/rustc_infer/src/infer/freshen.rs
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -127,7 +127,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
 
     #[inline]
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        if !t.needs_infer() && !t.has_erasable_regions() {
+        if !t.has_infer() && !t.has_erasable_regions() {
             t
         } else {
             match *t.kind() {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 1cd09cde0fc..1cfdb791cd6 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1327,7 +1327,7 @@ impl<'tcx> InferCtxt<'tcx> {
     where
         T: TypeFoldable<TyCtxt<'tcx>>,
     {
-        if !value.needs_infer() {
+        if !value.has_infer() {
             return value; // Avoid duplicated subst-folding.
         }
         let mut r = InferenceLiteralEraser { tcx: self.tcx };
@@ -1365,7 +1365,7 @@ impl<'tcx> InferCtxt<'tcx> {
     pub fn fully_resolve<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> FixupResult<'tcx, T> {
         let value = resolve::fully_resolve(self, value);
         assert!(
-            value.as_ref().map_or(true, |value| !value.needs_infer()),
+            value.as_ref().map_or(true, |value| !value.has_infer()),
             "`{value:?}` is not fully resolved"
         );
         value
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 2f5e2e417a6..d3f7eeff997 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -347,7 +347,7 @@ where
         let is_opaque = alias_ty.kind(self.tcx) == ty::Opaque;
         if approx_env_bounds.is_empty()
             && trait_bounds.is_empty()
-            && (alias_ty.needs_infer() || is_opaque)
+            && (alias_ty.has_infer() || is_opaque)
         {
             debug!("no declared bounds");
             let opt_variances = is_opaque.then(|| self.tcx.variances_of(alias_ty.def_id));
diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs
index 4f49f416507..3c41e8b3783 100644
--- a/compiler/rustc_infer/src/infer/resolve.rs
+++ b/compiler/rustc_infer/src/infer/resolve.rs
@@ -213,7 +213,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for FullTypeResolver<'a, 'tcx> {
     }
 
     fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
-        if !t.needs_infer() {
+        if !t.has_infer() {
             Ok(t) // micro-optimize -- if there is nothing in this type that this fold affects...
         } else {
             let t = self.infcx.shallow_resolve(t);
@@ -243,7 +243,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for FullTypeResolver<'a, 'tcx> {
     }
 
     fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
-        if !c.needs_infer() {
+        if !c.has_infer() {
             Ok(c) // micro-optimize -- if there is nothing in this const that this fold affects...
         } else {
             let c = self.infcx.shallow_resolve(c);
diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs
index 8d0af738dd1..e375d611936 100644
--- a/compiler/rustc_infer/src/traits/project.rs
+++ b/compiler/rustc_infer/src/traits/project.rs
@@ -20,7 +20,7 @@ pub struct MismatchedProjectionTypes<'tcx> {
     pub err: ty::error::TypeError<'tcx>,
 }
 
-#[derive(Clone, TypeFoldable, TypeVisitable)]
+#[derive(Clone)]
 pub struct Normalized<'tcx, T> {
     pub value: T,
     pub obligations: Vec<PredicateObligation<'tcx>>,
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 612903810d2..a27a1e2978a 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -4,6 +4,8 @@ use libloading::Library;
 use rustc_ast as ast;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+#[cfg(parallel_compiler)]
+use rustc_data_structures::sync;
 use rustc_errors::registry::Registry;
 use rustc_parse::validate_attr;
 use rustc_session as session;
@@ -170,6 +172,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
     use rustc_middle::ty::tls;
     use rustc_query_impl::{deadlock, QueryContext, QueryCtxt};
 
+    let registry = sync::Registry::new(threads);
     let mut builder = rayon::ThreadPoolBuilder::new()
         .thread_name(|_| "rustc".to_string())
         .acquire_thread_handler(jobserver::acquire_thread)
@@ -200,6 +203,9 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
                 .build_scoped(
                     // Initialize each new worker thread when created.
                     move |thread: rayon::ThreadBuilder| {
+                        // Register the thread for use with the `WorkerLocal` type.
+                        registry.register();
+
                         rustc_span::set_session_globals_then(session_globals, || thread.run())
                     },
                     // Run `f` on the first thread in the thread pool.
diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
index f1ba192f2bc..2ce28f3a049 100644
--- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
+++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
@@ -42,7 +42,7 @@ declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]);
 /// Returns `true` if we know for sure that the given type is not an enum. Note that for cases where
 /// the type is generic, we can't be certain if it will be an enum so we have to assume that it is.
 fn is_non_enum(t: Ty<'_>) -> bool {
-    !t.is_enum() && !t.needs_subst()
+    !t.is_enum() && !t.has_param()
 }
 
 fn enforce_mem_discriminant(
diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs
index 80b4c964ce4..8a22de931c3 100644
--- a/compiler/rustc_middle/src/hir/place.rs
+++ b/compiler/rustc_middle/src/hir/place.rs
@@ -66,7 +66,6 @@ pub struct Place<'tcx> {
 ///
 /// This is an HIR version of [`rustc_middle::mir::Place`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
-#[derive(TypeFoldable, TypeVisitable)]
 pub struct PlaceWithHirId<'tcx> {
     /// `HirId` of the expression or pattern producing this value.
     pub hir_id: HirId,
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 039194284b8..2490b17aac0 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -9,7 +9,7 @@ use crate::mir::visit::MirVisitable;
 use crate::ty::codec::{TyDecoder, TyEncoder};
 use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
 use crate::ty::print::{FmtPrinter, Printer};
-use crate::ty::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor};
+use crate::ty::visit::TypeVisitableExt;
 use crate::ty::{self, List, Ty, TyCtxt};
 use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex};
 use crate::ty::{GenericArg, InternalSubsts, SubstsRef};
@@ -36,7 +36,7 @@ use either::Either;
 
 use std::borrow::Cow;
 use std::fmt::{self, Debug, Display, Formatter, Write};
-use std::ops::{ControlFlow, Index, IndexMut};
+use std::ops::{Index, IndexMut};
 use std::{iter, mem};
 
 pub use self::query::*;
@@ -2722,6 +2722,7 @@ impl<'tcx> UserTypeProjections {
 ///   `field[0]` (aka `.0`), indicating that the type of `s` is
 ///   determined by finding the type of the `.0` field from `T`.
 #[derive(Clone, Debug, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
+#[derive(TypeFoldable, TypeVisitable)]
 pub struct UserTypeProjection {
     pub base: UserTypeAnnotationIndex,
     pub projs: Vec<ProjectionKind>,
@@ -2765,28 +2766,6 @@ impl UserTypeProjection {
     }
 }
 
-impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for UserTypeProjection {
-    fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
-        self,
-        folder: &mut F,
-    ) -> Result<Self, F::Error> {
-        Ok(UserTypeProjection {
-            base: self.base.try_fold_with(folder)?,
-            projs: self.projs.try_fold_with(folder)?,
-        })
-    }
-}
-
-impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for UserTypeProjection {
-    fn visit_with<Vs: TypeVisitor<TyCtxt<'tcx>>>(
-        &self,
-        visitor: &mut Vs,
-    ) -> ControlFlow<Vs::BreakTy> {
-        self.base.visit_with(visitor)
-        // Note: there's nothing in `self.proj` to visit.
-    }
-}
-
 rustc_index::newtype_index! {
     #[derive(HashStable)]
     #[debug_format = "promoted[{}]"]
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index da86cfd4772..813e109c41e 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -234,7 +234,6 @@ pub enum StmtKind<'tcx> {
 }
 
 #[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)]
-#[derive(TypeFoldable, TypeVisitable)]
 pub struct LocalVarId(pub hir::HirId);
 
 /// A THIR expression.
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 6a8ae525069..02433026266 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -569,7 +569,7 @@ pub struct DerivedObligationCause<'tcx> {
     pub parent_code: InternedObligationCauseCode<'tcx>,
 }
 
-#[derive(Clone, Debug, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Clone, Debug, TypeVisitable, Lift)]
 pub enum SelectionError<'tcx> {
     /// The trait is not implemented.
     Unimplemented,
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index 1cc9fd526b4..f2dda003b99 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -103,7 +103,7 @@ pub type EvaluationCache<'tcx> = Cache<
 /// required for associated types to work in default impls, as the bounds
 /// are visible both as projection bounds and as where-clauses from the
 /// parameter environment.
-#[derive(PartialEq, Eq, Debug, Clone, TypeFoldable, TypeVisitable)]
+#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)]
 pub enum SelectionCandidate<'tcx> {
     /// A builtin implementation for some specific traits, used in cases
     /// where we cannot rely an ordinary library implementations.
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index fef2be133e8..6b7b910a59b 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -120,7 +120,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
 }
 
 /// Additional constraints returned on success.
-#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
 pub struct ExternalConstraintsData<'tcx> {
     // FIXME: implement this.
     pub region_constraints: QueryRegionConstraints<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 7df4be263d5..830231646c6 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -215,7 +215,7 @@ impl<'tcx> CtxtInterners<'tcx> {
     ) -> Fingerprint {
         // It's impossible to hash inference variables (and will ICE), so we don't need to try to cache them.
         // Without incremental, we rarely stable-hash types, so let's not do it proactively.
-        if flags.flags.intersects(TypeFlags::NEEDS_INFER) || sess.opts.incremental.is_none() {
+        if flags.flags.intersects(TypeFlags::HAS_INFER) || sess.opts.incremental.is_none() {
             Fingerprint::ZERO
         } else {
             let mut hasher = StableHasher::new();
diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs
index 38377324832..ad930d1e6b6 100644
--- a/compiler/rustc_middle/src/ty/erase_regions.rs
+++ b/compiler/rustc_middle/src/ty/erase_regions.rs
@@ -40,7 +40,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RegionEraserVisitor<'tcx> {
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if ty.needs_infer() { ty.super_fold_with(self) } else { self.tcx.erase_regions_ty(ty) }
+        if ty.has_infer() { ty.super_fold_with(self) } else { self.tcx.erase_regions_ty(ty) }
     }
 
     fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T>
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index aff6c77e039..1be61e16dbe 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -28,7 +28,7 @@ impl<T> ExpectedFound<T> {
 }
 
 // Data structures used in type unification
-#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Lift, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, TypeVisitable, Lift, PartialEq, Eq)]
 #[rustc_pass_by_value]
 pub enum TypeError<'tcx> {
     Mismatch,
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 1c1432ecd5a..2aced27f7bb 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -2690,7 +2690,7 @@ impl<'tcx> ty::PolyTraitPredicate<'tcx> {
     }
 }
 
-#[derive(Debug, Copy, Clone, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Debug, Copy, Clone, Lift)]
 pub struct PrintClosureAsImpl<'tcx> {
     pub closure: ty::ClosureSubsts<'tcx>,
 }
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index b35b514d795..29a3bc8bb97 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -4,7 +4,6 @@
 //! to help with the tedium.
 
 use crate::mir::interpret;
-use crate::mir::ProjectionKind;
 use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
 use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer};
 use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
@@ -373,16 +372,6 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> {
 ///////////////////////////////////////////////////////////////////////////
 // Traversal implementations.
 
-/// AdtDefs are basically the same as a DefId.
-impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ty::AdtDef<'tcx> {
-    fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
-        self,
-        _folder: &mut F,
-    ) -> Result<Self, F::Error> {
-        Ok(self)
-    }
-}
-
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ty::AdtDef<'tcx> {
     fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
         &self,
@@ -445,15 +434,6 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx ty::List<ty::Const<'tcx>> {
     }
 }
 
-impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx ty::List<ProjectionKind> {
-    fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
-        self,
-        folder: &mut F,
-    ) -> Result<Self, F::Error> {
-        ty::util::fold_list(self, folder, |tcx, v| tcx.mk_projs(v))
-    }
-}
-
 impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for Ty<'tcx> {
     fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
         self,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 82dec7d98ad..7bda20ffe9a 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -631,7 +631,7 @@ impl<'tcx> UpvarSubsts<'tcx> {
 /// type of the constant. The reason that `R` is represented as an extra type parameter
 /// is the same reason that [`ClosureSubsts`] have `CS` and `U` as type parameters:
 /// inline const can reference lifetimes that are internal to the creating function.
-#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)]
+#[derive(Copy, Clone, Debug)]
 pub struct InlineConstSubsts<'tcx> {
     /// Generic parameters from the enclosing item,
     /// concatenated with the inferred type of the constant.
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs
index d05d3e2d3dc..43f95635ab0 100644
--- a/compiler/rustc_middle/src/ty/subst.rs
+++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -761,7 +761,7 @@ impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>>> ty::EarlyBinder<T> {
 
     /// Returns the inner value, but only if it contains no bound vars.
     pub fn no_bound_vars(self) -> Option<T> {
-        if !self.0.needs_subst() { Some(self.0) } else { None }
+        if !self.0.has_param() { Some(self.0) } else { None }
     }
 }
 
@@ -840,7 +840,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for SubstFolder<'a, 'tcx> {
     }
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        if !t.needs_subst() {
+        if !t.has_param() {
             return t;
         }
 
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 4c346b00256..c77985c6bd6 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1151,7 +1151,7 @@ impl<'tcx> Ty<'tcx> {
                 // context, or *something* like that, but for now just avoid passing inference
                 // variables to queries that can't cope with them. Instead, conservatively
                 // return "true" (may change drop order).
-                if query_ty.needs_infer() {
+                if query_ty.has_infer() {
                     return true;
                 }
 
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 1b07f52afca..5eaa58d69ed 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -70,7 +70,7 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
         }
     }
     fn has_non_region_param(&self) -> bool {
-        self.has_type_flags(TypeFlags::NEEDS_SUBST - TypeFlags::HAS_RE_PARAM)
+        self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM)
     }
     fn has_infer_regions(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_RE_INFER)
@@ -79,10 +79,10 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
         self.has_type_flags(TypeFlags::HAS_TY_INFER)
     }
     fn has_non_region_infer(&self) -> bool {
-        self.has_type_flags(TypeFlags::NEEDS_INFER - TypeFlags::HAS_RE_INFER)
+        self.has_type_flags(TypeFlags::HAS_INFER - TypeFlags::HAS_RE_INFER)
     }
-    fn needs_infer(&self) -> bool {
-        self.has_type_flags(TypeFlags::NEEDS_INFER)
+    fn has_infer(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_INFER)
     }
     fn has_placeholders(&self) -> bool {
         self.has_type_flags(
@@ -94,8 +94,8 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
     fn has_non_region_placeholders(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER)
     }
-    fn needs_subst(&self) -> bool {
-        self.has_type_flags(TypeFlags::NEEDS_SUBST)
+    fn has_param(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_PARAM)
     }
     /// "Free" regions in this context means that it has any region
     /// that is not (a) erased or (b) late-bound.
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 1ce7f09d8ce..880745b8f0e 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -391,7 +391,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     /// Returns the value, if any, of evaluating `c`.
     fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<OpTy<'tcx>> {
         // FIXME we need to revisit this for #67176
-        if c.needs_subst() {
+        if c.has_param() {
             return None;
         }
 
@@ -490,7 +490,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         }
 
         // FIXME we need to revisit this for #67176
-        if rvalue.needs_subst() {
+        if rvalue.has_param() {
             return None;
         }
         if !rvalue
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 3a105a2abae..a4049d08d7b 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -281,7 +281,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     /// Returns the value, if any, of evaluating `c`.
     fn eval_constant(&mut self, c: &Constant<'tcx>, location: Location) -> Option<OpTy<'tcx>> {
         // FIXME we need to revisit this for #67176
-        if c.needs_subst() {
+        if c.has_param() {
             return None;
         }
 
@@ -474,7 +474,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         }
 
         // FIXME we need to revisit this for #67176
-        if rvalue.needs_subst() {
+        if rvalue.has_param() {
             return None;
         }
         if !rvalue.ty(self.local_decls(), self.tcx).is_sized(self.tcx, self.param_env) {
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index 098ce0391fc..6046c3876be 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -92,7 +92,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
                     // FIXME: A not fully substituted drop shim can cause ICEs if one attempts to
                     // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
                     // needs some more analysis.
-                    if callee.needs_subst() {
+                    if callee.has_param() {
                         continue;
                     }
                 }
diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs
index bd24deb590a..03183a40660 100644
--- a/compiler/rustc_monomorphize/src/partitioning/default.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/default.rs
@@ -305,7 +305,7 @@ fn characteristic_def_id_of_mono_item<'tcx>(
 
                 // When polymorphization is enabled, methods which do not depend on their generic
                 // parameters, but the self-type of their impl block do will fail to normalize.
-                if !tcx.sess.opts.unstable_opts.polymorphize || !instance.needs_subst() {
+                if !tcx.sess.opts.unstable_opts.polymorphize || !instance.has_param() {
                     // This is a method within an impl, find out what the self-type is:
                     let impl_self_ty = tcx.subst_and_normalize_erasing_regions(
                         instance.substs,
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 055682a1509..40680150601 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -139,7 +139,6 @@ passes_doc_attr_not_crate_level =
 passes_attr_crate_level =
     this attribute can only be applied at the crate level
     .suggestion = to apply to the crate, use an inner attribute
-    .help = to apply to the crate, use an inner attribute
     .note = read <https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level> for more information
 
 passes_doc_test_unknown =
@@ -724,3 +723,45 @@ passes_skipping_const_checks = skipping const checks
 passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument
 
 passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments
+
+passes_unreachable_due_to_uninhabited = unreachable {$descr}
+    .label = unreachable {$descr}
+    .label_orig = any code following this expression is unreachable
+    .note = this expression has type `{$ty}`, which is uninhabited
+
+passes_unused_var_maybe_capture_ref = unused variable: `{$name}`
+    .help = did you mean to capture by reference instead?
+
+passes_unused_capture_maybe_capture_ref = value captured by `{$name}` is never read
+    .help = did you mean to capture by reference instead?
+
+passes_unused_var_remove_field = unused variable: `{$name}`
+passes_unused_var_remove_field_suggestion = try removing the field
+
+passes_unused_var_assigned_only = variable `{$name}` is assigned to, but never used
+    .note = consider using `_{$name}` instead
+
+passes_unnecessary_stable_feature = the feature `{$feature}` has been stable since {$since} and no longer requires an attribute to enable
+
+passes_unnecessary_partial_stable_feature = the feature `{$feature}` has been partially stabilized since {$since} and is succeeded by the feature `{$implies}`
+    .suggestion = if you are using features which are still unstable, change to using `{$implies}`
+    .suggestion_remove = if you are using features which are now stable, remove this line
+
+passes_ineffective_unstable_impl = an `#[unstable]` annotation here has no effect
+    .note = see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information
+
+passes_unused_assign = value assigned to `{$name}` is never read
+    .help = maybe it is overwritten before being read?
+
+passes_unused_assign_passed = value passed to `{$name}` is never read
+    .help = maybe it is overwritten before being read?
+
+passes_maybe_string_interpolation = you might have meant to use string interpolation in this string literal
+passes_string_interpolation_only_works = string interpolation only works in `format!` invocations
+
+passes_unused_variable_try_prefix = unused variable: `{$name}`
+    .label = unused variable
+    .suggestion = if this is intentional, prefix it with an underscore
+
+passes_unused_variable_try_ignore = unused variable: `{$name}`
+    .suggestion = try ignoring the field
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 085a28626ea..3f28ac26f86 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -29,7 +29,7 @@ use rustc_session::lint::builtin::{
 };
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{BytePos, Span, DUMMY_SP};
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
@@ -927,30 +927,18 @@ impl CheckAttrVisitor<'_> {
         hir_id: HirId,
     ) -> bool {
         if hir_id != CRATE_HIR_ID {
-            self.tcx.struct_span_lint_hir(
+            // insert a bang between `#` and `[...`
+            let bang_span = attr.span.lo() + BytePos(1);
+            let sugg = (attr.style == AttrStyle::Outer
+                && self.tcx.hir().get_parent_item(hir_id) == CRATE_OWNER_ID)
+                .then_some(errors::AttrCrateLevelOnlySugg {
+                    attr: attr.span.with_lo(bang_span).with_hi(bang_span),
+                });
+            self.tcx.emit_spanned_lint(
                 INVALID_DOC_ATTRIBUTES,
                 hir_id,
                 meta.span(),
-                fluent::passes_attr_crate_level,
-                |err| {
-                    if attr.style == AttrStyle::Outer
-                        && self.tcx.hir().get_parent_item(hir_id) == CRATE_OWNER_ID
-                    {
-                        if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) {
-                            src.insert(1, '!');
-                            err.span_suggestion_verbose(
-                                attr.span,
-                                fluent::passes_suggestion,
-                                src,
-                                Applicability::MaybeIncorrect,
-                            );
-                        } else {
-                            err.span_help(attr.span, fluent::passes_help);
-                        }
-                    }
-                    err.note(fluent::passes_note);
-                    err
-                },
+                errors::AttrCrateLevelOnly { sugg },
             );
             return false;
         }
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index e8603b3a2f1..99fc69d1bec 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -6,7 +6,8 @@ use std::{
 use crate::fluent_generated as fluent;
 use rustc_ast::Label;
 use rustc_errors::{
-    error_code, Applicability, DiagnosticSymbolList, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
+    error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticSymbolList, ErrorGuaranteed,
+    IntoDiagnostic, MultiSpan,
 };
 use rustc_hir::{self as hir, ExprKind, Target};
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
@@ -1555,3 +1556,160 @@ pub struct SkippingConstChecks {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unreachable_due_to_uninhabited)]
+pub struct UnreachableDueToUninhabited<'desc, 'tcx> {
+    pub descr: &'desc str,
+    #[label]
+    pub expr: Span,
+    #[label(passes_label_orig)]
+    #[note]
+    pub orig: Span,
+    pub ty: Ty<'tcx>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unused_var_maybe_capture_ref)]
+#[help]
+pub struct UnusedVarMaybeCaptureRef {
+    pub name: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unused_capture_maybe_capture_ref)]
+#[help]
+pub struct UnusedCaptureMaybeCaptureRef {
+    pub name: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unused_var_remove_field)]
+pub struct UnusedVarRemoveField {
+    pub name: String,
+    #[subdiagnostic]
+    pub sugg: UnusedVarRemoveFieldSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+    passes_unused_var_remove_field_suggestion,
+    applicability = "machine-applicable"
+)]
+pub struct UnusedVarRemoveFieldSugg {
+    #[suggestion_part(code = "")]
+    pub spans: Vec<Span>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unused_var_assigned_only)]
+#[note]
+pub struct UnusedVarAssignedOnly {
+    pub name: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unnecessary_stable_feature)]
+pub struct UnnecessaryStableFeature {
+    pub feature: Symbol,
+    pub since: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unnecessary_partial_stable_feature)]
+pub struct UnnecessaryPartialStableFeature {
+    #[suggestion(code = "{implies}", applicability = "maybe-incorrect")]
+    pub span: Span,
+    #[suggestion(passes_suggestion_remove, code = "", applicability = "maybe-incorrect")]
+    pub line: Span,
+    pub feature: Symbol,
+    pub since: Symbol,
+    pub implies: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_ineffective_unstable_impl)]
+#[note]
+pub struct IneffectiveUnstableImpl;
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unused_assign)]
+#[help]
+pub struct UnusedAssign {
+    pub name: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unused_assign_passed)]
+#[help]
+pub struct UnusedAssignPassed {
+    pub name: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unused_variable_try_prefix)]
+pub struct UnusedVariableTryPrefix {
+    #[label]
+    pub label: Option<Span>,
+    #[subdiagnostic]
+    pub string_interp: Vec<UnusedVariableStringInterp>,
+    #[subdiagnostic]
+    pub sugg: UnusedVariableTryPrefixSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(passes_suggestion, applicability = "machine-applicable")]
+pub struct UnusedVariableTryPrefixSugg {
+    #[suggestion_part(code = "_{name}")]
+    pub spans: Vec<Span>,
+    pub name: String,
+}
+
+pub struct UnusedVariableStringInterp {
+    pub lit: Span,
+    pub lo: Span,
+    pub hi: Span,
+}
+
+impl AddToDiagnostic for UnusedVariableStringInterp {
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) {
+        diag.span_label(self.lit, crate::fluent_generated::passes_maybe_string_interpolation);
+        diag.multipart_suggestion(
+            crate::fluent_generated::passes_string_interpolation_only_works,
+            vec![(self.lo, String::from("format!(")), (self.hi, String::from(")"))],
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_unused_variable_try_ignore)]
+pub struct UnusedVarTryIgnore {
+    #[subdiagnostic]
+    pub sugg: UnusedVarTryIgnoreSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(passes_suggestion, applicability = "machine-applicable")]
+pub struct UnusedVarTryIgnoreSugg {
+    #[suggestion_part(code = "{name}: _")]
+    pub shorthands: Vec<Span>,
+    #[suggestion_part(code = "_")]
+    pub non_shorthands: Vec<Span>,
+    pub name: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_attr_crate_level)]
+#[note]
+pub struct AttrCrateLevelOnly {
+    #[subdiagnostic]
+    pub sugg: Option<AttrCrateLevelOnlySugg>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(passes_suggestion, applicability = "maybe-incorrect", code = "!", style = "verbose")]
+pub struct AttrCrateLevelOnlySugg {
+    #[primary_span]
+    pub attr: Span,
+}
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index eca3bae9a1c..8b7338e29aa 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -12,6 +12,8 @@
 #![feature(min_specialization)]
 #![feature(try_blocks)]
 #![recursion_limit = "256"]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate rustc_middle;
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 7d8f6add632..6758024419d 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -81,13 +81,13 @@
 //! We generate various special nodes for various, well, special purposes.
 //! These are described in the `Liveness` struct.
 
+use crate::errors;
+
 use self::LiveNodeKind::*;
 use self::VarKind::*;
 
 use rustc_ast::InlineAsmOptions;
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::Applicability;
-use rustc_errors::Diagnostic;
 use rustc_hir as hir;
 use rustc_hir::def::*;
 use rustc_hir::def_id::LocalDefId;
@@ -1297,13 +1297,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         self.exit_ln
     }
 
-    fn warn_about_unreachable(
+    fn warn_about_unreachable<'desc>(
         &mut self,
         orig_span: Span,
         orig_ty: Ty<'tcx>,
         expr_span: Span,
         expr_id: HirId,
-        descr: &str,
+        descr: &'desc str,
     ) {
         if !orig_ty.is_never() {
             // Unreachable code warnings are already emitted during type checking.
@@ -1316,22 +1316,15 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             // that we do not emit the same warning twice if the uninhabited type
             // is indeed `!`.
 
-            let msg = format!("unreachable {}", descr);
-            self.ir.tcx.struct_span_lint_hir(
+            self.ir.tcx.emit_spanned_lint(
                 lint::builtin::UNREACHABLE_CODE,
                 expr_id,
                 expr_span,
-                &msg,
-                |diag| {
-                    diag.span_label(expr_span, &msg)
-                        .span_label(orig_span, "any code following this expression is unreachable")
-                        .span_note(
-                            orig_span,
-                            &format!(
-                                "this expression has type `{}`, which is uninhabited",
-                                orig_ty
-                            ),
-                        )
+                errors::UnreachableDueToUninhabited {
+                    expr: expr_span,
+                    orig: orig_span,
+                    descr,
+                    ty: orig_ty,
                 },
             );
         }
@@ -1483,23 +1476,21 @@ impl<'tcx> Liveness<'_, 'tcx> {
                 if self.used_on_entry(entry_ln, var) {
                     if !self.live_on_entry(entry_ln, var) {
                         if let Some(name) = self.should_warn(var) {
-                            self.ir.tcx.struct_span_lint_hir(
+                            self.ir.tcx.emit_spanned_lint(
                                 lint::builtin::UNUSED_ASSIGNMENTS,
                                 var_hir_id,
                                 vec![span],
-                                format!("value captured by `{}` is never read", name),
-                                |lint| lint.help("did you mean to capture by reference instead?"),
+                                errors::UnusedCaptureMaybeCaptureRef { name },
                             );
                         }
                     }
                 } else {
                     if let Some(name) = self.should_warn(var) {
-                        self.ir.tcx.struct_span_lint_hir(
+                        self.ir.tcx.emit_spanned_lint(
                             lint::builtin::UNUSED_VARIABLES,
                             var_hir_id,
                             vec![span],
-                            format!("unused variable: `{}`", name),
-                            |lint| lint.help("did you mean to capture by reference instead?"),
+                            errors::UnusedVarMaybeCaptureRef { name },
                         );
                     }
                 }
@@ -1514,11 +1505,14 @@ impl<'tcx> Liveness<'_, 'tcx> {
                 Some(entry_ln),
                 Some(body),
                 |spans, hir_id, ln, var| {
-                    if !self.live_on_entry(ln, var) {
-                        self.report_unused_assign(hir_id, spans, var, |name| {
-                            format!("value passed to `{}` is never read", name)
-                        });
-                    }
+                    if !self.live_on_entry(ln, var)
+                        && let Some(name) = self.should_warn(var) {
+                            self.ir.tcx.emit_spanned_lint(
+                                lint::builtin::UNUSED_ASSIGNMENTS,
+                                hir_id,
+                                spans,
+                                errors::UnusedAssignPassed { name },
+                            );                    }
                 },
             );
         }
@@ -1587,39 +1581,35 @@ impl<'tcx> Liveness<'_, 'tcx> {
                 if ln == self.exit_ln { false } else { self.assigned_on_exit(ln, var) };
 
             if is_assigned {
-                self.ir.tcx.struct_span_lint_hir(
+                self.ir.tcx.emit_spanned_lint(
                     lint::builtin::UNUSED_VARIABLES,
                     first_hir_id,
                     hir_ids_and_spans
                         .into_iter()
                         .map(|(_, _, ident_span)| ident_span)
                         .collect::<Vec<_>>(),
-                    format!("variable `{}` is assigned to, but never used", name),
-                    |lint| lint.note(&format!("consider using `_{}` instead", name)),
+                    errors::UnusedVarAssignedOnly { name },
                 )
             } else if can_remove {
-                self.ir.tcx.struct_span_lint_hir(
+                let spans = hir_ids_and_spans
+                    .iter()
+                    .map(|(_, pat_span, _)| {
+                        let span = self
+                            .ir
+                            .tcx
+                            .sess
+                            .source_map()
+                            .span_extend_to_next_char(*pat_span, ',', true);
+                        span.with_hi(BytePos(span.hi().0 + 1))
+                    })
+                    .collect();
+                self.ir.tcx.emit_spanned_lint(
                     lint::builtin::UNUSED_VARIABLES,
                     first_hir_id,
                     hir_ids_and_spans.iter().map(|(_, pat_span, _)| *pat_span).collect::<Vec<_>>(),
-                    format!("unused variable: `{}`", name),
-                    |lint| {
-                        lint.multipart_suggestion(
-                            "try removing the field",
-                            hir_ids_and_spans
-                                .iter()
-                                .map(|(_, pat_span, _)| {
-                                    let span = self
-                                        .ir
-                                        .tcx
-                                        .sess
-                                        .source_map()
-                                        .span_extend_to_next_char(*pat_span, ',', true);
-                                    (span.with_hi(BytePos(span.hi().0 + 1)), String::new())
-                                })
-                                .collect(),
-                            Applicability::MachineApplicable,
-                        )
+                    errors::UnusedVarRemoveField {
+                        name,
+                        sugg: errors::UnusedVarRemoveFieldSugg { spans },
                     },
                 );
             } else {
@@ -1633,55 +1623,46 @@ impl<'tcx> Liveness<'_, 'tcx> {
                 // the field" message, and suggest `_` for the non-shorthands. If we only
                 // have non-shorthand, then prefix with an underscore instead.
                 if !shorthands.is_empty() {
-                    let shorthands = shorthands
-                        .into_iter()
-                        .map(|(_, pat_span, _)| (pat_span, format!("{}: _", name)))
-                        .chain(
-                            non_shorthands
-                                .into_iter()
-                                .map(|(_, pat_span, _)| (pat_span, "_".to_string())),
-                        )
-                        .collect::<Vec<_>>();
+                    let shorthands =
+                        shorthands.into_iter().map(|(_, pat_span, _)| pat_span).collect();
+                    let non_shorthands =
+                        non_shorthands.into_iter().map(|(_, pat_span, _)| pat_span).collect();
 
-                    self.ir.tcx.struct_span_lint_hir(
+                    self.ir.tcx.emit_spanned_lint(
                         lint::builtin::UNUSED_VARIABLES,
                         first_hir_id,
                         hir_ids_and_spans
                             .iter()
                             .map(|(_, pat_span, _)| *pat_span)
                             .collect::<Vec<_>>(),
-                        format!("unused variable: `{}`", name),
-                        |lint| {
-                            lint.multipart_suggestion(
-                                "try ignoring the field",
+                        errors::UnusedVarTryIgnore {
+                            sugg: errors::UnusedVarTryIgnoreSugg {
                                 shorthands,
-                                Applicability::MachineApplicable,
-                            )
+                                non_shorthands,
+                                name,
+                            },
                         },
                     );
                 } else {
                     let non_shorthands = non_shorthands
                         .into_iter()
-                        .map(|(_, _, ident_span)| (ident_span, format!("_{}", name)))
+                        .map(|(_, _, ident_span)| ident_span)
                         .collect::<Vec<_>>();
-
-                    self.ir.tcx.struct_span_lint_hir(
+                    let suggestions = self.string_interp_suggestions(&name, opt_body);
+                    self.ir.tcx.emit_spanned_lint(
                         lint::builtin::UNUSED_VARIABLES,
                         first_hir_id,
                         hir_ids_and_spans
                             .iter()
                             .map(|(_, _, ident_span)| *ident_span)
                             .collect::<Vec<_>>(),
-                        format!("unused variable: `{}`", name),
-                        |lint| {
-                            if self.has_added_lit_match_name_span(&name, opt_body, lint) {
-                                lint.span_label(pat.span, "unused variable");
-                            }
-                            lint.multipart_suggestion(
-                                "if this is intentional, prefix it with an underscore",
-                                non_shorthands,
-                                Applicability::MachineApplicable,
-                            )
+                        errors::UnusedVariableTryPrefix {
+                            label: if !suggestions.is_empty() { Some(pat.span) } else { None },
+                            sugg: errors::UnusedVariableTryPrefixSugg {
+                                spans: non_shorthands,
+                                name,
+                            },
+                            string_interp: suggestions,
                         },
                     );
                 }
@@ -1689,65 +1670,40 @@ impl<'tcx> Liveness<'_, 'tcx> {
         }
     }
 
-    fn has_added_lit_match_name_span(
+    fn string_interp_suggestions(
         &self,
         name: &str,
         opt_body: Option<&hir::Body<'_>>,
-        err: &mut Diagnostic,
-    ) -> bool {
-        let mut has_litstring = false;
-        let Some(opt_body) = opt_body else {return false;};
+    ) -> Vec<errors::UnusedVariableStringInterp> {
+        let mut suggs = Vec::new();
+        let Some(opt_body) = opt_body else { return suggs; };
         let mut visitor = CollectLitsVisitor { lit_exprs: vec![] };
         intravisit::walk_body(&mut visitor, opt_body);
         for lit_expr in visitor.lit_exprs {
             let hir::ExprKind::Lit(litx) = &lit_expr.kind else { continue };
             let rustc_ast::LitKind::Str(syb, _) = litx.node else{ continue; };
             let name_str: &str = syb.as_str();
-            let mut name_pa = String::from("{");
-            name_pa.push_str(&name);
-            name_pa.push('}');
+            let name_pa = format!("{{{name}}}");
             if name_str.contains(&name_pa) {
-                err.span_label(
-                    lit_expr.span,
-                    "you might have meant to use string interpolation in this string literal",
-                );
-                err.multipart_suggestion(
-                    "string interpolation only works in `format!` invocations",
-                    vec![
-                        (lit_expr.span.shrink_to_lo(), "format!(".to_string()),
-                        (lit_expr.span.shrink_to_hi(), ")".to_string()),
-                    ],
-                    Applicability::MachineApplicable,
-                );
-                has_litstring = true;
+                suggs.push(errors::UnusedVariableStringInterp {
+                    lit: lit_expr.span,
+                    lo: lit_expr.span.shrink_to_lo(),
+                    hi: lit_expr.span.shrink_to_hi(),
+                });
             }
         }
-        has_litstring
+        suggs
     }
 
     fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
-        if !self.live_on_exit(ln, var) {
-            self.report_unused_assign(hir_id, spans, var, |name| {
-                format!("value assigned to `{}` is never read", name)
-            });
-        }
-    }
-
-    fn report_unused_assign(
-        &self,
-        hir_id: HirId,
-        spans: Vec<Span>,
-        var: Variable,
-        message: impl Fn(&str) -> String,
-    ) {
-        if let Some(name) = self.should_warn(var) {
-            self.ir.tcx.struct_span_lint_hir(
-                lint::builtin::UNUSED_ASSIGNMENTS,
-                hir_id,
-                spans,
-                message(&name),
-                |lint| lint.help("maybe it is overwritten before being read?"),
-            )
-        }
+        if !self.live_on_exit(ln, var)
+            && let Some(name) = self.should_warn(var) {
+                self.ir.tcx.emit_spanned_lint(
+                    lint::builtin::UNUSED_ASSIGNMENTS,
+                    hir_id,
+                    spans,
+                    errors::UnusedAssign { name },
+                );
+            }
     }
 }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 4a35c679466..9615f283ff4 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -7,7 +7,6 @@ use rustc_attr::{
     UnstableReason, VERSION_PLACEHOLDER,
 };
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
-use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
@@ -759,12 +758,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                         // do not lint when the trait isn't resolved, since resolution error should
                         // be fixed first
                         if t.path.res != Res::Err && c.fully_stable {
-                            self.tcx.struct_span_lint_hir(
+                            self.tcx.emit_spanned_lint(
                                 INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
                                 item.hir_id(),
                                 span,
-                                "an `#[unstable]` annotation here has no effect",
-                                |lint| lint.note("see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information")
+                                errors::IneffectiveUnstableImpl,
                             );
                         }
                     }
@@ -1095,29 +1093,16 @@ fn unnecessary_partially_stable_feature_lint(
     implies: Symbol,
     since: Symbol,
 ) {
-    tcx.struct_span_lint_hir(
+    tcx.emit_spanned_lint(
         lint::builtin::STABLE_FEATURES,
         hir::CRATE_HIR_ID,
         span,
-        format!(
-            "the feature `{feature}` has been partially stabilized since {since} and is succeeded \
-             by the feature `{implies}`"
-        ),
-        |lint| {
-            lint.span_suggestion(
-                span,
-                &format!(
-                "if you are using features which are still unstable, change to using `{implies}`"
-            ),
-                implies,
-                Applicability::MaybeIncorrect,
-            )
-            .span_suggestion(
-                tcx.sess.source_map().span_extend_to_line(span),
-                "if you are using features which are now stable, remove this line",
-                "",
-                Applicability::MaybeIncorrect,
-            )
+        errors::UnnecessaryPartialStableFeature {
+            span,
+            line: tcx.sess.source_map().span_extend_to_line(span),
+            feature,
+            since,
+            implies,
         },
     );
 }
@@ -1131,7 +1116,10 @@ fn unnecessary_stable_feature_lint(
     if since.as_str() == VERSION_PLACEHOLDER {
         since = rust_version_symbol();
     }
-    tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, format!("the feature `{feature}` has been stable since {since} and no longer requires an attribute to enable"), |lint| {
-        lint
-    });
+    tcx.emit_spanned_lint(
+        lint::builtin::STABLE_FEATURES,
+        hir::CRATE_HIR_ID,
+        span,
+        errors::UnnecessaryStableFeature { feature, since },
+    );
 }
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index b9922b26afc..8de4d06fe78 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -354,24 +354,20 @@ impl<K: DepKind> DepGraphData<K> {
                  - dep-node: {key:?}"
         );
 
-        let task_deps = if cx.dep_context().is_eval_always(key.kind) {
-            None
+        let with_deps = |task_deps| K::with_deps(task_deps, || task(cx, arg));
+        let (result, edges) = if cx.dep_context().is_eval_always(key.kind) {
+            (with_deps(TaskDepsRef::EvalAlways), smallvec![])
         } else {
-            Some(Lock::new(TaskDeps {
+            let task_deps = Lock::new(TaskDeps {
                 #[cfg(debug_assertions)]
                 node: Some(key),
                 reads: SmallVec::new(),
                 read_set: Default::default(),
                 phantom_data: PhantomData,
-            }))
+            });
+            (with_deps(TaskDepsRef::Allow(&task_deps)), task_deps.into_inner().reads)
         };
 
-        let task_deps_ref =
-            task_deps.as_ref().map(TaskDepsRef::Allow).unwrap_or(TaskDepsRef::EvalAlways);
-
-        let result = K::with_deps(task_deps_ref, || task(cx, arg));
-        let edges = task_deps.map_or_else(|| smallvec![], |lock| lock.into_inner().reads);
-
         let dcx = cx.dep_context();
         let hashing_timer = dcx.profiler().incr_result_hashing();
         let current_fingerprint =
@@ -1236,76 +1232,48 @@ impl<K: DepKind> CurrentDepGraph<K> {
             self.node_intern_event_id.map(|eid| profiler.generic_activity_with_event_id(eid));
 
         if let Some(prev_index) = prev_graph.node_to_index_opt(&key) {
+            let get_dep_node_index = |color, fingerprint| {
+                if print_status {
+                    eprintln!("[task::{color:}] {key:?}");
+                }
+
+                let mut prev_index_to_index = self.prev_index_to_index.lock();
+
+                let dep_node_index = match prev_index_to_index[prev_index] {
+                    Some(dep_node_index) => dep_node_index,
+                    None => {
+                        let dep_node_index =
+                            self.encoder.borrow().send(profiler, key, fingerprint, edges);
+                        prev_index_to_index[prev_index] = Some(dep_node_index);
+                        dep_node_index
+                    }
+                };
+
+                #[cfg(debug_assertions)]
+                self.record_edge(dep_node_index, key, fingerprint);
+
+                dep_node_index
+            };
+
             // Determine the color and index of the new `DepNode`.
             if let Some(fingerprint) = fingerprint {
                 if fingerprint == prev_graph.fingerprint_by_index(prev_index) {
-                    if print_status {
-                        eprintln!("[task::green] {key:?}");
-                    }
-
                     // This is a green node: it existed in the previous compilation,
                     // its query was re-executed, and it has the same result as before.
-                    let mut prev_index_to_index = self.prev_index_to_index.lock();
-
-                    let dep_node_index = match prev_index_to_index[prev_index] {
-                        Some(dep_node_index) => dep_node_index,
-                        None => {
-                            let dep_node_index =
-                                self.encoder.borrow().send(profiler, key, fingerprint, edges);
-                            prev_index_to_index[prev_index] = Some(dep_node_index);
-                            dep_node_index
-                        }
-                    };
-
-                    #[cfg(debug_assertions)]
-                    self.record_edge(dep_node_index, key, fingerprint);
+                    let dep_node_index = get_dep_node_index("green", fingerprint);
                     (dep_node_index, Some((prev_index, DepNodeColor::Green(dep_node_index))))
                 } else {
-                    if print_status {
-                        eprintln!("[task::red] {key:?}");
-                    }
-
                     // This is a red node: it existed in the previous compilation, its query
                     // was re-executed, but it has a different result from before.
-                    let mut prev_index_to_index = self.prev_index_to_index.lock();
-
-                    let dep_node_index = match prev_index_to_index[prev_index] {
-                        Some(dep_node_index) => dep_node_index,
-                        None => {
-                            let dep_node_index =
-                                self.encoder.borrow().send(profiler, key, fingerprint, edges);
-                            prev_index_to_index[prev_index] = Some(dep_node_index);
-                            dep_node_index
-                        }
-                    };
-
-                    #[cfg(debug_assertions)]
-                    self.record_edge(dep_node_index, key, fingerprint);
+                    let dep_node_index = get_dep_node_index("red", fingerprint);
                     (dep_node_index, Some((prev_index, DepNodeColor::Red)))
                 }
             } else {
-                if print_status {
-                    eprintln!("[task::unknown] {key:?}");
-                }
-
                 // This is a red node, effectively: it existed in the previous compilation
                 // session, its query was re-executed, but it doesn't compute a result hash
                 // (i.e. it represents a `no_hash` query), so we have no way of determining
                 // whether or not the result was the same as before.
-                let mut prev_index_to_index = self.prev_index_to_index.lock();
-
-                let dep_node_index = match prev_index_to_index[prev_index] {
-                    Some(dep_node_index) => dep_node_index,
-                    None => {
-                        let dep_node_index =
-                            self.encoder.borrow().send(profiler, key, Fingerprint::ZERO, edges);
-                        prev_index_to_index[prev_index] = Some(dep_node_index);
-                        dep_node_index
-                    }
-                };
-
-                #[cfg(debug_assertions)]
-                self.record_edge(dep_node_index, key, Fingerprint::ZERO);
+                let dep_node_index = get_dep_node_index("unknown", Fingerprint::ZERO);
                 (dep_node_index, Some((prev_index, DepNodeColor::Red)))
             }
         } else {
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 6a0ca06f69c..a3f262905c7 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -108,7 +108,7 @@ fn get_symbol_hash<'tcx>(
             tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher);
 
             // Include the main item-type. Note that, in this case, the
-            // assertions about `needs_subst` may not hold, but this item-type
+            // assertions about `has_param` may not hold, but this item-type
             // ought to be the same for every reference anyway.
             assert!(!item_type.has_erasable_regions());
             hcx.while_hashing_spans(false, |hcx| {
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 911cc0b88c4..fcf86da08f4 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -41,7 +41,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
     fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
         let ty = self.resolve_vars_if_possible(ty);
 
-        if !(param_env, ty).needs_infer() {
+        if !(param_env, ty).has_infer() {
             return ty.is_copy_modulo_regions(self.tcx, param_env);
         }
 
diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
index 976849696e3..ff4bff10cc8 100644
--- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
@@ -69,7 +69,7 @@ impl<'a, 'tcx> Canonicalizer<'a, 'tcx> {
         };
 
         let value = value.fold_with(&mut canonicalizer);
-        assert!(!value.needs_infer());
+        assert!(!value.has_infer());
         assert!(!value.has_placeholders());
 
         let (max_universe, variables) = canonicalizer.finalize();
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 20c2605f219..b7690f79933 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -582,7 +582,7 @@ fn orphan_check_trait_ref<'tcx>(
     trait_ref: ty::TraitRef<'tcx>,
     in_crate: InCrate,
 ) -> Result<(), OrphanCheckErr<'tcx>> {
-    if trait_ref.needs_infer() && trait_ref.needs_subst() {
+    if trait_ref.has_infer() && trait_ref.has_param() {
         bug!(
             "can't orphan check a trait ref with both params and inference variables {:?}",
             trait_ref
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 4c9df5d9d0a..0e8c74a6765 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -414,7 +414,7 @@ fn subst_and_check_impossible_predicates<'tcx>(
         predicates.push(ty::Binder::dummy(trait_ref).to_predicate(tcx));
     }
 
-    predicates.retain(|predicate| !predicate.needs_subst());
+    predicates.retain(|predicate| !predicate.has_param());
     let result = impossible_predicates(tcx, predicates);
 
     debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result);
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index e01a57ea4fe..0db80232891 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -60,7 +60,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
         // We may however encounter unconstrained lifetime variables in invalid
         // code. See #110161 for context.
         assert!(!ty.has_non_region_infer());
-        if ty.needs_infer() {
+        if ty.has_infer() {
             self.tcx.sess.delay_span_bug(
                 self.tcx.def_span(body_id),
                 "skipped implied_outlives_bounds due to unconstrained lifetimes",
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index af61ca0c29f..863553670de 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -449,7 +449,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len());
 
-        let needs_infer = stack.obligation.predicate.has_non_region_infer();
+        let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
 
         // If there are STILL multiple candidates, we can further
         // reduce the list by dropping duplicates -- including
@@ -461,7 +461,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     self.candidate_should_be_dropped_in_favor_of(
                         &candidates[i],
                         &candidates[j],
-                        needs_infer,
+                        has_non_region_infer,
                     ) == DropVictim::Yes
                 });
                 if should_drop_i {
@@ -1000,7 +1000,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     ) -> Result<EvaluationResult, OverflowError> {
         if !self.is_intercrate()
             && obligation.is_global()
-            && obligation.param_env.caller_bounds().iter().all(|bound| bound.needs_subst())
+            && obligation.param_env.caller_bounds().iter().all(|bound| bound.has_param())
         {
             // If a param env has no global bounds, global obligations do not
             // depend on its particular value in order to work, so we can clear
@@ -1330,7 +1330,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         if self.can_use_global_caches(param_env) {
-            if !trait_pred.needs_infer() {
+            if !trait_pred.has_infer() {
                 debug!(?trait_pred, ?result, "insert_evaluation_cache global");
                 // This may overwrite the cache with the same value
                 // FIXME: Due to #50507 this overwrites the different values
@@ -1516,7 +1516,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // If there are any inference variables in the `ParamEnv`, then we
         // always use a cache local to this particular scope. Otherwise, we
         // switch to a global cache.
-        if param_env.needs_infer() {
+        if param_env.has_infer() {
             return false;
         }
 
@@ -1587,7 +1587,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return false;
         }
         match result {
-            Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.needs_infer(),
+            Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.has_infer(),
             _ => true,
         }
     }
@@ -1613,8 +1613,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         if self.can_use_global_caches(param_env) {
             if let Err(Overflow(OverflowError::Canonical)) = candidate {
                 // Don't cache overflow globally; we only produce this in certain modes.
-            } else if !pred.needs_infer() {
-                if !candidate.needs_infer() {
+            } else if !pred.has_infer() {
+                if !candidate.has_infer() {
                     debug!(?pred, ?candidate, "insert_candidate_cache global");
                     // This may overwrite the cache with the same value.
                     tcx.selection_cache.insert((param_env, pred), dep_node, candidate);
@@ -1724,7 +1724,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             .map(|InferOk { obligations: _, value: () }| {
                 // This method is called within a probe, so we can't have
                 // inference variables and placeholders escape.
-                if !trait_bound.needs_infer() && !trait_bound.has_placeholders() {
+                if !trait_bound.has_infer() && !trait_bound.has_placeholders() {
                     Some(trait_bound)
                 } else {
                     None
@@ -1840,7 +1840,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         &mut self,
         victim: &EvaluatedCandidate<'tcx>,
         other: &EvaluatedCandidate<'tcx>,
-        needs_infer: bool,
+        has_non_region_infer: bool,
     ) -> DropVictim {
         if victim.candidate == other.candidate {
             return DropVictim::Yes;
@@ -1956,7 +1956,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             | (ObjectCandidate(i), ObjectCandidate(j)) => {
                 // Arbitrarily pick the lower numbered candidate for backwards
                 // compatibility reasons. Don't let this affect inference.
-                DropVictim::drop_if(i < j && !needs_infer)
+                DropVictim::drop_if(i < j && !has_non_region_infer)
             }
             (ObjectCandidate(_), ProjectionCandidate(..))
             | (ProjectionCandidate(..), ObjectCandidate(_)) => {
@@ -2062,7 +2062,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                         // existence of multiple marker trait impls tells us nothing
                         // about which one should actually apply.
                         DropVictim::drop_if(
-                            !needs_infer && other.evaluation.must_apply_considering_regions(),
+                            !has_non_region_infer
+                                && other.evaluation.must_apply_considering_regions(),
                         )
                     }
                     None => DropVictim::No,
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index a4e9928f8b2..c56e7c7cadd 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -353,8 +353,8 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
     ),
 ) -> Option<usize> {
     let (source, target) = key;
-    assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer());
-    assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer());
+    assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.has_infer());
+    assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.has_infer());
 
     // this has been typecked-before, so diagnostics is not really needed.
     let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None);
diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs
index 126a494f34f..5da0f16c2bf 100644
--- a/compiler/rustc_traits/src/normalize_erasing_regions.rs
+++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs
@@ -47,7 +47,7 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<TyCtxt<'tcx>> + Par
             // us a test case.
             debug_assert_eq!(normalized_value, resolved_value);
             let erased = infcx.tcx.erase_regions(resolved_value);
-            debug_assert!(!erased.needs_infer(), "{erased:?}");
+            debug_assert!(!erased.has_infer(), "{erased:?}");
             Ok(erased)
         }
         Err(NoSolution) => Err(NoSolution),
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index 8be02c1d988..77c0526e3aa 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -62,7 +62,7 @@ mod rustc {
 
     use rustc_hir::lang_items::LangItem;
     use rustc_infer::infer::InferCtxt;
-    use rustc_macros::{TypeFoldable, TypeVisitable};
+    use rustc_macros::TypeVisitable;
     use rustc_middle::traits::ObligationCause;
     use rustc_middle::ty::Const;
     use rustc_middle::ty::ParamEnv;
@@ -70,7 +70,7 @@ mod rustc {
     use rustc_middle::ty::TyCtxt;
 
     /// The source and destination types of a transmutation.
-    #[derive(TypeFoldable, TypeVisitable, Debug, Clone, Copy)]
+    #[derive(TypeVisitable, Debug, Clone, Copy)]
     pub struct Types<'tcx> {
         /// The source type.
         pub src: Ty<'tcx>,
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 21cc3c9517e..64586a6782b 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -104,8 +104,8 @@ fn resolve_associated_item<'tcx>(
                 "resolving ImplSource::UserDefined: {:?}, {:?}, {:?}, {:?}",
                 param_env, trait_item_id, rcvr_substs, impl_data
             );
-            assert!(!rcvr_substs.needs_infer());
-            assert!(!trait_ref.needs_infer());
+            assert!(!rcvr_substs.has_infer());
+            assert!(!trait_ref.has_infer());
 
             let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap();
             let trait_def = tcx.trait_def(trait_def_id);
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 95237dda8c2..1e91e26e2af 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -179,7 +179,7 @@ bitflags! {
         /// Does this have `ConstKind::Param`?
         const HAS_CT_PARAM                = 1 << 2;
 
-        const NEEDS_SUBST                 = TypeFlags::HAS_TY_PARAM.bits
+        const HAS_PARAM                 = TypeFlags::HAS_TY_PARAM.bits
                                           | TypeFlags::HAS_RE_PARAM.bits
                                           | TypeFlags::HAS_CT_PARAM.bits;
 
@@ -192,7 +192,7 @@ bitflags! {
 
         /// Does this have inference variables? Used to determine whether
         /// inference is required.
-        const NEEDS_INFER                 = TypeFlags::HAS_TY_INFER.bits
+        const HAS_INFER                 = TypeFlags::HAS_TY_INFER.bits
                                           | TypeFlags::HAS_RE_INFER.bits
                                           | TypeFlags::HAS_CT_INFER.bits;
 
diff --git a/compiler/rustc_type_ir/src/structural_impls.rs b/compiler/rustc_type_ir/src/structural_impls.rs
index 54c1ab5a275..c9675f93f95 100644
--- a/compiler/rustc_type_ir/src/structural_impls.rs
+++ b/compiler/rustc_type_ir/src/structural_impls.rs
@@ -6,11 +6,10 @@ use crate::fold::{FallibleTypeFolder, TypeFoldable};
 use crate::visit::{TypeVisitable, TypeVisitor};
 use crate::Interner;
 use rustc_data_structures::functor::IdFunctor;
+use rustc_data_structures::sync::Lrc;
 use rustc_index::{Idx, IndexVec};
 
 use std::ops::ControlFlow;
-use std::rc::Rc;
-use std::sync::Arc;
 
 ///////////////////////////////////////////////////////////////////////////
 // Atomic structs
@@ -106,25 +105,13 @@ impl<I: Interner, T: TypeVisitable<I>, E: TypeVisitable<I>> TypeVisitable<I> for
     }
 }
 
-impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Rc<T> {
+impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Lrc<T> {
     fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
         self.try_map_id(|value| value.try_fold_with(folder))
     }
 }
 
-impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Rc<T> {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        (**self).visit_with(visitor)
-    }
-}
-
-impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Arc<T> {
-    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
-        self.try_map_id(|value| value.try_fold_with(folder))
-    }
-}
-
-impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Arc<T> {
+impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Lrc<T> {
     fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
         (**self).visit_with(visitor)
     }
@@ -154,19 +141,11 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Vec<T> {
     }
 }
 
-impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for &[T] {
-    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
-        self.iter().try_for_each(|t| t.visit_with(visitor))
-    }
-}
-
-impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Box<[T]> {
-    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
-        self.try_map_id(|t| t.try_fold_with(folder))
-    }
-}
+// `TypeFoldable` isn't impl'd for `&[T]`. It doesn't make sense in the general
+// case, because we can't return a new slice. But note that there are a couple
+// of trivial impls of `TypeFoldable` for specific slice types elsewhere.
 
-impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Box<[T]> {
+impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for &[T] {
     fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
         self.iter().try_for_each(|t| t.visit_with(visitor))
     }
diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs
index c5a5991cc81..ec774e62deb 100644
--- a/library/std/src/alloc.rs
+++ b/library/std/src/alloc.rs
@@ -93,7 +93,7 @@ pub use alloc_crate::alloc::*;
 ///
 /// ```rust
 /// use std::alloc::{System, GlobalAlloc, Layout};
-/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
+/// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
 ///
 /// struct Counter;
 ///
@@ -103,14 +103,14 @@ pub use alloc_crate::alloc::*;
 ///     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
 ///         let ret = System.alloc(layout);
 ///         if !ret.is_null() {
-///             ALLOCATED.fetch_add(layout.size(), SeqCst);
+///             ALLOCATED.fetch_add(layout.size(), Relaxed);
 ///         }
 ///         ret
 ///     }
 ///
 ///     unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
 ///         System.dealloc(ptr, layout);
-///         ALLOCATED.fetch_sub(layout.size(), SeqCst);
+///         ALLOCATED.fetch_sub(layout.size(), Relaxed);
 ///     }
 /// }
 ///
@@ -118,7 +118,7 @@ pub use alloc_crate::alloc::*;
 /// static A: Counter = Counter;
 ///
 /// fn main() {
-///     println!("allocated bytes before main: {}", ALLOCATED.load(SeqCst));
+///     println!("allocated bytes before main: {}", ALLOCATED.load(Relaxed));
 /// }
 /// ```
 ///
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 42a68496fc4..30e553f285b 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -2284,6 +2284,11 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 ///
 /// See [`fs::remove_file`] and [`fs::remove_dir`].
 ///
+/// `remove_dir_all` will fail if `remove_dir` or `remove_file` fail on any constituent paths, including the root path.
+/// As a result, the directory you are deleting must exist, meaning that this function is not idempotent.
+///
+/// Consider ignoring the error if validating the removal is not required for your use case.
+///
 /// [`fs::remove_file`]: remove_file
 /// [`fs::remove_dir`]: remove_dir
 ///
diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs
index 2aefd7c513d..7a7a7737635 100644
--- a/library/std/src/prelude/v1.rs
+++ b/library/std/src/prelude/v1.rs
@@ -91,10 +91,10 @@ pub use core::prelude::v1::cfg_eval;
 )]
 pub use core::prelude::v1::type_ascribe;
 
-// The file so far is equivalent to src/libcore/prelude/v1.rs,
-// and below to src/liballoc/prelude.rs.
-// Those files are duplicated rather than using glob imports
-// because we want docs to show these re-exports as pointing to within `std`.
+// The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated
+// rather than glob imported because we want docs to show these re-exports as
+// pointing to within `std`.
+// Below are the items from the alloc crate.
 
 #[stable(feature = "rust1", since = "1.0.0")]
 #[doc(no_inline)]
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index fa08fdc1653..3b7c31826b9 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -134,10 +134,28 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
 /// thread_local! {
 ///     pub static FOO: RefCell<u32> = RefCell::new(1);
 ///
-///     #[allow(unused)]
 ///     static BAR: RefCell<f32> = RefCell::new(1.0);
 /// }
-/// # fn main() {}
+///
+/// FOO.with(|foo| assert_eq!(*foo.borrow(), 1));
+/// BAR.with(|bar| assert_eq!(*bar.borrow(), 1.0));
+/// ```
+///
+/// This macro supports a special `const {}` syntax that can be used
+/// when the initialization expression can be evaluated as a constant.
+/// This can enable a more efficient thread local implementation that
+/// can avoid lazy initialization. For types that do not
+/// [need to be dropped][crate::mem::needs_drop], this can enable an
+/// even more efficient implementation that does not need to
+/// track any additional state.
+///
+/// ```
+/// use std::cell::Cell;
+/// thread_local! {
+///     pub static FOO: Cell<u32> = const { Cell::new(1) };
+/// }
+///
+/// FOO.with(|foo| assert_eq!(foo.get(), 1));
 /// ```
 ///
 /// See [`LocalKey` documentation][`std::thread::LocalKey`] for more
diff --git a/src/bootstrap/bootstrap_test.py b/src/bootstrap/bootstrap_test.py
index 26bd80a008f..5ecda83ee66 100644
--- a/src/bootstrap/bootstrap_test.py
+++ b/src/bootstrap/bootstrap_test.py
@@ -112,6 +112,14 @@ class GenerateAndParseConfig(unittest.TestCase):
         build = self.serialize_and_parse(["--set", "profile=compiler"])
         self.assertEqual(build.get_toml("profile"), 'compiler')
 
+    def test_set_codegen_backends(self):
+        build = self.serialize_and_parse(["--set", "rust.codegen-backends=cranelift"])
+        self.assertNotEqual(build.config_toml.find("codegen-backends = ['cranelift']"), -1)
+        build = self.serialize_and_parse(["--set", "rust.codegen-backends=cranelift,llvm"])
+        self.assertNotEqual(build.config_toml.find("codegen-backends = ['cranelift', 'llvm']"), -1)
+        build = self.serialize_and_parse(["--enable-full-tools"])
+        self.assertNotEqual(build.config_toml.find("codegen-backends = ['llvm']"), -1)
+
 if __name__ == '__main__':
     SUITE = unittest.TestSuite()
     TEST_LOADER = unittest.TestLoader()
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index dd1851e29a9..571062a3a6f 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -153,8 +153,7 @@ v("experimental-targets", "llvm.experimental-targets",
   "experimental LLVM targets to build")
 v("release-channel", "rust.channel", "the name of the release channel to build")
 v("release-description", "rust.description", "optional descriptive string for version output")
-v("dist-compression-formats", None,
-  "comma-separated list of compression formats to use")
+v("dist-compression-formats", None, "List of compression formats to use")
 
 # Used on systems where "cc" is unavailable
 v("default-linker", "rust.default-linker", "the default linker")
@@ -168,8 +167,8 @@ o("extended", "build.extended", "build an extended rust tool set")
 v("tools", None, "List of extended tools will be installed")
 v("codegen-backends", None, "List of codegen backends to build")
 v("build", "build.build", "GNUs ./configure syntax LLVM build triple")
-v("host", None, "GNUs ./configure syntax LLVM host triples")
-v("target", None, "GNUs ./configure syntax LLVM target triples")
+v("host", None, "List of GNUs ./configure syntax LLVM host triples")
+v("target", None, "List of GNUs ./configure syntax LLVM target triples")
 
 v("set", None, "set arbitrary key/value pairs in TOML configuration")
 
@@ -182,6 +181,11 @@ def err(msg):
     print("configure: error: " + msg)
     sys.exit(1)
 
+def is_value_list(key):
+    for option in options:
+        if option.name == key and option.desc.startswith('List of'):
+            return True
+    return False
 
 if '--help' in sys.argv or '-h' in sys.argv:
     print('Usage: ./configure [options]')
@@ -295,6 +299,8 @@ def set(key, value, config):
     parts = key.split('.')
     for i, part in enumerate(parts):
         if i == len(parts) - 1:
+            if is_value_list(part) and isinstance(value, str):
+                value = value.split(',')
             arr[part] = value
         else:
             if part not in arr:
diff --git a/src/doc/rustdoc/src/scraped-examples.md b/src/doc/rustdoc/src/scraped-examples.md
index d75f6d522e8..7197e01c8e3 100644
--- a/src/doc/rustdoc/src/scraped-examples.md
+++ b/src/doc/rustdoc/src/scraped-examples.md
@@ -24,14 +24,14 @@ Then this code snippet will be included in the documentation for `a_func`. This
 This feature is unstable, so you can enable it by calling Rustdoc with the unstable `rustdoc-scrape-examples` flag:
 
 ```bash
-cargo doc -Zunstable-options -Zrustdoc-scrape-examples=examples
+cargo doc -Zunstable-options -Zrustdoc-scrape-examples
 ```
 
 To enable this feature on [docs.rs](https://docs.rs), add this to your Cargo.toml:
 
 ```toml
 [package.metadata.docs.rs]
-cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples=examples"]
+cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
 ```
 
 
diff --git a/src/doc/style-guide/src/statements.md b/src/doc/style-guide/src/statements.md
index 4ab1c36f976..671e6d31a57 100644
--- a/src/doc/style-guide/src/statements.md
+++ b/src/doc/style-guide/src/statements.md
@@ -138,18 +138,31 @@ Otherwise, the `else` keyword and opening brace should be placed on the next lin
 For example:
 
 ```rust
-let Some(x) = abcdef()
-    .foo(
-        "abc",
-        some_really_really_really_long_ident,
-        "ident",
-        "123456",
-    )
-    .bar()
-    .baz()
-    .qux("fffffffffffffffff")
-else {
-    foo_bar()
+fn main() {
+    let Some(x) = abcdef()
+        .foo(
+            "abc",
+            some_really_really_really_long_ident,
+            "ident",
+            "123456",
+        )
+        .bar()
+        .baz()
+        .qux("fffffffffffffffff")
+    else {
+        return
+    };
+
+    let Some(x) = some_really_really_really_really_really_really_really_really_really_long_name
+    else {
+        return;
+    };
+
+    let Some(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) =
+        bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+    else {
+        return;
+    };
 }
 ```
 
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index f2486abaaa7..7e173a171a8 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -1295,7 +1295,8 @@ impl LinkCollector<'_, '_> {
                                 }
                             }
                         }
-                        resolution_failure(self, diag, path_str, disambiguator, smallvec![err])
+                        resolution_failure(self, diag, path_str, disambiguator, smallvec![err]);
+                        return vec![];
                     }
                 }
             }
@@ -1331,13 +1332,14 @@ impl LinkCollector<'_, '_> {
                     .fold(0, |acc, res| if let Ok(res) = res { acc + res.len() } else { acc });
 
                 if len == 0 {
-                    return resolution_failure(
+                    resolution_failure(
                         self,
                         diag,
                         path_str,
                         disambiguator,
                         candidates.into_iter().filter_map(|res| res.err()).collect(),
                     );
+                    return vec![];
                 } else if len == 1 {
                     candidates.into_iter().filter_map(|res| res.ok()).flatten().collect::<Vec<_>>()
                 } else {
@@ -1642,9 +1644,8 @@ fn resolution_failure(
     path_str: &str,
     disambiguator: Option<Disambiguator>,
     kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
-) -> Vec<(Res, Option<DefId>)> {
+) {
     let tcx = collector.cx.tcx;
-    let mut recovered_res = None;
     report_diagnostic(
         tcx,
         BROKEN_INTRA_DOC_LINKS,
@@ -1736,19 +1737,25 @@ fn resolution_failure(
 
                         if !path_str.contains("::") {
                             if disambiguator.map_or(true, |d| d.ns() == MacroNS)
-                                && let Some(&res) = collector.cx.tcx.resolutions(()).all_macro_rules
-                                                             .get(&Symbol::intern(path_str))
+                                && collector
+                                    .cx
+                                    .tcx
+                                    .resolutions(())
+                                    .all_macro_rules
+                                    .get(&Symbol::intern(path_str))
+                                    .is_some()
                             {
                                 diag.note(format!(
                                     "`macro_rules` named `{path_str}` exists in this crate, \
                                      but it is not in scope at this link's location"
                                 ));
-                                recovered_res = res.try_into().ok().map(|res| (res, None));
                             } else {
                                 // If the link has `::` in it, assume it was meant to be an
                                 // intra-doc link. Otherwise, the `[]` might be unrelated.
-                                diag.help("to escape `[` and `]` characters, \
-                                           add '\\' before them like `\\[` or `\\]`");
+                                diag.help(
+                                    "to escape `[` and `]` characters, \
+                                           add '\\' before them like `\\[` or `\\]`",
+                                );
                             }
                         }
 
@@ -1854,11 +1861,6 @@ fn resolution_failure(
             }
         },
     );
-
-    match recovered_res {
-        Some(r) => vec![r],
-        None => Vec::new(),
-    }
 }
 
 fn report_multiple_anchors(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>) {
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index cb700126c2b..a6572011644 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -226,7 +226,7 @@ pub fn implements_trait_with_env<'tcx>(
     ty_params: impl IntoIterator<Item = Option<GenericArg<'tcx>>>,
 ) -> bool {
     // Clippy shouldn't have infer types
-    assert!(!ty.needs_infer());
+    assert!(!ty.has_infer());
 
     let ty = tcx.erase_regions(ty);
     if ty.has_escaping_bound_vars() {
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index f6597c72938..e03a73c4e71 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -224,6 +224,7 @@ enum Emit {
     Metadata,
     LlvmIr,
     Asm,
+    LinkArgsAsm,
 }
 
 impl<'test> TestCx<'test> {
@@ -2035,6 +2036,9 @@ impl<'test> TestCx<'test> {
             Emit::Asm => {
                 rustc.args(&["--emit", "asm"]);
             }
+            Emit::LinkArgsAsm => {
+                rustc.args(&["-Clink-args=--emit=asm"]);
+            }
         }
 
         if !is_rustdoc {
@@ -2328,11 +2332,15 @@ impl<'test> TestCx<'test> {
                 emit = Emit::Asm;
             }
 
+            Some("bpf-linker") => {
+                emit = Emit::LinkArgsAsm;
+            }
+
             Some("ptx-linker") => {
                 // No extra flags needed.
             }
 
-            Some(_) => self.fatal("unknown 'assembly-output' header"),
+            Some(header) => self.fatal(&format!("unknown 'assembly-output' header: {header}")),
             None => self.fatal("missing 'assembly-output' header"),
         }
 
diff --git a/tests/debuginfo/thread.rs b/tests/debuginfo/thread.rs
index 388d50c5cdc..e7e83c7aacd 100644
--- a/tests/debuginfo/thread.rs
+++ b/tests/debuginfo/thread.rs
@@ -1,4 +1,4 @@
-// Testing the the display of JoinHandle and Thread in cdb.
+// Testing the display of JoinHandle and Thread in cdb.
 
 // cdb-only
 // min-cdb-version: 10.0.18317.1001
diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir
index 7cce3415fa1..9bced25a595 100644
--- a/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir
+++ b/tests/mir-opt/building/async_await.b-{closure#0}.generator_resume.0.mir
@@ -12,7 +12,7 @@
         _1: GeneratorSavedTy {
             ty: impl std::future::Future<Output = ()>,
             source_info: SourceInfo {
-                span: $DIR/async_await.rs:16:8: 16:14 (#11),
+                span: $DIR/async_await.rs:16:8: 16:14 (#10),
                 scope: scope[0],
             },
             ignore_for_traits: false,
diff --git a/tests/rustdoc-ui/doc_cfg_hide.stderr b/tests/rustdoc-ui/doc_cfg_hide.stderr
index 03623368cd0..b7e8870fdf5 100644
--- a/tests/rustdoc-ui/doc_cfg_hide.stderr
+++ b/tests/rustdoc-ui/doc_cfg_hide.stderr
@@ -16,7 +16,7 @@ LL | #![deny(warnings)]
 help: to apply to the crate, use an inner attribute
    |
 LL | #![doc(cfg_hide(doc))]
-   | ~~~~~~~~~~~~~~~~~~~~~~
+   |  +
 
 error: `#[doc(cfg_hide(...)]` takes a list of attributes
   --> $DIR/doc_cfg_hide.rs:4:8
diff --git a/tests/rustdoc-ui/invalid-doc-attr.rs b/tests/rustdoc-ui/invalid-doc-attr.rs
index de004b41e27..c231e43b35c 100644
--- a/tests/rustdoc-ui/invalid-doc-attr.rs
+++ b/tests/rustdoc-ui/invalid-doc-attr.rs
@@ -5,7 +5,7 @@
 //~^ ERROR can only be applied at the crate level
 //~| WARN is being phased out
 //~| HELP to apply to the crate, use an inner attribute
-//~| SUGGESTION #![doc(test(no_crate_inject))]
+//~| SUGGESTION !
 #[doc(inline)]
 //~^ ERROR can only be applied to a `use` item
 //~| WARN is being phased out
diff --git a/tests/rustdoc-ui/invalid-doc-attr.stderr b/tests/rustdoc-ui/invalid-doc-attr.stderr
index 3c66e587b47..b23b8ded867 100644
--- a/tests/rustdoc-ui/invalid-doc-attr.stderr
+++ b/tests/rustdoc-ui/invalid-doc-attr.stderr
@@ -16,7 +16,7 @@ LL | #![deny(warnings)]
 help: to apply to the crate, use an inner attribute
    |
 LL | #![doc(test(no_crate_inject))]
-   |
+   |  +
 
 error: this attribute can only be applied to a `use` item
   --> $DIR/invalid-doc-attr.rs:9:7
diff --git a/tests/rustdoc/issue-106142.rs b/tests/rustdoc/issue-106142.rs
new file mode 100644
index 00000000000..41505e72405
--- /dev/null
+++ b/tests/rustdoc/issue-106142.rs
@@ -0,0 +1,14 @@
+// @has 'issue_106142/a/index.html'
+// @count 'issue_106142/a/index.html' '//ul[@class="item-table"]//li//a' 1
+
+#![allow(rustdoc::broken_intra_doc_links)]
+
+pub mod a {
+    /// [`m`]
+    pub fn f() {}
+
+    #[macro_export]
+    macro_rules! m {
+        () => {};
+    }
+}
diff --git a/tests/ui/attributes/invalid-doc-attr.rs b/tests/ui/attributes/invalid-doc-attr.rs
index de004b41e27..c231e43b35c 100644
--- a/tests/ui/attributes/invalid-doc-attr.rs
+++ b/tests/ui/attributes/invalid-doc-attr.rs
@@ -5,7 +5,7 @@
 //~^ ERROR can only be applied at the crate level
 //~| WARN is being phased out
 //~| HELP to apply to the crate, use an inner attribute
-//~| SUGGESTION #![doc(test(no_crate_inject))]
+//~| SUGGESTION !
 #[doc(inline)]
 //~^ ERROR can only be applied to a `use` item
 //~| WARN is being phased out
diff --git a/tests/ui/attributes/invalid-doc-attr.stderr b/tests/ui/attributes/invalid-doc-attr.stderr
index 3c66e587b47..b23b8ded867 100644
--- a/tests/ui/attributes/invalid-doc-attr.stderr
+++ b/tests/ui/attributes/invalid-doc-attr.stderr
@@ -16,7 +16,7 @@ LL | #![deny(warnings)]
 help: to apply to the crate, use an inner attribute
    |
 LL | #![doc(test(no_crate_inject))]
-   |
+   |  +
 
 error: this attribute can only be applied to a `use` item
   --> $DIR/invalid-doc-attr.rs:9:7
diff --git a/tests/ui/coherence/coherence-overlap-negative-impls.rs b/tests/ui/coherence/coherence-overlap-negative-impls.rs
new file mode 100644
index 00000000000..cd1df53a528
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-negative-impls.rs
@@ -0,0 +1,41 @@
+// check-pass
+// known-bug: #74629
+
+// Should fail. The `0` and `1` impls overlap, violating coherence. Eg, with
+// `T = Test, F = ()`, all bounds are true, making both impls applicable.
+// `Test: Fold<Nil>`, `Test: Fold<()>` are true because of `2`.
+// `Is<Test>: NotNil` is true because of `auto trait` and lack of negative impl.
+
+#![feature(negative_impls)]
+#![feature(auto_traits)]
+
+struct Nil;
+struct Cons<H>(H);
+struct Test;
+
+trait Fold<F> {}
+
+impl<T, F> Fold<F> for Cons<T> // 0
+where
+    T: Fold<Nil>,
+{}
+
+impl<T, F> Fold<F> for Cons<T> // 1
+where
+    T: Fold<F>,
+    private::Is<T>: private::NotNil,
+{}
+
+impl<F> Fold<F> for Test {} // 2
+
+mod private {
+    use crate::Nil;
+
+    pub struct Is<T>(T);
+    pub auto trait NotNil {}
+
+    #[allow(suspicious_auto_trait_impls)]
+    impl !NotNil for Is<Nil> {}
+}
+
+fn main() {}
diff --git a/tests/ui/generic-associated-types/self-outlives-lint.rs b/tests/ui/generic-associated-types/self-outlives-lint.rs
index 673891fc3d1..0ea81b5aecb 100644
--- a/tests/ui/generic-associated-types/self-outlives-lint.rs
+++ b/tests/ui/generic-associated-types/self-outlives-lint.rs
@@ -189,7 +189,7 @@ trait MultipleMethods {
 }
 
 // We would normally require `Self: 'a`, but we can prove that `Self: 'static`
-// because of the the bounds on the trait, so the bound is proven
+// because of the bounds on the trait, so the bound is proven
 trait Trait: 'static {
     type Assoc<'a>;
     fn make_assoc(_: &u32) -> Self::Assoc<'_>;
diff --git a/tests/ui/methods/method-not-found-generic-arg-elision.rs b/tests/ui/methods/method-not-found-generic-arg-elision.rs
index 799ced5e9c4..538eeadae08 100644
--- a/tests/ui/methods/method-not-found-generic-arg-elision.rs
+++ b/tests/ui/methods/method-not-found-generic-arg-elision.rs
@@ -83,8 +83,8 @@ fn main() {
     //~^ ERROR no method named `distance` found for struct `Point<i32>
     let d = point_i32.other();
     //~^ ERROR no method named `other` found for struct `Point
-    let v = vec![1_i32, 2, 3];
-    v.iter().map(|x| x * x).extend(std::iter::once(100));
+    let v = vec![1, 2, 3];
+    v.iter().map(Box::new(|x| x * x) as Box<dyn Fn(&i32) -> i32>).extend(std::iter::once(100));
     //~^ ERROR no method named `extend` found for struct `Map
     let wrapper = Wrapper(true);
     wrapper.method();
diff --git a/tests/ui/methods/method-not-found-generic-arg-elision.stderr b/tests/ui/methods/method-not-found-generic-arg-elision.stderr
index f3db56d1d53..b97688d3868 100644
--- a/tests/ui/methods/method-not-found-generic-arg-elision.stderr
+++ b/tests/ui/methods/method-not-found-generic-arg-elision.stderr
@@ -20,10 +20,10 @@ LL |     let d = point_i32.other();
    |                       ^^^^^ method not found in `Point<i32>`
 
 error[E0599]: no method named `extend` found for struct `Map` in the current scope
-  --> $DIR/method-not-found-generic-arg-elision.rs:87:29
+  --> $DIR/method-not-found-generic-arg-elision.rs:87:67
    |
-LL |     v.iter().map(|x| x * x).extend(std::iter::once(100));
-   |                             ^^^^^^ method not found in `Map<Iter<'_, i32>, [closure@method-not-found-generic-arg-elision.rs:87:18]>`
+LL |     v.iter().map(Box::new(|x| x * x) as Box<dyn Fn(&i32) -> i32>).extend(std::iter::once(100));
+   |                                                                   ^^^^^^ method not found in `Map<Iter<'_, i32>, Box<dyn Fn(&i32) -> i32>>`
 
 error[E0599]: no method named `method` found for struct `Wrapper<bool>` in the current scope
   --> $DIR/method-not-found-generic-arg-elision.rs:90:13
diff --git a/tests/ui/panics/fmt-only-once.rs b/tests/ui/panics/fmt-only-once.rs
new file mode 100644
index 00000000000..6211bf961b3
--- /dev/null
+++ b/tests/ui/panics/fmt-only-once.rs
@@ -0,0 +1,21 @@
+// run-fail
+// check-run-results
+// exec-env:RUST_BACKTRACE=0
+
+// Test that we format the panic message only once.
+// Regression test for https://github.com/rust-lang/rust/issues/110717
+
+use std::fmt;
+
+struct PrintOnFmt;
+
+impl fmt::Display for PrintOnFmt {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        eprintln!("fmt");
+        f.write_str("PrintOnFmt")
+    }
+}
+
+fn main() {
+    panic!("{}", PrintOnFmt)
+}
diff --git a/tests/ui/panics/fmt-only-once.run.stderr b/tests/ui/panics/fmt-only-once.run.stderr
new file mode 100644
index 00000000000..39bd06881ad
--- /dev/null
+++ b/tests/ui/panics/fmt-only-once.run.stderr
@@ -0,0 +1,3 @@
+fmt
+thread 'main' panicked at 'PrintOnFmt', $DIR/fmt-only-once.rs:20:5
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
diff --git a/tests/ui/specialization/issue-40582.rs b/tests/ui/specialization/issue-40582.rs
new file mode 100644
index 00000000000..9805933553d
--- /dev/null
+++ b/tests/ui/specialization/issue-40582.rs
@@ -0,0 +1,35 @@
+// check-pass
+// known-bug: #40582
+
+// Should fail. Should not be possible to implement `make_static`.
+
+#![feature(specialization)]
+#![allow(incomplete_features)]
+
+trait FromRef<'a, T: ?Sized> {
+    fn from_ref(r: &'a T) -> Self;
+}
+
+impl<'a, T: ?Sized> FromRef<'a, T> for &'a T {
+    fn from_ref(r: &'a T) -> Self {
+        r
+    }
+}
+
+impl<'a, T: ?Sized, R> FromRef<'a, T> for R {
+    default fn from_ref(_: &'a T) -> Self {
+        unimplemented!()
+    }
+}
+
+fn make_static<T: ?Sized>(data: &T) -> &'static T {
+    fn helper<T: ?Sized, R>(data: &T) -> R {
+        R::from_ref(data)
+    }
+    helper(data)
+}
+
+fn main() {
+    let s = "specialization".to_owned();
+    println!("{:?}", make_static(s.as_str()));
+}
diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.rs b/tests/ui/specialization/specialization-default-items-drop-coherence.rs
new file mode 100644
index 00000000000..16ad942d5ab
--- /dev/null
+++ b/tests/ui/specialization/specialization-default-items-drop-coherence.rs
@@ -0,0 +1,30 @@
+// check-pass
+// known-bug: #105782
+
+// Should fail. Default items completely drop candidates instead of ambiguity,
+// which is unsound during coherence, since coherence requires completeness.
+
+#![feature(specialization)]
+#![allow(incomplete_features)]
+
+trait Default {
+   type Id;
+}
+
+impl<T> Default for T {
+   default type Id = T;
+}
+
+trait Overlap {
+   type Assoc;
+}
+
+impl Overlap for u32 {
+   type Assoc = usize;
+}
+
+impl Overlap for <u32 as Default>::Id {
+   type Assoc = Box<usize>;
+}
+
+fn main() {}
diff --git a/tests/ui/thread-local/thread-local-static-ref-use-after-free.rs b/tests/ui/thread-local/thread-local-static-ref-use-after-free.rs
new file mode 100644
index 00000000000..c282e2185bc
--- /dev/null
+++ b/tests/ui/thread-local/thread-local-static-ref-use-after-free.rs
@@ -0,0 +1,46 @@
+// check-pass
+// known-bug: #49682
+// edition:2021
+
+// Should fail. Keeping references to thread local statics can result in a
+// use-after-free.
+
+#![feature(thread_local)]
+
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::thread;
+
+#[allow(dead_code)]
+#[thread_local]
+static FOO: AtomicUsize = AtomicUsize::new(0);
+
+#[allow(dead_code)]
+async fn bar() {}
+
+#[allow(dead_code)]
+async fn foo() {
+    let r = &FOO;
+    bar().await;
+    r.load(Ordering::SeqCst);
+}
+
+fn main() {
+    // &FOO = 0x7fd1e9cbf6d0
+    _ = thread::spawn(|| {
+        let g = foo();
+        println!("&FOO = {:p}", &FOO);
+        g
+    })
+    .join()
+    .unwrap();
+
+    // &FOO = 0x7fd1e9cc0f50
+    println!("&FOO = {:p}", &FOO);
+
+    // &FOO = 0x7fd1e9cbf6d0
+    thread::spawn(move || {
+        println!("&FOO = {:p}", &FOO);
+    })
+    .join()
+    .unwrap();
+}