about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTshepang Mbambo <hopsi@tuta.io>2025-06-26 06:36:49 +0200
committerGitHub <noreply@github.com>2025-06-26 06:36:49 +0200
commitfe2fd8a2d3fce02b07c7a15072812d86092dcbaf (patch)
treeab8fc713b93940db4b05f29280f5af4ebd8a73e2
parent6ad42bf4e7ab81ec3d66a5e47dfe01dc51603c0b (diff)
parentfdf44db88020c7dad46647aaa81ec05a295e96f2 (diff)
downloadrust-fe2fd8a2d3fce02b07c7a15072812d86092dcbaf.tar.gz
rust-fe2fd8a2d3fce02b07c7a15072812d86092dcbaf.zip
Merge pull request #2479 from rust-lang/rustc-pull
Rustc pull update
-rw-r--r--.github/ISSUE_TEMPLATE/rustdoc.md54
-rw-r--r--.mailmap5
-rw-r--r--Cargo.lock336
-rw-r--r--Cargo.toml7
-rw-r--r--RELEASES.md100
-rw-r--r--bootstrap.example.toml393
-rw-r--r--compiler/rustc_abi/src/canon_abi.rs4
-rw-r--r--compiler/rustc_abi/src/extern_abi.rs13
-rw-r--r--compiler/rustc_abi/src/layout.rs12
-rw-r--r--compiler/rustc_abi/src/layout/ty.rs2
-rw-r--r--compiler/rustc_abi/src/lib.rs2
-rw-r--r--compiler/rustc_ast/Cargo.toml2
-rw-r--r--compiler/rustc_ast/src/ast.rs4
-rw-r--r--compiler/rustc_ast/src/ast_traits.rs7
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs29
-rw-r--r--compiler/rustc_ast/src/format.rs8
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs490
-rw-r--r--compiler/rustc_ast/src/token.rs1
-rw-r--r--compiler/rustc_ast/src/util/literal.rs15
-rw-r--r--compiler/rustc_ast/src/visit.rs655
-rw-r--r--compiler/rustc_ast_lowering/Cargo.toml1
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs15
-rw-r--r--compiler/rustc_ast_lowering/src/format.rs215
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs28
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs20
-rw-r--r--compiler/rustc_ast_lowering/src/stability.rs9
-rw-r--r--compiler/rustc_ast_passes/Cargo.toml1
-rw-r--r--compiler/rustc_ast_passes/messages.ftl23
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs87
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs22
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs54
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs43
-rw-r--r--compiler/rustc_attr_data_structures/src/encode_cross_crate.rs42
-rw-r--r--compiler/rustc_attr_data_structures/src/lib.rs2
-rw-r--r--compiler/rustc_attr_parsing/messages.ftl7
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs203
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/inline.rs6
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs21
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/loop_match.rs31
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs35
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/must_use.rs40
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/repr.rs61
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/semantics.rs22
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/stability.rs9
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/traits.rs54
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs146
-rw-r--r--compiler/rustc_attr_parsing/src/parser.rs20
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs37
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs38
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs11
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs51
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs10
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs4
-rw-r--r--compiler/rustc_borrowck/src/lib.rs30
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl6
-rw-r--r--compiler/rustc_builtin_macros/src/alloc_error_handler.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/autodiff.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_eval.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/concat_bytes.rs40
-rw-r--r--compiler/rustc_builtin_macros/src/concat_idents.rs71
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs31
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/standard_library_imports.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.lock84
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.toml24
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch69
-rw-r--r--compiler/rustc_codegen_cranelift/rust-toolchain2
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/abi-cafe-rules.toml17
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh14
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs7
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs7
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_gcc/messages.ftl16
-rw-r--r--compiler/rustc_codegen_gcc/src/builder.rs4
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs26
-rw-r--r--compiler/rustc_codegen_gcc/src/gcc_util.rs123
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs52
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl10
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs26
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs17
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs279
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl26
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs131
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs20
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs47
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs174
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs111
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/naked_asm.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs284
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/builder.rs2
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs8
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs34
-rw-r--r--compiler/rustc_const_eval/src/errors.rs7
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs14
-rw-r--r--compiler/rustc_data_structures/Cargo.toml2
-rw-r--r--compiler/rustc_data_structures/src/sync.rs2
-rw-r--r--compiler/rustc_data_structures/src/sync/lock.rs2
-rw-r--r--compiler/rustc_data_structures/src/sync/parallel.rs14
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs20
-rw-r--r--compiler/rustc_driver_impl/src/pretty.rs6
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0722.md8
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0775.md2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0781.md8
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0798.md14
-rw-r--r--compiler/rustc_error_codes/src/lib.rs3
-rw-r--r--compiler/rustc_error_messages/Cargo.toml1
-rw-r--r--compiler/rustc_error_messages/src/lib.rs18
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs36
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs32
-rw-r--r--compiler/rustc_errors/src/emitter.rs88
-rw-r--r--compiler/rustc_errors/src/json.rs41
-rw-r--r--compiler/rustc_errors/src/json/tests.rs6
-rw-r--r--compiler/rustc_errors/src/lib.rs43
-rw-r--r--compiler/rustc_errors/src/tests.rs43
-rw-r--r--compiler/rustc_errors/src/timings.rs47
-rw-r--r--compiler/rustc_errors/src/translation.rs43
-rw-r--r--compiler/rustc_expand/messages.ftl2
-rw-r--r--compiler/rustc_expand/src/config.rs1
-rw-r--r--compiler/rustc_expand/src/errors.rs7
-rw-r--r--compiler/rustc_expand/src/expand.rs114
-rw-r--r--compiler/rustc_expand/src/mbe.rs10
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs1
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs12
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs53
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs49
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs128
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs771
-rw-r--r--compiler/rustc_expand/src/placeholders.rs4
-rw-r--r--compiler/rustc_expand/src/stats.rs28
-rw-r--r--compiler/rustc_feature/Cargo.toml3
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs25
-rw-r--r--compiler/rustc_feature/src/removed.rs15
-rw-r--r--compiler/rustc_feature/src/unstable.rs10
-rw-r--r--compiler/rustc_hir/src/hir.rs32
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl9
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs54
-rw-r--r--compiler/rustc_hir_analysis/src/check/entry.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs68
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs86
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs132
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs157
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs2
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs102
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl9
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs63
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs25
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs20
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs51
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs69
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/loops.rs70
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/naked_functions.rs5
-rw-r--r--compiler/rustc_infer/src/infer/canonical/instantiate.rs189
-rw-r--r--compiler/rustc_interface/Cargo.toml2
-rw-r--r--compiler/rustc_interface/src/interface.rs19
-rw-r--r--compiler/rustc_interface/src/passes.rs15
-rw-r--r--compiler/rustc_interface/src/queries.rs3
-rw-r--r--compiler/rustc_interface/src/tests.rs9
-rw-r--r--compiler/rustc_interface/src/util.rs44
-rw-r--r--compiler/rustc_lint/src/builtin.rs25
-rw-r--r--compiler/rustc_lint/src/context.rs30
-rw-r--r--compiler/rustc_lint/src/expect.rs3
-rw-r--r--compiler/rustc_lint/src/levels.rs5
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_lint/src/lifetime_syntax.rs4
-rw-r--r--compiler/rustc_lint/src/lints.rs36
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs119
-rw-r--r--compiler/rustc_lint/src/unused.rs9
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs1
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs30
-rw-r--r--compiler/rustc_metadata/src/dependency_format.rs2
-rw-r--r--compiler/rustc_metadata/src/errors.rs3
-rw-r--r--compiler/rustc_metadata/src/lib.rs2
-rw-r--r--compiler/rustc_metadata/src/locator.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs44
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs47
-rw-r--r--compiler/rustc_middle/Cargo.toml2
-rw-r--r--compiler/rustc_middle/src/hir/map.rs26
-rw-r--r--compiler/rustc_middle/src/hir/mod.rs10
-rw-r--r--compiler/rustc_middle/src/lib.rs2
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs3
-rw-r--r--compiler/rustc_middle/src/middle/exported_symbols.rs2
-rw-r--r--compiler/rustc_middle/src/thir.rs13
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs3
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs82
-rw-r--r--compiler/rustc_middle/src/ty/context.rs81
-rw-r--r--compiler/rustc_middle/src/ty/context/tls.rs2
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/mod.rs10
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs7
-rw-r--r--compiler/rustc_mir_build/Cargo.toml1
-rw-r--r--compiler/rustc_mir_build/messages.ftl33
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_place.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/category.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/into.rs122
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/stmt.rs3
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/mod.rs144
-rw-r--r--compiler/rustc_mir_build/src/builder/scope.rs264
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs2
-rw-r--r--compiler/rustc_mir_build/src/errors.rs94
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs192
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs14
-rw-r--r--compiler/rustc_mir_build/src/thir/print.rs21
-rw-r--r--compiler/rustc_mir_transform/src/copy_prop.rs52
-rw-r--r--compiler/rustc_mir_transform/src/jump_threading.rs2
-rw-r--r--compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs4
-rw-r--r--compiler/rustc_mir_transform/src/simplify.rs2
-rw-r--r--compiler/rustc_mir_transform/src/ssa.rs23
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs12
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs49
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs15
-rw-r--r--compiler/rustc_parse/Cargo.toml2
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs94
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs7
-rw-r--r--compiler/rustc_parse/src/parser/item.rs51
-rw-r--r--compiler/rustc_parse/src/parser/tests.rs6
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs12
-rw-r--r--compiler/rustc_parse_format/Cargo.toml2
-rw-r--r--compiler/rustc_parse_format/src/lib.rs3
-rw-r--r--compiler/rustc_passes/messages.ftl31
-rw-r--r--compiler/rustc_passes/src/check_attr.rs357
-rw-r--r--compiler/rustc_passes/src/dead.rs80
-rw-r--r--compiler/rustc_passes/src/errors.rs62
-rw-r--r--compiler/rustc_passes/src/input_stats.rs61
-rw-r--r--compiler/rustc_passes/src/liveness.rs4
-rw-r--r--compiler/rustc_pattern_analysis/src/constructor.rs3
-rw-r--r--compiler/rustc_proc_macro/Cargo.toml2
-rw-r--r--compiler/rustc_query_system/Cargo.toml2
-rw-r--r--compiler/rustc_query_system/src/query/job.rs10
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs4
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs14
-rw-r--r--compiler/rustc_resolve/src/errors.rs5
-rw-r--r--compiler/rustc_resolve/src/imports.rs18
-rw-r--r--compiler/rustc_resolve/src/late.rs209
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs7
-rw-r--r--compiler/rustc_serialize/src/int_overflow.rs2
-rw-r--r--compiler/rustc_serialize/src/opaque.rs2
-rw-r--r--compiler/rustc_session/Cargo.toml1
-rw-r--r--compiler/rustc_session/messages.ftl8
-rw-r--r--compiler/rustc_session/src/config.rs35
-rw-r--r--compiler/rustc_session/src/errors.rs17
-rw-r--r--compiler/rustc_session/src/features.rs59
-rw-r--r--compiler/rustc_session/src/filesearch.rs18
-rw-r--r--compiler/rustc_session/src/lib.rs1
-rw-r--r--compiler/rustc_session/src/options.rs27
-rw-r--r--compiler/rustc_session/src/parse.rs22
-rw-r--r--compiler/rustc_session/src/session.rs60
-rw-r--r--compiler/rustc_smir/src/rustc_internal/internal.rs5
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs6
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs5
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs1
-rw-r--r--compiler/rustc_span/src/hygiene.rs16
-rw-r--r--compiler/rustc_span/src/symbol.rs17
-rw-r--r--compiler/rustc_target/src/spec/abi_map.rs28
-rw-r--r--compiler/rustc_target/src/target_features.rs45
-rw-r--r--compiler/rustc_thread_pool/Cargo.toml50
-rw-r--r--compiler/rustc_thread_pool/README.md10
-rw-r--r--compiler/rustc_thread_pool/src/broadcast/mod.rs148
-rw-r--r--compiler/rustc_thread_pool/src/broadcast/tests.rs264
-rw-r--r--compiler/rustc_thread_pool/src/compile_fail/mod.rs7
-rw-r--r--compiler/rustc_thread_pool/src/compile_fail/quicksort_race1.rs28
-rw-r--r--compiler/rustc_thread_pool/src/compile_fail/quicksort_race2.rs28
-rw-r--r--compiler/rustc_thread_pool/src/compile_fail/quicksort_race3.rs28
-rw-r--r--compiler/rustc_thread_pool/src/compile_fail/rc_return.rs17
-rw-r--r--compiler/rustc_thread_pool/src/compile_fail/rc_upvar.rs9
-rw-r--r--compiler/rustc_thread_pool/src/compile_fail/scope_join_bad.rs24
-rw-r--r--compiler/rustc_thread_pool/src/job.rs277
-rw-r--r--compiler/rustc_thread_pool/src/join/mod.rs201
-rw-r--r--compiler/rustc_thread_pool/src/join/tests.rs151
-rw-r--r--compiler/rustc_thread_pool/src/latch.rs431
-rw-r--r--compiler/rustc_thread_pool/src/lib.rs903
-rw-r--r--compiler/rustc_thread_pool/src/private.rs26
-rw-r--r--compiler/rustc_thread_pool/src/registry.rs1025
-rw-r--r--compiler/rustc_thread_pool/src/scope/mod.rs783
-rw-r--r--compiler/rustc_thread_pool/src/scope/tests.rs607
-rw-r--r--compiler/rustc_thread_pool/src/sleep/README.md252
-rw-r--r--compiler/rustc_thread_pool/src/sleep/counters.rs273
-rw-r--r--compiler/rustc_thread_pool/src/sleep/mod.rs386
-rw-r--r--compiler/rustc_thread_pool/src/spawn/mod.rs165
-rw-r--r--compiler/rustc_thread_pool/src/spawn/tests.rs246
-rw-r--r--compiler/rustc_thread_pool/src/tests.rs197
-rw-r--r--compiler/rustc_thread_pool/src/thread_pool/mod.rs513
-rw-r--r--compiler/rustc_thread_pool/src/thread_pool/tests.rs416
-rw-r--r--compiler/rustc_thread_pool/src/tlv.rs32
-rw-r--r--compiler/rustc_thread_pool/src/unwind.rs31
-rw-r--r--compiler/rustc_thread_pool/src/worker_local.rs75
-rw-r--r--compiler/rustc_thread_pool/tests/double_init_fail.rs15
-rw-r--r--compiler/rustc_thread_pool/tests/init_zero_threads.rs9
-rw-r--r--compiler/rustc_thread_pool/tests/scope_join.rs47
-rw-r--r--compiler/rustc_thread_pool/tests/scoped_threadpool.rs99
-rw-r--r--compiler/rustc_thread_pool/tests/simple_panic.rs9
-rw-r--r--compiler/rustc_thread_pool/tests/stack_overflow_crash.rs87
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs45
-rw-r--r--compiler/rustc_trait_selection/src/errors/note_and_explain.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs24
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs18
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs2
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs8
-rw-r--r--compiler/rustc_type_ir/src/fast_reject.rs4
-rw-r--r--compiler/rustc_type_ir/src/interner.rs7
-rw-r--r--compiler/rustc_type_ir/src/search_graph/global_cache.rs17
-rw-r--r--compiler/rustc_type_ir/src/search_graph/mod.rs168
-rw-r--r--compiler/rustc_type_ir/src/search_graph/stack.rs8
-rw-r--r--library/Cargo.lock19
-rw-r--r--library/Cargo.toml13
-rw-r--r--library/alloc/src/collections/linked_list/tests.rs18
-rw-r--r--library/alloc/src/collections/vec_deque/tests.rs38
-rw-r--r--library/alloc/src/ffi/c_str.rs109
-rw-r--r--library/alloc/src/string.rs28
-rw-r--r--library/alloctests/testing/macros.rs37
-rw-r--r--library/alloctests/testing/mod.rs1
-rw-r--r--library/alloctests/tests/fmt.rs46
-rw-r--r--library/alloctests/tests/num.rs4
-rw-r--r--library/alloctests/tests/testing/mod.rs2
-rw-r--r--library/alloctests/tests/vec.rs119
-rw-r--r--library/alloctests/tests/vec_deque.rs119
-rw-r--r--library/core/src/ascii.rs1
-rw-r--r--library/core/src/char/methods.rs1
-rw-r--r--library/core/src/clone.rs9
-rw-r--r--library/core/src/cmp.rs25
-rw-r--r--library/core/src/default.rs1
-rw-r--r--library/core/src/ffi/c_str.rs16
-rw-r--r--library/core/src/fmt/mod.rs19
-rw-r--r--library/core/src/fmt/num.rs205
-rw-r--r--library/core/src/fmt/rt.rs38
-rw-r--r--library/core/src/iter/adapters/fuse.rs24
-rw-r--r--library/core/src/iter/range.rs1
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/core/src/macros/mod.rs40
-rw-r--r--library/core/src/marker/variance.rs2
-rw-r--r--library/core/src/mem/mod.rs1
-rw-r--r--library/core/src/num/int_macros.rs208
-rw-r--r--library/core/src/num/mod.rs4
-rw-r--r--library/core/src/num/saturating.rs46
-rw-r--r--library/core/src/num/uint_macros.rs197
-rw-r--r--library/core/src/ops/arith.rs24
-rw-r--r--library/core/src/prelude/v1.rs3
-rw-r--r--library/core/src/slice/mod.rs83
-rw-r--r--library/core/src/slice/raw.rs1
-rw-r--r--library/core/src/str/mod.rs83
-rw-r--r--library/coretests/tests/atomic.rs30
-rw-r--r--library/coretests/tests/fmt/mod.rs15
-rw-r--r--library/proc_macro/Cargo.toml2
-rw-r--r--library/proc_macro/src/lib.rs13
-rw-r--r--library/rustc-std-workspace-core/Cargo.toml8
-rw-r--r--library/std/src/io/error.rs2
-rw-r--r--library/std/src/lib.rs6
-rw-r--r--library/std/src/macros.rs9
-rw-r--r--library/std/src/net/tcp.rs6
-rw-r--r--library/std/src/os/unix/mod.rs3
-rw-r--r--library/std/src/os/unix/net/stream.rs23
-rw-r--r--library/std/src/os/unix/process.rs35
-rw-r--r--library/std/src/path.rs17
-rw-r--r--library/std/src/prelude/v1.rs3
-rw-r--r--library/std/src/sys/fs/windows.rs1
-rw-r--r--library/std/src/sys/net/connection/socket/unix.rs8
-rw-r--r--library/std/src/sys/pal/unix/linux/pidfd.rs6
-rw-r--r--library/std/src/sys/path/cygwin.rs92
-rw-r--r--library/std/src/sys/path/mod.rs5
-rw-r--r--library/std/src/sys/path/windows.rs175
-rw-r--r--library/std/src/sys/path/windows/tests.rs2
-rw-r--r--library/std/src/sys/path/windows_prefix.rs182
-rw-r--r--library/std/src/sys/process/unix/fuchsia.rs5
-rw-r--r--library/std/src/sys/process/unix/unix.rs12
-rw-r--r--library/std/src/sys/process/unix/unsupported.rs6
-rw-r--r--library/std/src/sys/process/unix/vxworks.rs8
-rw-r--r--library/std/src/sys/random/uefi.rs169
-rw-r--r--library/std/tests/path.rs472
-rw-r--r--library/std/tests/sync/mpmc.rs4
-rw-r--r--src/bootstrap/build.rs1
-rwxr-xr-xsrc/bootstrap/configure.py31
-rw-r--r--src/bootstrap/src/bin/rustc.rs12
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs77
-rw-r--r--src/bootstrap/src/core/build_steps/clippy.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs20
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs1
-rw-r--r--src/bootstrap/src/core/builder/tests.rs53
-rw-r--r--src/bootstrap/src/core/config/config.rs4
-rw-r--r--src/bootstrap/src/utils/exec.rs5
-rw-r--r--src/bootstrap/src/utils/helpers.rs5
-rw-r--r--src/bootstrap/src/utils/tests/mod.rs71
-rw-r--r--src/build_helper/src/ci.rs6
-rw-r--r--src/build_helper/src/git.rs2
-rw-r--r--src/ci/docker/host-x86_64/mingw-check-1/Dockerfile2
-rwxr-xr-xsrc/ci/docker/host-x86_64/mingw-check-1/validate-error-codes.sh20
-rw-r--r--src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile57
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh62
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh55
-rw-r--r--src/ci/github-actions/jobs.yml7
m---------src/doc/book0
m---------src/doc/reference0
m---------src/doc/rust-by-example0
-rw-r--r--src/doc/rustc-dev-guide/rust-version2
-rw-r--r--src/doc/rustc-dev-guide/src/autodiff/flags.md1
-rw-r--r--src/doc/rustc-dev-guide/src/tests/ui.md10
-rw-r--r--src/doc/unstable-book/src/compiler-flags/autodiff.md1
-rw-r--r--src/doc/unstable-book/src/compiler-flags/macro-stats.md6
-rw-r--r--src/doc/unstable-book/src/compiler-flags/min-function-alignment.md2
-rw-r--r--src/doc/unstable-book/src/language-features/abi-cmse-nonsecure-call.md (renamed from src/doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md)16
-rw-r--r--src/doc/unstable-book/src/language-features/cmse-nonsecure-entry.md13
-rw-r--r--src/doc/unstable-book/src/language-features/loop-match.md52
-rw-r--r--src/doc/unstable-book/src/language-features/macro-metavar-expr-concat.md6
-rw-r--r--src/doc/unstable-book/src/library-features/concat-idents.md27
-rw-r--r--src/librustdoc/clean/blanket_impl.rs2
-rw-r--r--src/librustdoc/clean/types.rs155
-rw-r--r--src/librustdoc/clean/utils.rs4
-rw-r--r--src/librustdoc/config.rs14
-rw-r--r--src/librustdoc/core.rs13
-rw-r--r--src/librustdoc/doctest.rs7
-rw-r--r--src/librustdoc/doctest/make.rs9
-rw-r--r--src/librustdoc/formats/renderer.rs2
-rw-r--r--src/librustdoc/html/render/context.rs4
-rw-r--r--src/librustdoc/html/render/mod.rs4
-rw-r--r--src/librustdoc/html/render/print_item.rs5
-rw-r--r--src/librustdoc/html/render/write_shared.rs39
-rw-r--r--src/librustdoc/html/sources.rs2
-rw-r--r--src/librustdoc/html/static/js/main.js30
-rw-r--r--src/librustdoc/html/static/js/search.js78
-rw-r--r--src/librustdoc/json/conversions.rs390
-rw-r--r--src/librustdoc/json/mod.rs37
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs2
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs28
-rw-r--r--src/rustdoc-json-types/lib.rs14
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/Cargo.toml1
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/io_other_error.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/to_digit_is_some.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/useless_concat.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs9
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/mod.rs16
-rw-r--r--src/tools/clippy/src/driver.rs35
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_closure.stdout39
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_loop.stdout35
-rw-r--r--src/tools/clippy/tests/ui/manual_inspect.fixed1
-rw-r--r--src/tools/clippy/tests/ui/manual_inspect.rs1
-rw-r--r--src/tools/clippy/tests/ui/manual_inspect.stderr23
-rw-r--r--src/tools/compiletest/src/errors.rs46
-rw-r--r--src/tools/compiletest/src/header.rs2
-rw-r--r--src/tools/compiletest/src/json.rs40
-rw-r--r--src/tools/compiletest/src/runtest.rs147
-rw-r--r--src/tools/compiletest/src/runtest/run_make.rs4
m---------src/tools/enzyme0
-rw-r--r--src/tools/jsondoclint/src/validator.rs13
-rwxr-xr-xsrc/tools/linkchecker/linkcheck.sh1
-rw-r--r--src/tools/lint-docs/Cargo.toml2
-rw-r--r--src/tools/lint-docs/src/lib.rs4
-rw-r--r--src/tools/miri/tests/pass/fn_align.rs10
-rw-r--r--src/tools/run-make-support/src/external_deps/rustc.rs24
-rw-r--r--src/tools/run-make-support/src/external_deps/rustdoc.rs37
-rw-r--r--src/tools/run-make-support/src/lib.rs4
-rw-r--r--src/tools/run-make-support/src/macros.rs9
-rw-r--r--src/tools/run-make-support/src/run.rs25
-rw-r--r--src/tools/run-make-support/src/targets.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs227
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs138
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/attrs.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/has_source.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs448
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs67
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs41
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/term_search.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs33
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs98
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs207
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs149
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs37
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs326
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs50
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs33
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs112
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs89
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs59
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs60
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs33
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs124
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs28
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/search.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/typing.rs88
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs2
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs1
-rw-r--r--src/tools/rust-analyzer/crates/load-cargo/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs26
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs90
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs8
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs58
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs13
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs150
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs9
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs1
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/variance.rs270
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs7
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/fixture.rs40
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs15
-rw-r--r--src/tools/rust-analyzer/docs/book/src/configuration_generated.md13
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json12
-rw-r--r--src/tools/rust-analyzer/editors/code/src/config.ts14
-rw-r--r--src/tools/rust-analyzer/rust-version2
-rw-r--r--src/tools/rust-installer/install-template.sh8
-rw-r--r--src/tools/rustbook/Cargo.lock86
-rw-r--r--src/tools/rustfmt/src/expr.rs2
-rw-r--r--src/tools/rustfmt/src/imports.rs2
-rw-r--r--src/tools/rustfmt/src/parse/session.rs56
-rw-r--r--src/tools/rustfmt/tests/source/pin_sugar.rs10
-rw-r--r--src/tools/rustfmt/tests/target/pin_sugar.rs7
-rw-r--r--src/tools/tidy/src/deps.rs6
-rw-r--r--src/tools/tidy/src/error_codes.rs32
-rw-r--r--src/tools/tidy/src/issues.txt3
-rw-r--r--src/tools/tidy/src/lib.rs62
-rw-r--r--src/tools/tidy/src/main.rs7
-rw-r--r--src/tools/tidy/src/rustdoc_js.rs3
-rw-r--r--src/tools/tidy/src/rustdoc_json.rs90
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
-rw-r--r--src/tools/wasm-component-ld/Cargo.toml2
-rw-r--r--src/version2
-rw-r--r--tests/assembly/cmse.rs8
-rw-r--r--tests/assembly/naked-functions/wasm32.rs2
-rw-r--r--tests/assembly/s390x-backchain-toggle.rs5
-rw-r--r--tests/auxiliary/minicore.rs7
-rw-r--r--tests/codegen-units/item-collection/drop-glue-noop.rs23
-rw-r--r--tests/codegen-units/item-collection/instantiation-through-vtable.rs2
-rw-r--r--tests/codegen-units/item-collection/non-generic-closures.rs29
-rw-r--r--tests/codegen-units/item-collection/unsizing.rs24
-rw-r--r--tests/codegen/abi-x86-interrupt.rs6
-rw-r--r--tests/codegen/align-fn.rs22
-rw-r--r--tests/codegen/asm/critical.rs1
-rw-r--r--tests/codegen/min-function-alignment.rs16
-rw-r--r--tests/codegen/naked-asan.rs22
-rw-r--r--tests/codegen/naked-fn/aligned.rs2
-rw-r--r--tests/codegen/naked-fn/min-function-alignment.rs4
-rw-r--r--tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs14
-rw-r--r--tests/codegen/target-feature-negative-implication.rs20
-rw-r--r--tests/codegen/target-feature-overrides.rs5
-rw-r--r--tests/codegen/tied-features-strength.rs15
-rw-r--r--tests/codegen/transmute-scalar.rs45
-rw-r--r--tests/crashes/126269.rs12
-rw-r--r--tests/crashes/126982.rs18
-rw-r--r--tests/crashes/130104.rs2
-rw-r--r--tests/crashes/131048.rs7
-rw-r--r--tests/crashes/132142.rs2
-rw-r--r--tests/crashes/132430.rs10
-rw-r--r--tests/crashes/134217.rs9
-rw-r--r--tests/crashes/136678.rs18
-rw-r--r--tests/crashes/138131.rs12
-rw-r--r--tests/crashes/138265.rs12
-rw-r--r--tests/crashes/138738.rs7
-rw-r--r--tests/incremental/issue-61323.rs2
-rw-r--r--tests/incremental/track-deps-in-new-solver.rs2
-rw-r--r--tests/mir-opt/copy-prop/write_to_borrowed.main.CopyProp.diff30
-rw-r--r--tests/mir-opt/copy-prop/write_to_borrowed.rs45
-rw-r--r--tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff159
-rw-r--r--tests/pretty/pin-ergonomics-hir.pp44
-rw-r--r--tests/pretty/pin-ergonomics-hir.rs40
-rw-r--r--tests/pretty/pin-ergonomics.rs13
-rw-r--r--tests/run-make/allow-warnings-cmdline-stability/rmake.rs2
-rw-r--r--tests/run-make/apple-deployment-target/rmake.rs5
-rw-r--r--tests/run-make/apple-sdk-version/rmake.rs9
-rw-r--r--tests/run-make/arm64ec-import-export-static/export.rs27
-rw-r--r--tests/run-make/arm64ec-import-export-static/import.rs12
-rw-r--r--tests/run-make/arm64ec-import-export-static/rmake.rs15
-rw-r--r--tests/run-make/bin-emit-no-symbols/app.rs10
-rw-r--r--tests/run-make/c-link-to-rust-va-list-fn/rmake.rs4
-rw-r--r--tests/run-make/crate-circular-deps-link/a.rs8
-rw-r--r--tests/run-make/crate-circular-deps-link/rmake.rs4
-rw-r--r--tests/run-make/doctests-merge/rmake.rs3
-rw-r--r--tests/run-make/doctests-runtool/rmake.rs4
-rw-r--r--tests/run-make/embed-metadata/rmake.rs5
-rw-r--r--tests/run-make/embed-source-dwarf/rmake.rs2
-rw-r--r--tests/run-make/emit-shared-files/rmake.rs2
-rw-r--r--tests/run-make/emit-stack-sizes/rmake.rs3
-rw-r--r--tests/run-make/env-dep-info/rmake.rs6
-rw-r--r--tests/run-make/exit-code/rmake.rs4
-rw-r--r--tests/run-make/export-executable-symbols/rmake.rs5
-rw-r--r--tests/run-make/export/disambiguator/rmake.rs6
-rw-r--r--tests/run-make/export/extern-opt/rmake.rs6
-rw-r--r--tests/run-make/export/simple/rmake.rs6
-rw-r--r--tests/run-make/extern-diff-internal-name/rmake.rs4
-rw-r--r--tests/run-make/extern-flag-fun/rmake.rs2
-rw-r--r--tests/run-make/extern-multiple-copies/rmake.rs4
-rw-r--r--tests/run-make/extern-multiple-copies2/rmake.rs4
-rw-r--r--tests/run-make/fmt-write-bloat/rmake.rs9
-rw-r--r--tests/run-make/ice-dep-cannot-find-dep/rmake.rs1
-rw-r--r--tests/run-make/include-all-symbols-linking/rmake.rs4
-rw-r--r--tests/run-make/incr-prev-body-beyond-eof/rmake.rs2
-rw-r--r--tests/run-make/incr-test-moved-file/rmake.rs2
-rw-r--r--tests/run-make/intrinsic-unreachable/rmake.rs1
-rw-r--r--tests/run-make/invalid-so/rmake.rs4
-rw-r--r--tests/run-make/issue-125484-used-dependencies/rmake.rs4
-rw-r--r--tests/run-make/json-error-no-offset/rmake.rs4
-rw-r--r--tests/run-make/link-args-order/rmake.rs3
-rw-r--r--tests/run-make/link-dedup/rmake.rs10
-rw-r--r--tests/run-make/linker-warning/rmake.rs6
-rw-r--r--tests/run-make/moved-src-dir-fingerprint-ice/rmake.rs2
-rw-r--r--tests/run-make/mte-ffi/rmake.rs6
-rw-r--r--tests/run-make/naked-symbol-visibility/rmake.rs3
-rw-r--r--tests/run-make/native-lib-alt-naming/rmake.rs6
-rw-r--r--tests/run-make/native-link-modifier-verbatim-linker/rmake.rs3
-rw-r--r--tests/run-make/no-alloc-shim/foo.rs8
-rw-r--r--tests/run-make/no-builtins-attribute/filecheck.main.txt4
-rw-r--r--tests/run-make/no-builtins-attribute/rmake.rs2
-rw-r--r--tests/run-make/no-builtins-lto/rmake.rs4
-rw-r--r--tests/run-make/non-unicode-in-incremental-dir/rmake.rs2
-rw-r--r--tests/run-make/proc-macro-three-crates/rmake.rs6
-rw-r--r--tests/run-make/relro-levels/rmake.rs1
-rw-r--r--tests/run-make/repr128-dwarf/rmake.rs3
-rw-r--r--tests/run-make/reproducible-build-2/rmake.rs38
-rw-r--r--tests/run-make/rlib-format-packed-bundled-libs-2/rmake.rs4
-rw-r--r--tests/run-make/rustc-macro-dep-files/rmake.rs8
-rw-r--r--tests/run-make/rustdoc-default-output/rmake.rs4
-rw-r--r--tests/run-make/rustdoc-dep-info/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-determinism/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-error-lines/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-io-error/rmake.rs1
-rw-r--r--tests/run-make/rustdoc-map-file/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-output-path/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-output-stdout/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-test-args/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-themes/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-verify-output-files/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-with-out-dir-option/rmake.rs2
-rw-r--r--tests/run-make/rustdoc-with-output-option/rmake.rs2
-rw-r--r--tests/run-make/sanitizer-dylib-link/program.rs2
-rw-r--r--tests/run-make/share-generics-dylib/rmake.rs6
-rw-r--r--tests/run-make/short-ice/rmake.rs15
-rw-r--r--tests/run-make/static-pie/rmake.rs1
-rw-r--r--tests/run-make/staticlib-thin-archive/rmake.rs4
-rw-r--r--tests/run-make/stdin-rustc/rmake.rs10
-rw-r--r--tests/run-make/stdin-rustdoc/rmake.rs2
-rw-r--r--tests/run-make/symbol-visibility/rmake.rs6
-rw-r--r--tests/run-make/sysroot-crates-are-unstable/rmake.rs1
-rw-r--r--tests/run-make/textrel-on-minimal-lib/rmake.rs8
-rw-r--r--tests/run-make/track-path-dep-info/rmake.rs6
-rw-r--r--tests/run-make/used/rmake.rs3
-rw-r--r--tests/rustdoc-js-std/doc-alias-use.js12
-rw-r--r--tests/rustdoc-js/big-result.rs1
-rw-r--r--tests/rustdoc-json/attrs/cold.rs3
-rw-r--r--tests/rustdoc-json/attrs/must_use.rs4
-rw-r--r--tests/rustdoc-json/attrs/optimize.rs13
-rw-r--r--tests/rustdoc-json/generic-args.rs23
-rw-r--r--tests/rustdoc/target-feature.rs38
-rw-r--r--tests/ui-fulldeps/pprust-parenthesis-insertion.rs20
-rw-r--r--tests/ui-fulldeps/run-compiler-twice.rs6
-rw-r--r--tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr1
-rw-r--r--tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr5
-rw-r--r--tests/ui/SUMMARY.md1596
-rw-r--r--tests/ui/abi/bad-custom.rs34
-rw-r--r--tests/ui/abi/bad-custom.stderr94
-rw-r--r--tests/ui/abi/cannot-be-called.avr.stderr75
-rw-r--r--tests/ui/abi/cannot-be-called.i686.stderr75
-rw-r--r--tests/ui/abi/cannot-be-called.msp430.stderr75
-rw-r--r--tests/ui/abi/cannot-be-called.riscv32.stderr87
-rw-r--r--tests/ui/abi/cannot-be-called.riscv64.stderr87
-rw-r--r--tests/ui/abi/cannot-be-called.rs92
-rw-r--r--tests/ui/abi/cannot-be-called.x64.stderr75
-rw-r--r--tests/ui/abi/cannot-be-called.x64_win.stderr75
-rw-r--r--tests/ui/abi/cannot-be-coroutine.avr.stderr23
-rw-r--r--tests/ui/abi/cannot-be-coroutine.i686.stderr23
-rw-r--r--tests/ui/abi/cannot-be-coroutine.msp430.stderr23
-rw-r--r--tests/ui/abi/cannot-be-coroutine.riscv32.stderr35
-rw-r--r--tests/ui/abi/cannot-be-coroutine.riscv64.stderr35
-rw-r--r--tests/ui/abi/cannot-be-coroutine.rs54
-rw-r--r--tests/ui/abi/cannot-be-coroutine.x64.stderr23
-rw-r--r--tests/ui/abi/cannot-be-coroutine.x64_win.stderr23
-rw-r--r--tests/ui/abi/invalid-call-abi-ctfe.rs14
-rw-r--r--tests/ui/abi/invalid-call-abi-ctfe.stderr9
-rw-r--r--tests/ui/abi/invalid-call-abi.rs12
-rw-r--r--tests/ui/abi/invalid-call-abi.stderr9
-rw-r--r--tests/ui/abi/unsupported-abi-transmute.rs12
-rw-r--r--tests/ui/abi/unsupported-abi-transmute.stderr9
-rw-r--r--tests/ui/abi/unsupported-in-impls.rs36
-rw-r--r--tests/ui/abi/unsupported-in-impls.stderr27
-rw-r--r--tests/ui/abi/unsupported-varargs-fnptr.rs19
-rw-r--r--tests/ui/abi/unsupported-varargs-fnptr.stderr9
-rw-r--r--tests/ui/abi/unsupported.aarch64.stderr443
-rw-r--r--tests/ui/abi/unsupported.arm.stderr403
-rw-r--r--tests/ui/abi/unsupported.i686.stderr245
-rw-r--r--tests/ui/abi/unsupported.riscv32.stderr411
-rw-r--r--tests/ui/abi/unsupported.riscv64.stderr411
-rw-r--r--tests/ui/abi/unsupported.rs67
-rw-r--r--tests/ui/abi/unsupported.x64.stderr361
-rw-r--r--tests/ui/abi/unsupported.x64_win.stderr329
-rw-r--r--tests/ui/allocator/no_std-alloc-error-handler-custom.rs13
-rw-r--r--tests/ui/allocator/no_std-alloc-error-handler-default.rs13
-rw-r--r--tests/ui/argument-suggestions/issue-100154.stderr4
-rw-r--r--tests/ui/argument-suggestions/issue-100478.rs4
-rw-r--r--tests/ui/asm/naked-functions-inline.stderr14
-rw-r--r--tests/ui/asm/naked-invalid-attr.stderr18
-rw-r--r--tests/ui/asm/naked-with-invalid-repr-attr.rs3
-rw-r--r--tests/ui/asm/naked-with-invalid-repr-attr.stderr12
-rw-r--r--tests/ui/associated-inherent-types/bound_vars_in_args.rs22
-rw-r--r--tests/ui/associated-inherent-types/bound_vars_in_args.stderr20
-rw-r--r--tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr33
-rw-r--r--tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs16
-rw-r--r--tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr37
-rw-r--r--tests/ui/associated-inherent-types/candidate-with-alias-2.rs29
-rw-r--r--tests/ui/associated-inherent-types/candidate-with-alias-2.stderr20
-rw-r--r--tests/ui/associated-inherent-types/candidate-with-alias.rs30
-rw-r--r--tests/ui/associated-inherent-types/iat-in-where-bound.rs14
-rw-r--r--tests/ui/associated-inherent-types/iat-inside-of-adt.rs (renamed from tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs)9
-rw-r--r--tests/ui/associated-inherent-types/impl_params_are_infers.rs34
-rw-r--r--tests/ui/associated-inherent-types/impl_params_are_infers.stderr20
-rw-r--r--tests/ui/associated-inherent-types/inhabited-predicates.rs (renamed from tests/crashes/125879.rs)7
-rw-r--r--tests/ui/associated-inherent-types/inhabited-predicates.stderr27
-rw-r--r--tests/ui/associated-inherent-types/issue-109299-1.rs4
-rw-r--r--tests/ui/associated-inherent-types/issue-109299-1.stderr28
-rw-r--r--tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-1.rs23
-rw-r--r--tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-2.rs29
-rw-r--r--tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-2.stderr20
-rw-r--r--tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-3.rs27
-rw-r--r--tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs2
-rw-r--r--tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr18
-rw-r--r--tests/ui/associated-inherent-types/really_deep_self_ty_mismatch.rs26
-rw-r--r--tests/ui/associated-types/defaults-unsound-62211-1.current.stderr3
-rw-r--r--tests/ui/associated-types/defaults-unsound-62211-1.next.stderr3
-rw-r--r--tests/ui/associated-types/defaults-unsound-62211-2.current.stderr3
-rw-r--r--tests/ui/associated-types/defaults-unsound-62211-2.next.stderr3
-rw-r--r--tests/ui/async-await/async-drop/async-without-sync.rs19
-rw-r--r--tests/ui/async-await/async-drop/async-without-sync.stderr10
-rw-r--r--tests/ui/async-await/incorrect-move-async-order-issue-79694.fixed2
-rw-r--r--tests/ui/async-await/incorrect-move-async-order-issue-79694.rs2
-rw-r--r--tests/ui/attributes/arg-error-issue-121425.stderr12
-rw-r--r--tests/ui/attributes/expected-word.rs3
-rw-r--r--tests/ui/attributes/expected-word.stderr12
-rw-r--r--tests/ui/attributes/invalid-repr.rs2
-rw-r--r--tests/ui/attributes/invalid-repr.stderr4
-rw-r--r--tests/ui/attributes/lint_on_root.rs7
-rw-r--r--tests/ui/attributes/lint_on_root.stderr12
-rw-r--r--tests/ui/attributes/malformed-fn-align.rs22
-rw-r--r--tests/ui/attributes/malformed-fn-align.stderr70
-rw-r--r--tests/ui/attributes/malformed-must_use.rs4
-rw-r--r--tests/ui/attributes/malformed-must_use.stderr8
-rw-r--r--tests/ui/attributes/rustc_skip_during_method_dispatch.rs38
-rw-r--r--tests/ui/attributes/rustc_skip_during_method_dispatch.stderr76
-rw-r--r--tests/ui/binop/issue-77910-1.stderr3
-rw-r--r--tests/ui/borrowck/clone-on-ref.stderr4
-rw-r--r--tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs16
-rw-r--r--tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr31
-rw-r--r--tests/ui/check-cfg/target_feature.stderr4
-rw-r--r--tests/ui/closures/issue-111932.stderr4
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/callback-as-argument.rs6
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs8
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr31
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs28
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr70
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs12
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr50
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs20
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr90
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs34
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.rs4
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.stderr6
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.rs4
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.stderr4
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.rs6
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.stderr18
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs24
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr50
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs10
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr50
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs18
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr90
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.aarch64.stderr8
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.rs2
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.thumb7.stderr8
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.x86.stderr8
-rw-r--r--tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs32
-rw-r--r--tests/ui/coercion/invalid-blanket-coerce-unsized-impl.rs13
-rw-r--r--tests/ui/coercion/invalid-blanket-coerce-unsized-impl.stderr19
-rw-r--r--tests/ui/compiletest-self-test/line-annotation-mismatches.rs42
-rw-r--r--tests/ui/compiletest-self-test/line-annotation-mismatches.stderr61
-rw-r--r--tests/ui/const-generics/issues/issue-86535-2.rs2
-rw-r--r--tests/ui/const-generics/issues/issue-86535-2.stderr10
-rw-r--r--tests/ui/const-generics/issues/issue-86535.rs2
-rw-r--r--tests/ui/const-generics/issues/issue-86535.stderr10
-rw-r--r--tests/ui/consts/const-eval/format.stderr2
-rw-r--r--tests/ui/consts/const_cmp_type_id.rs5
-rw-r--r--tests/ui/consts/const_cmp_type_id.stderr26
-rw-r--r--tests/ui/consts/fn_trait_refs.stderr18
-rw-r--r--tests/ui/consts/issue-73976-monomorphic.stderr8
-rw-r--r--tests/ui/consts/issue-90870.rs15
-rw-r--r--tests/ui/consts/issue-90870.stderr58
-rw-r--r--tests/ui/consts/recursive-const-in-impl.stderr5
-rw-r--r--tests/ui/defaults-well-formedness.rs27
-rw-r--r--tests/ui/deprecation/deprecated-expr-precedence.rs8
-rw-r--r--tests/ui/deprecation/deprecated-expr-precedence.stderr11
-rw-r--r--tests/ui/deprecation/deprecated_main_function.rs (renamed from tests/ui/deprecation-in-force-unstable.rs)2
-rw-r--r--tests/ui/deprecation/deprecated_no_stack_check.rs2
-rw-r--r--tests/ui/deprecation/deprecated_no_stack_check.stderr4
-rw-r--r--tests/ui/deref-rc.rs8
-rw-r--r--tests/ui/deref.rs6
-rw-r--r--tests/ui/derive-uninhabited-enum-38885.rs19
-rw-r--r--tests/ui/derives/clone-debug-dead-code.stderr2
-rw-r--r--tests/ui/derives/derive-debug-uninhabited-enum.rs23
-rw-r--r--tests/ui/derives/derive-debug-uninhabited-enum.stderr (renamed from tests/ui/derive-uninhabited-enum-38885.stderr)2
-rw-r--r--tests/ui/derives/derives-span-Debug-enum-struct-variant.stderr3
-rw-r--r--tests/ui/derives/derives-span-Debug-enum.stderr3
-rw-r--r--tests/ui/derives/derives-span-Debug-struct.stderr3
-rw-r--r--tests/ui/derives/derives-span-Debug-tuple-struct.stderr3
-rw-r--r--tests/ui/derives/nonsense-input-to-debug.rs12
-rw-r--r--tests/ui/derives/nonsense-input-to-debug.stderr30
-rw-r--r--tests/ui/destructure-trait-ref.rs46
-rw-r--r--tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr2
-rw-r--r--tests/ui/diverging-fallback-method-chain.rs20
-rw-r--r--tests/ui/diverging-fallback-option.rs14
-rw-r--r--tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr10
-rw-r--r--tests/ui/dyn-compatibility/supertrait-mentions-GAT.rs3
-rw-r--r--tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr21
-rw-r--r--tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr4
-rw-r--r--tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr4
-rw-r--r--tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr4
-rw-r--r--tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr4
-rw-r--r--tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr4
-rw-r--r--tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr4
-rw-r--r--tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr4
-rw-r--r--tests/ui/extern-flag/auxiliary/panic_handler.rs10
-rw-r--r--tests/ui/feature-gates/feature-gate-abi-custom.rs8
-rw-r--r--tests/ui/feature-gates/feature-gate-abi-custom.stderr8
-rw-r--r--tests/ui/feature-gates/feature-gate-abi_gpu_kernel.AMDGPU.stderr8
-rw-r--r--tests/ui/feature-gates/feature-gate-abi_gpu_kernel.HOST.stderr103
-rw-r--r--tests/ui/feature-gates/feature-gate-abi_gpu_kernel.NVPTX.stderr8
-rw-r--r--tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs4
-rw-r--r--tests/ui/feature-gates/feature-gate-cfi_encoding.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-concat_idents.rs11
-rw-r--r--tests/ui/feature-gates/feature-gate-concat_idents.stderr23
-rw-r--r--tests/ui/feature-gates/feature-gate-concat_idents2.rs6
-rw-r--r--tests/ui/feature-gates/feature-gate-concat_idents2.stderr20
-rw-r--r--tests/ui/feature-gates/feature-gate-concat_idents3.rs9
-rw-r--r--tests/ui/feature-gates/feature-gate-concat_idents3.stderr23
-rw-r--r--tests/ui/feature-gates/feature-gate-coverage-attribute.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-coverage-attribute.stderr6
-rw-r--r--tests/ui/feature-gates/feature-gate-fn_align.rs7
-rw-r--r--tests/ui/feature-gates/feature-gate-fn_align.stderr35
-rw-r--r--tests/ui/feature-gates/feature-gate-loop-match.rs30
-rw-r--r--tests/ui/feature-gates/feature-gate-loop-match.stderr33
-rw-r--r--tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr4
-rw-r--r--tests/ui/feature-gates/feature-gate-optimize_attribute.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-optimize_attribute.stderr13
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.rs28
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr70
-rw-r--r--tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.rs2
-rw-r--r--tests/ui/feature-gates/gated-bad-feature.rs1
-rw-r--r--tests/ui/feature-gates/gated-bad-feature.stderr16
-rw-r--r--tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr16
-rw-r--r--tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs2
-rw-r--r--tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr6
-rw-r--r--tests/ui/fmt/format-args-argument-span.stderr10
-rw-r--r--tests/ui/fmt/ifmt-unimpl.stderr4
-rw-r--r--tests/ui/fmt/non-source-literals.rs13
-rw-r--r--tests/ui/fmt/non-source-literals.stderr53
-rw-r--r--tests/ui/generic-associated-types/generic-associated-types-where.stderr3
-rw-r--r--tests/ui/generics/default-type-params-well-formedness.rs50
-rw-r--r--tests/ui/hygiene/no_implicit_prelude.stderr2
-rw-r--r--tests/ui/impl-header-lifetime-elision/assoc-type.rs2
-rw-r--r--tests/ui/impl-trait/extra-impl-in-trait-impl.fixed1
-rw-r--r--tests/ui/impl-trait/extra-impl-in-trait-impl.rs1
-rw-r--r--tests/ui/impl-trait/extra-impl-in-trait-impl.stderr8
-rw-r--r--tests/ui/impl-trait/in-bindings/lifetime-equality.rs19
-rw-r--r--tests/ui/impl-trait/in-bindings/region-lifetimes.rs17
-rw-r--r--tests/ui/impl-trait/in-trait/doesnt-satisfy.stderr4
-rw-r--r--tests/ui/impl-trait/in-trait/wf-bounds.stderr3
-rw-r--r--tests/ui/impl-trait/precise-capturing/foreign-2021.stderr2
-rw-r--r--tests/ui/impl-trait/precise-capturing/migration-note.stderr10
-rw-r--r--tests/ui/imports/issue-28134.rs2
-rw-r--r--tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2015.stderr (renamed from tests/ui/imports/multiple-extern-by-macro-for-underscore.stderr)2
-rw-r--r--tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2021.stderr8
-rw-r--r--tests/ui/imports/multiple-extern-by-macro-for-underscore.rs4
-rw-r--r--tests/ui/inference/hint-closure-signature-119266.rs2
-rw-r--r--tests/ui/integral-indexing.rs16
-rw-r--r--tests/ui/issues/issue-32950.rs10
-rw-r--r--tests/ui/issues/issue-32950.stderr15
-rw-r--r--tests/ui/issues/issue-43988.stderr4
-rw-r--r--tests/ui/issues/issue-50403.rs6
-rw-r--r--tests/ui/issues/issue-50403.stderr8
-rw-r--r--tests/ui/issues/issue-59488.stderr6
-rw-r--r--tests/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr3
-rw-r--r--tests/ui/issues/issue-92741.rs6
-rw-r--r--tests/ui/lifetimes/no_lending_iterators.rs6
-rw-r--r--tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr2
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs2
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr2
-rw-r--r--tests/ui/lint/dead-code/issue-41883.stderr2
-rw-r--r--tests/ui/lint/dead-code/issue-59003.rs2
-rw-r--r--tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs37
-rw-r--r--tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr20
-rw-r--r--tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr2
-rw-r--r--tests/ui/lint/dead-code/not-lint-adt-appeared-in-pattern-issue-120770.rs32
-rw-r--r--tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs2
-rw-r--r--tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr8
-rw-r--r--tests/ui/lint/dead-code/unused-struct-derive-default.rs1
-rw-r--r--tests/ui/lint/dead-code/unused-struct-derive-default.stderr1
-rw-r--r--tests/ui/lint/lint-non-uppercase-usages.fixed44
-rw-r--r--tests/ui/lint/lint-non-uppercase-usages.rs44
-rw-r--r--tests/ui/lint/lint-non-uppercase-usages.stderr39
-rw-r--r--tests/ui/lint/unused/unused-attr-duplicate.stderr98
-rw-r--r--tests/ui/loop-match/break-to-block.rs23
-rw-r--r--tests/ui/loop-match/const-continue-to-block.rs26
-rw-r--r--tests/ui/loop-match/const-continue-to-block.stderr8
-rw-r--r--tests/ui/loop-match/const-continue-to-loop.rs27
-rw-r--r--tests/ui/loop-match/const-continue-to-loop.stderr8
-rw-r--r--tests/ui/loop-match/const-continue-to-polymorphic-const.rs29
-rw-r--r--tests/ui/loop-match/const-continue-to-polymorphic-const.stderr8
-rw-r--r--tests/ui/loop-match/drop-in-match-arm.rs47
-rw-r--r--tests/ui/loop-match/invalid-attribute.rs43
-rw-r--r--tests/ui/loop-match/invalid-attribute.stderr131
-rw-r--r--tests/ui/loop-match/invalid.rs161
-rw-r--r--tests/ui/loop-match/invalid.stderr91
-rw-r--r--tests/ui/loop-match/loop-match.rs45
-rw-r--r--tests/ui/loop-match/macro.rs48
-rw-r--r--tests/ui/loop-match/nested.rs83
-rw-r--r--tests/ui/loop-match/or-patterns.rs54
-rw-r--r--tests/ui/loop-match/unsupported-type.rs27
-rw-r--r--tests/ui/loop-match/unsupported-type.stderr10
-rw-r--r--tests/ui/loop-match/unwind.rs53
-rw-r--r--tests/ui/loop-match/valid-patterns.rs117
-rw-r--r--tests/ui/macros/concat-bytes-error.rs25
-rw-r--r--tests/ui/macros/concat-bytes-error.stderr99
-rw-r--r--tests/ui/macros/format-args-temporaries-in-write.stderr10
-rw-r--r--tests/ui/macros/macro-comma-support-rpass.rs12
-rw-r--r--tests/ui/macros/macro-match-nonterminal.rs3
-rw-r--r--tests/ui/macros/macro-match-nonterminal.stderr11
-rw-r--r--tests/ui/macros/macro-metavar-expr-concat/empty-input.rs12
-rw-r--r--tests/ui/macros/macro-metavar-expr-concat/empty-input.stderr19
-rw-r--r--tests/ui/macros/macro-missing-fragment-deduplication.rs9
-rw-r--r--tests/ui/macros/macro-missing-fragment-deduplication.stderr58
-rw-r--r--tests/ui/macros/macro-missing-fragment.rs1
-rw-r--r--tests/ui/macros/macro-missing-fragment.stderr12
-rw-r--r--tests/ui/macros/macro-reexport-removed.rs1
-rw-r--r--tests/ui/macros/macro-reexport-removed.stderr6
-rw-r--r--tests/ui/macros/macros-nonfatal-errors.rs5
-rw-r--r--tests/ui/macros/macros-nonfatal-errors.stderr68
-rw-r--r--tests/ui/macros/missing-writer-issue-139830.rs9
-rw-r--r--tests/ui/macros/missing-writer-issue-139830.stderr23
-rw-r--r--tests/ui/methods/suggest-convert-ptr-to-ref.stderr6
-rw-r--r--tests/ui/mir/unreachable-loop-jump-threading.rs (renamed from tests/crashes/131451.rs)3
-rw-r--r--tests/ui/mir/unreachable-loop-jump-threading.stderr10
-rw-r--r--tests/ui/mismatched_types/method-help-unsatisfied-bound.stderr3
-rw-r--r--tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs8
-rw-r--r--tests/ui/modules/issue-107649.stderr5
-rw-r--r--tests/ui/never_type/never-type-fallback-option.rs22
-rw-r--r--tests/ui/no_std/simple-runs.rs12
-rw-r--r--tests/ui/on-unimplemented/no-debug.stderr16
-rw-r--r--tests/ui/panic-runtime/incompatible-type.rs10
-rw-r--r--tests/ui/panics/location-detail-unwrap-multiline.rs2
-rw-r--r--tests/ui/parser/bad-lit-suffixes.rs1
-rw-r--r--tests/ui/parser/bad-lit-suffixes.stderr21
-rw-r--r--tests/ui/parser/issues/issue-105366.fixed1
-rw-r--r--tests/ui/parser/issues/issue-105366.rs1
-rw-r--r--tests/ui/parser/issues/issue-105366.stderr2
-rw-r--r--tests/ui/parser/macro/issue-33569.rs3
-rw-r--r--tests/ui/parser/macro/issue-33569.stderr23
-rw-r--r--tests/ui/parser/recover/recover-field-semi.rs6
-rw-r--r--tests/ui/parser/recover/recover-field-semi.stderr12
-rw-r--r--tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr238
-rw-r--r--tests/ui/pin-ergonomics/borrow-unpin.rs143
-rw-r--r--tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr136
-rw-r--r--tests/ui/pin-ergonomics/borrow.rs38
-rw-r--r--tests/ui/pin-ergonomics/coerce-non-pointer-pin.rs (renamed from tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.rs)0
-rw-r--r--tests/ui/pin-ergonomics/coerce-non-pointer-pin.stderr (renamed from tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.stderr)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-arg.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-arg.rs)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-const-as-mut.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.rs)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-const-as-mut.stderr (renamed from tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.stderr)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-once.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-once.rs)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-once.stderr (renamed from tests/ui/async-await/pin-ergonomics/reborrow-once.stderr)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-self.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-self.rs)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-shorter.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-shorter.rs)0
-rw-r--r--tests/ui/pin-ergonomics/sugar-ambiguity.rs (renamed from tests/ui/async-await/pin-ergonomics/sugar-ambiguity.rs)0
-rw-r--r--tests/ui/pin-ergonomics/sugar-no-const.rs (renamed from tests/ui/async-await/pin-ergonomics/sugar-no-const.rs)0
-rw-r--r--tests/ui/pin-ergonomics/sugar-no-const.stderr (renamed from tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr)0
-rw-r--r--tests/ui/pin-ergonomics/sugar-self.rs (renamed from tests/ui/async-await/pin-ergonomics/sugar-self.rs)0
-rw-r--r--tests/ui/pin-ergonomics/sugar.rs (renamed from tests/ui/async-await/pin-ergonomics/sugar.rs)0
-rw-r--r--tests/ui/print-calling-conventions.stdout5
-rw-r--r--tests/ui/range/range-1.stderr10
-rw-r--r--tests/ui/repr/attr-usage-repr.rs2
-rw-r--r--tests/ui/repr/attr-usage-repr.stderr4
-rw-r--r--tests/ui/repr/malformed-repr-hints.stderr24
-rw-r--r--tests/ui/repr/repr.stderr6
-rw-r--r--tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr8
-rw-r--r--tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr8
-rw-r--r--tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-ref-impl.rs17
-rw-r--r--tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs3
-rw-r--r--tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr7
-rw-r--r--tests/ui/rustdoc/renamed-features-rustdoc_internals.rs2
-rw-r--r--tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr8
-rw-r--r--tests/ui/sanitizer/cfi/invalid-attr-encoding.rs2
-rw-r--r--tests/ui/sized/unsized-binding.rs4
-rw-r--r--tests/ui/span/issue-71363.stderr7
-rw-r--r--tests/ui/statics/read_before_init.rs22
-rw-r--r--tests/ui/statics/read_before_init.stderr17
-rw-r--r--tests/ui/stats/input-stats.stderr196
-rw-r--r--tests/ui/stats/macro-stats.stderr40
-rw-r--r--tests/ui/suggestions/bound-suggestions.stderr24
-rw-r--r--tests/ui/suggestions/derive-macro-missing-bounds.stderr3
-rw-r--r--tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs3
-rw-r--r--tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr52
-rw-r--r--tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.rs6
-rw-r--r--tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr38
-rw-r--r--tests/ui/suggestions/dyn-incompatible-trait-should-use-self.rs6
-rw-r--r--tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr38
-rw-r--r--tests/ui/suggestions/impl-trait-with-missing-bounds.stderr18
-rw-r--r--tests/ui/suggestions/issue-105645.rs2
-rw-r--r--tests/ui/suggestions/issue-116434-2015.rs2
-rw-r--r--tests/ui/suggestions/issue-116434-2015.stderr18
-rw-r--r--tests/ui/suggestions/issue-81098.stderr10
-rw-r--r--tests/ui/suggestions/issue-97760.stderr7
-rw-r--r--tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr6
-rw-r--r--tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr6
-rw-r--r--tests/ui/suggestions/path-display.stderr9
-rw-r--r--tests/ui/suggestions/suggest-full-enum-variant-for-local-module.rs2
-rw-r--r--tests/ui/syntax-extension-minor.rs15
-rw-r--r--tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr10
-rw-r--r--tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs2
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr2
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr2
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr2
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.rs6
-rw-r--r--tests/ui/thir-print/thir-tree-loop-match.rs22
-rw-r--r--tests/ui/thir-print/thir-tree-loop-match.stdout301
-rw-r--r--tests/ui/traits/const-traits/call-const-trait-method-pass.rs3
-rw-r--r--tests/ui/traits/const-traits/call-const-trait-method-pass.stderr20
-rw-r--r--tests/ui/traits/const-traits/call-generic-in-impl.rs3
-rw-r--r--tests/ui/traits/const-traits/call-generic-in-impl.stderr30
-rw-r--r--tests/ui/traits/const-traits/call-generic-method-chain.rs3
-rw-r--r--tests/ui/traits/const-traits/call-generic-method-chain.stderr66
-rw-r--r--tests/ui/traits/const-traits/call-generic-method-dup-bound.rs3
-rw-r--r--tests/ui/traits/const-traits/call-generic-method-dup-bound.stderr74
-rw-r--r--tests/ui/traits/const-traits/call-generic-method-fail.rs2
-rw-r--r--tests/ui/traits/const-traits/call-generic-method-fail.stderr6
-rw-r--r--tests/ui/traits/const-traits/call-generic-method-pass.rs3
-rw-r--r--tests/ui/traits/const-traits/call-generic-method-pass.stderr47
-rw-r--r--tests/ui/traits/const-traits/const-impl-trait.stderr200
-rw-r--r--tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs1
-rw-r--r--tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr6
-rw-r--r--tests/ui/traits/const-traits/const_derives/derive-const-use.stderr58
-rw-r--r--tests/ui/traits/const-traits/const_derives/derive-const-with-params.rs3
-rw-r--r--tests/ui/traits/const-traits/const_derives/derive-const-with-params.stderr35
-rw-r--r--tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs1
-rw-r--r--tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr11
-rw-r--r--tests/ui/traits/on_unimplemented_long_types.stderr3
-rw-r--r--tests/ui/traits/suggest-remove-deref-issue-140166.stderr4
-rw-r--r--tests/ui/traits/trait-object-destructure.rs29
-rw-r--r--tests/ui/traits/trait-object-destructure.stderr (renamed from tests/ui/destructure-trait-ref.stderr)12
-rw-r--r--tests/ui/type-alias-impl-trait/bounds-are-checked3.stderr3
-rw-r--r--tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr2
-rw-r--r--tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr2
-rw-r--r--tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr8
-rw-r--r--tests/ui/type-alias-impl-trait/nested.stderr4
-rw-r--r--tests/ui/type/binding-assigned-block-without-tail-expression.stderr16
-rw-r--r--tests/ui/type/option-ref-advice.rs4
-rw-r--r--tests/ui/type/pattern_types/derives_fail.stderr4
-rw-r--r--tests/ui/typeck/auxiliary/private-dep.rs3
-rw-r--r--tests/ui/typeck/auxiliary/public-dep.rs11
-rw-r--r--tests/ui/typeck/dont-suggest-private-dependencies.rs37
-rw-r--r--tests/ui/typeck/dont-suggest-private-dependencies.stderr27
-rw-r--r--tests/ui/typeck/inference-method-chain-diverging-fallback.rs19
-rw-r--r--tests/ui/typeck/issue-100246.rs2
-rw-r--r--tests/ui/typeck/issue-89275.rs2
-rw-r--r--tests/ui/typeck/point-at-type-param-in-path-expr.stderr4
-rw-r--r--tests/ui/underscore-imports/issue-110164.ed2015.stderr (renamed from tests/ui/underscore-imports/issue-110164.stderr)36
-rw-r--r--tests/ui/underscore-imports/issue-110164.ed2021.stderr39
-rw-r--r--tests/ui/underscore-imports/issue-110164.rs7
-rw-r--r--tests/ui/underscore-imports/multiple-uses.ed2015.stderr49
-rw-r--r--tests/ui/underscore-imports/multiple-uses.ed2021.stderr49
-rw-r--r--tests/ui/underscore-imports/multiple-uses.rs16
-rw-r--r--tests/ui/unpretty/deprecated-attr.rs5
-rw-r--r--tests/ui/unpretty/deprecated-attr.stdout9
-rw-r--r--tests/ui/unpretty/diagnostic-attr.stdout4
-rw-r--r--tests/ui/unpretty/exhaustive-asm.hir.stdout2
-rw-r--r--tests/ui/unpretty/exhaustive.expanded.stdout10
-rw-r--r--tests/ui/unpretty/exhaustive.hir.stderr40
-rw-r--r--tests/ui/unpretty/exhaustive.hir.stdout85
-rw-r--r--tests/ui/unpretty/exhaustive.rs11
-rw-r--r--tests/ui/unpretty/flattened-format-args.stdout6
-rw-r--r--tests/ui/unsized-locals/yote.rs2
-rw-r--r--tests/ui/unsized-locals/yote.stderr4
-rw-r--r--tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs1
-rw-r--r--tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr19
-rw-r--r--tests/ui/wf/issue-87495.stderr5
-rw-r--r--triagebot.toml6
1190 files changed, 30374 insertions, 13184 deletions
diff --git a/.github/ISSUE_TEMPLATE/rustdoc.md b/.github/ISSUE_TEMPLATE/rustdoc.md
new file mode 100644
index 00000000000..130d5f67102
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/rustdoc.md
@@ -0,0 +1,54 @@
+---
+name: Problem with rustdoc
+about: Report an issue with how docs get generated.
+labels: C-bug, T-rustdoc
+---
+<!--
+Thank you for filing a rustdoc issue! Rustdoc is the tool that handles the generation of docs.  It is usually invoked via `cargo doc`, but can also be used directly.
+
+If you have an issue with the actual content of the docs, use the "Documentation problem" template instead.
+-->
+
+# Code
+<!-- problematic snippet and/or link to repo and/or full path of standard library function -->
+
+```rust
+<code>
+```
+
+# Reproduction Steps
+<!--
+* command(s) to run, if any
+* permalink to hosted documentation, if any
+* search query, if any
+-->
+
+# Expected Outcome
+<!--
+What did you want to happen?
+
+For GUI issues, feel free to provide a mockup image of what you want it to look like.
+
+For diagnostics, please provide a mockup of the desired output in a code block.
+-->
+
+# Actual Output
+<!--
+* rustdoc console output
+* browser screenshot of generated html
+* rustdoc json (prettify by running through `jq` or running thorugh an online formatter)
+-->
+```console
+<code>
+```
+
+
+# Version
+<!--
+Available via `rustdoc --version` or under the "Help" menu.
+
+If the issue involves opening the documentation in a browser, please also provide the name and version of the browser used.
+-->
+
+# Additional Details
+<!-- Anything else you think is relevant -->
diff --git a/.mailmap b/.mailmap
index b9fb7be0403..2a53cbf9eff 100644
--- a/.mailmap
+++ b/.mailmap
@@ -162,8 +162,10 @@ David Carlier <devnexen@gmail.com>
 David Klein <david.klein@baesystemsdetica.com>
 David Manescu <david.manescu@gmail.com> <dman2626@uni.sydney.edu.au>
 David Ross <daboross@daboross.net>
-David Wood <david@davidtw.co> <david.wood@huawei.com>
+David Wood <david@davidtw.co> <Q0KPU0H1YOEPHRY1R2SN5B5RL@david.davidtw.co>
+David Wood <david@davidtw.co> <agile.lion3441@fuligin.ink>
 David Wood <david@davidtw.co> <david.wood2@arm.com>
+David Wood <david@davidtw.co> <david.wood@huawei.com>
 Deadbeef <ent3rm4n@gmail.com>
 Deadbeef <ent3rm4n@gmail.com> <fee1-dead-beef@protonmail.com>
 dependabot[bot] <dependabot[bot]@users.noreply.github.com> <27856297+dependabot-preview[bot]@users.noreply.github.com>
@@ -698,3 +700,4 @@ Zach Pomerantz <zmp@umich.edu>
 Zack Corr <zack@z0w0.me> <zackcorr95@gmail.com>
 Zack Slayton <zack.slayton@gmail.com>
 Zbigniew Siciarz <zbigniew@siciarz.net> Zbigniew Siciarz <antyqjon@gmail.com>
+y21 <30553356+y21@users.noreply.github.com>
diff --git a/Cargo.lock b/Cargo.lock
index 4d95f5e11b2..e95cacf1f6d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,9 +13,9 @@ dependencies = [
 
 [[package]]
 name = "adler2"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
 
 [[package]]
 name = "aes"
@@ -75,7 +75,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4"
 dependencies = [
  "anstyle",
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
 ]
 
 [[package]]
@@ -136,7 +136,7 @@ dependencies = [
  "anstyle-lossy",
  "anstyle-parse",
  "html-escape",
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
 ]
 
 [[package]]
@@ -204,7 +204,7 @@ dependencies = [
  "rustc-hash 2.1.1",
  "serde",
  "serde_derive",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -216,7 +216,7 @@ dependencies = [
  "memchr",
  "serde",
  "serde_derive",
- "winnow 0.7.10",
+ "winnow 0.7.11",
 ]
 
 [[package]]
@@ -431,9 +431,9 @@ dependencies = [
 
 [[package]]
 name = "cfg-if"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
 
 [[package]]
 name = "cfg_aliases"
@@ -487,9 +487,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.39"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f"
+checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -507,9 +507,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.39"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51"
+checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
 dependencies = [
  "anstream",
  "anstyle",
@@ -519,21 +519,21 @@ dependencies = [
 
 [[package]]
 name = "clap_derive"
-version = "4.5.32"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
+checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
 dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.7.4"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
 
 [[package]]
 name = "clippy"
@@ -558,7 +558,7 @@ dependencies = [
  "rustc_tools_util 0.4.2",
  "serde",
  "serde_json",
- "syn 2.0.101",
+ "syn 2.0.103",
  "tempfile",
  "termize",
  "tokio",
@@ -673,7 +673,7 @@ dependencies = [
  "nom",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -748,7 +748,7 @@ dependencies = [
  "encode_unicode",
  "libc",
  "once_cell",
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
  "windows-sys 0.59.0",
 ]
 
@@ -900,7 +900,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -911,7 +911,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -939,7 +939,7 @@ checksum = "e73f2692d4bd3cac41dca28934a39894200c9fabf49586d77d0e5954af1d7902"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -960,7 +960,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -970,7 +970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
 dependencies = [
  "derive_builder_core",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -982,7 +982,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -1050,7 +1050,7 @@ dependencies = [
  "libc",
  "option-ext",
  "redox_users 0.5.0",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -1072,7 +1072,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -1352,7 +1352,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -1416,11 +1416,11 @@ dependencies = [
 
 [[package]]
 name = "getopts"
-version = "0.2.21"
+version = "0.2.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
 dependencies = [
- "unicode-width 0.1.14",
+ "unicode-width 0.2.1",
 ]
 
 [[package]]
@@ -1431,7 +1431,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi 0.11.1+wasi-snapshot-preview1",
 ]
 
 [[package]]
@@ -1511,9 +1511,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "hermit-abi"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
 
 [[package]]
 name = "hex"
@@ -1775,7 +1775,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -1859,7 +1859,7 @@ dependencies = [
  "console",
  "number_prefix",
  "portable-atomic",
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
  "web-time",
 ]
 
@@ -1933,9 +1933,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
 
 [[package]]
 name = "jiff"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93"
+checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
 dependencies = [
  "jiff-static",
  "log",
@@ -1946,13 +1946,13 @@ dependencies = [
 
 [[package]]
 name = "jiff-static"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442"
+checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -2045,9 +2045,9 @@ checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7"
 
 [[package]]
 name = "libc"
-version = "0.2.172"
+version = "0.2.174"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
 
 [[package]]
 name = "libdbus-sys"
@@ -2085,7 +2085,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
 dependencies = [
  "cfg-if",
- "windows-targets 0.53.0",
+ "windows-targets 0.53.2",
 ]
 
 [[package]]
@@ -2223,7 +2223,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -2261,9 +2261,9 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.7.4"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
 
 [[package]]
 name = "memmap2"
@@ -2276,9 +2276,9 @@ dependencies = [
 
 [[package]]
 name = "minifier"
-version = "0.3.5"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bfdc64e2f805f3d12965f10522000bae36e88d2cfea44112331f467d4f4bf68"
+checksum = "14f1541610994bba178cb36757e102d06a52a2d9612aa6d34c64b3b377c5d943"
 
 [[package]]
 name = "minimal-lexical"
@@ -2288,9 +2288,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.8"
+version = "0.8.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
 dependencies = [
  "adler2",
 ]
@@ -2512,15 +2512,15 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.37.0"
+version = "0.37.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6273adb7096cf9ab4335f258e627d8230e69d40d45567d678f552dcec6245215"
+checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a"
 dependencies = [
  "crc32fast",
  "hashbrown",
  "indexmap",
  "memchr",
- "wasmparser 0.232.0",
+ "wasmparser 0.234.0",
 ]
 
 [[package]]
@@ -2725,7 +2725,7 @@ dependencies = [
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -2984,6 +2984,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "rand_xorshift"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
+dependencies = [
+ "rand_core 0.9.3",
+]
+
+[[package]]
 name = "rand_xoshiro"
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3014,9 +3023,9 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.12"
+version = "0.5.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
+checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
 dependencies = [
  "bitflags",
 ]
@@ -3148,9 +3157,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.24"
+version = "0.1.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
 
 [[package]]
 name = "rustc-hash"
@@ -3166,9 +3175,9 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
 
 [[package]]
 name = "rustc-literal-escaper"
-version = "0.0.2"
+version = "0.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
+checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b"
 
 [[package]]
 name = "rustc-main"
@@ -3183,16 +3192,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "rustc-rayon-core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f42932dcd3bcbe484b38a3ccf79b7906fac41c02d408b5b1bac26da3416efdb"
-dependencies = [
- "crossbeam-deque",
- "crossbeam-utils",
-]
-
-[[package]]
 name = "rustc-semver"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3283,6 +3282,7 @@ dependencies = [
  "rustc_abi",
  "rustc_ast",
  "rustc_ast_pretty",
+ "rustc_attr_data_structures",
  "rustc_attr_parsing",
  "rustc_data_structures",
  "rustc_errors",
@@ -3317,6 +3317,7 @@ dependencies = [
  "rustc_parse",
  "rustc_session",
  "rustc_span",
+ "rustc_target",
  "thin-vec",
 ]
 
@@ -3440,7 +3441,7 @@ dependencies = [
  "itertools",
  "libc",
  "measureme",
- "object 0.37.0",
+ "object 0.37.1",
  "rustc-demangle",
  "rustc_abi",
  "rustc_ast",
@@ -3479,7 +3480,7 @@ dependencies = [
  "cc",
  "itertools",
  "libc",
- "object 0.37.0",
+ "object 0.37.1",
  "pathdiff",
  "regex",
  "rustc_abi",
@@ -3558,7 +3559,6 @@ dependencies = [
  "parking_lot",
  "portable-atomic",
  "rustc-hash 2.1.1",
- "rustc-rayon-core",
  "rustc-stable-hash",
  "rustc_arena",
  "rustc_graphviz",
@@ -3566,6 +3566,7 @@ dependencies = [
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
+ "rustc_thread_pool",
  "smallvec",
  "stacker",
  "tempfile",
@@ -3656,7 +3657,6 @@ dependencies = [
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
- "smallvec",
  "tracing",
  "unic-langid",
 ]
@@ -3724,6 +3724,7 @@ dependencies = [
 name = "rustc_feature"
 version = "0.0.0"
 dependencies = [
+ "rustc_attr_data_structures",
  "rustc_data_structures",
  "rustc_span",
  "serde",
@@ -3739,7 +3740,7 @@ dependencies = [
  "fluent-syntax",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
  "unic-langid",
 ]
 
@@ -3888,7 +3889,7 @@ version = "0.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -3913,7 +3914,6 @@ dependencies = [
 name = "rustc_interface"
 version = "0.0.0"
 dependencies = [
- "rustc-rayon-core",
  "rustc_abi",
  "rustc_ast",
  "rustc_ast_lowering",
@@ -3952,6 +3952,7 @@ dependencies = [
  "rustc_span",
  "rustc_symbol_mangling",
  "rustc_target",
+ "rustc_thread_pool",
  "rustc_trait_selection",
  "rustc_traits",
  "rustc_ty_utils",
@@ -4035,7 +4036,7 @@ version = "0.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
  "synstructure",
 ]
 
@@ -4079,7 +4080,6 @@ dependencies = [
  "either",
  "gsgdt",
  "polonius-engine",
- "rustc-rayon-core",
  "rustc_abi",
  "rustc_apfloat",
  "rustc_arena",
@@ -4103,6 +4103,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
+ "rustc_thread_pool",
  "rustc_type_ir",
  "smallvec",
  "thin-vec",
@@ -4118,6 +4119,7 @@ dependencies = [
  "rustc_apfloat",
  "rustc_arena",
  "rustc_ast",
+ "rustc_attr_data_structures",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fluent_macro",
@@ -4240,7 +4242,7 @@ dependencies = [
  "thin-vec",
  "tracing",
  "unicode-normalization",
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
 ]
 
 [[package]]
@@ -4349,7 +4351,6 @@ version = "0.0.0"
 dependencies = [
  "hashbrown",
  "parking_lot",
- "rustc-rayon-core",
  "rustc_abi",
  "rustc_ast",
  "rustc_attr_data_structures",
@@ -4364,6 +4365,7 @@ dependencies = [
  "rustc_serialize",
  "rustc_session",
  "rustc_span",
+ "rustc_thread_pool",
  "smallvec",
  "tracing",
 ]
@@ -4447,7 +4449,6 @@ dependencies = [
  "rustc_serialize",
  "rustc_span",
  "rustc_target",
- "smallvec",
  "termize",
  "tracing",
  "windows",
@@ -4489,7 +4490,7 @@ dependencies = [
  "sha1",
  "sha2",
  "tracing",
- "unicode-width 0.2.0",
+ "unicode-width 0.2.1",
 ]
 
 [[package]]
@@ -4514,7 +4515,7 @@ name = "rustc_target"
 version = "0.0.0"
 dependencies = [
  "bitflags",
- "object 0.37.0",
+ "object 0.37.1",
  "rustc_abi",
  "rustc_data_structures",
  "rustc_fs_util",
@@ -4526,6 +4527,18 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustc_thread_pool"
+version = "0.0.0"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "libc",
+ "rand 0.9.1",
+ "rand_xorshift",
+ "scoped-tls",
+]
+
+[[package]]
 name = "rustc_tools_util"
 version = "0.4.2"
 
@@ -4635,7 +4648,7 @@ version = "0.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
  "synstructure",
 ]
 
@@ -4726,7 +4739,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -4854,7 +4867,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -5094,9 +5107,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.101"
+version = "2.0.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
+checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5111,7 +5124,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -5244,7 +5257,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -5255,7 +5268,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -5272,12 +5285,11 @@ dependencies = [
 
 [[package]]
 name = "thread_local"
-version = "1.1.8"
+version = "1.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
 dependencies = [
  "cfg-if",
- "once_cell",
 ]
 
 [[package]]
@@ -5431,7 +5443,7 @@ checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -5602,7 +5614,7 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5"
 dependencies = [
  "proc-macro-hack",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
  "unic-langid-impl",
 ]
 
@@ -5670,9 +5682,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
 
 [[package]]
 name = "unicode-width"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
 
 [[package]]
 name = "unicode-xid"
@@ -5773,9 +5785,9 @@ dependencies = [
 
 [[package]]
 name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 
 [[package]]
 name = "wasi"
@@ -5788,9 +5800,9 @@ dependencies = [
 
 [[package]]
 name = "wasi-preview1-component-adapter-provider"
-version = "31.0.0"
+version = "34.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86fabda09a0d89ffd1615b297b4a5d4b4d99df9598aeb24685837e63019e927b"
+checksum = "aafa1e6af9a954a4bcf6ef420c33355d0ce84ddc6afbcba7bb6f05126f9120ae"
 
 [[package]]
 name = "wasm-bindgen"
@@ -5814,7 +5826,7 @@ dependencies = [
  "log",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
  "wasm-bindgen-shared",
 ]
 
@@ -5836,7 +5848,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -5852,9 +5864,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-component-ld"
-version = "0.5.13"
+version = "0.5.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a60a07a994a3538b57d8c5f8caba19f4793fb4c7156276e5e90e90acbb829e20"
+checksum = "b015ec93764aa5517bc8b839efa9941b90be8ce680b1134f8224644ba1e48e3f"
 dependencies = [
  "anyhow",
  "clap",
@@ -5862,7 +5874,7 @@ dependencies = [
  "libc",
  "tempfile",
  "wasi-preview1-component-adapter-provider",
- "wasmparser 0.229.0",
+ "wasmparser 0.234.0",
  "wat",
  "windows-sys 0.59.0",
  "winsplit",
@@ -5889,34 +5901,34 @@ dependencies = [
 
 [[package]]
 name = "wasm-encoder"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38ba1d491ecacb085a2552025c10a675a6fddcbd03b1fc9b36c536010ce265d2"
+checksum = "170a0157eef517a179f2d20ed7c68df9c3f7f6c1c047782d488bf5a464174684"
 dependencies = [
  "leb128fmt",
- "wasmparser 0.229.0",
+ "wasmparser 0.234.0",
 ]
 
 [[package]]
 name = "wasm-encoder"
-version = "0.233.0"
+version = "0.235.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9679ae3cf7cfa2ca3a327f7fab97f27f3294d402fd1a76ca8ab514e17973e4d3"
+checksum = "b3bc393c395cb621367ff02d854179882b9a351b4e0c93d1397e6090b53a5c2a"
 dependencies = [
  "leb128fmt",
- "wasmparser 0.233.0",
+ "wasmparser 0.235.0",
 ]
 
 [[package]]
 name = "wasm-metadata"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78fdb7d29a79191ab363dc90c1ddd3a1e880ffd5348d92d48482393a9e6c5f4d"
+checksum = "a42fe3f5cbfb56fc65311ef827930d06189160038e81db62188f66b4bf468e3a"
 dependencies = [
  "anyhow",
  "indexmap",
- "wasm-encoder 0.229.0",
- "wasmparser 0.229.0",
+ "wasm-encoder 0.234.0",
+ "wasmparser 0.234.0",
 ]
 
 [[package]]
@@ -5931,9 +5943,9 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cc3b1f053f5d41aa55640a1fa9b6d1b8a9e4418d118ce308d20e24ff3575a8c"
+checksum = "be22e5a8f600afce671dd53c8d2dd26b4b7aa810fd18ae27dfc49737f3e02fc5"
 dependencies = [
  "bitflags",
  "hashbrown",
@@ -5944,18 +5956,9 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.232.0"
+version = "0.235.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "917739b33bb1eb0e9a49bcd2637a351931be4578d0cc4d37b908d7a797784fbb"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "wasmparser"
-version = "0.233.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b51cb03afce7964bbfce46602d6cb358726f36430b6ba084ac6020d8ce5bc102"
+checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917"
 dependencies = [
  "bitflags",
  "indexmap",
@@ -5964,22 +5967,22 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "233.0.0"
+version = "235.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2eaf4099d8d0c922b83bf3c90663f5666f0769db9e525184284ebbbdb1dd2180"
+checksum = "1eda4293f626c99021bb3a6fbe4fbbe90c0e31a5ace89b5f620af8925de72e13"
 dependencies = [
  "bumpalo",
  "leb128fmt",
  "memchr",
- "unicode-width 0.2.0",
- "wasm-encoder 0.233.0",
+ "unicode-width 0.2.1",
+ "wasm-encoder 0.235.0",
 ]
 
 [[package]]
 name = "wat"
-version = "1.233.0"
+version = "1.235.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d9bc80f5e4b25ea086ef41b91ccd244adde45d931c384d94a8ff64ab8bd7d87"
+checksum = "e777e0327115793cb96ab220b98f85327ec3d11f34ec9e8d723264522ef206aa"
 dependencies = [
  "wast",
 ]
@@ -6027,9 +6030,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows"
-version = "0.61.1"
+version = "0.61.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
+checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
 dependencies = [
  "windows-collections",
  "windows-core",
@@ -6090,7 +6093,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -6101,14 +6104,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
 name = "windows-link"
-version = "0.1.1"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
 
 [[package]]
 name = "windows-numerics"
@@ -6166,6 +6169,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.2",
+]
+
+[[package]]
 name = "windows-targets"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -6198,9 +6210,9 @@ dependencies = [
 
 [[package]]
 name = "windows-targets"
-version = "0.53.0"
+version = "0.53.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
+checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
 dependencies = [
  "windows_aarch64_gnullvm 0.53.0",
  "windows_aarch64_msvc 0.53.0",
@@ -6370,9 +6382,9 @@ dependencies = [
 
 [[package]]
 name = "winnow"
-version = "0.7.10"
+version = "0.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
+checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
 dependencies = [
  "memchr",
 ]
@@ -6394,9 +6406,9 @@ dependencies = [
 
 [[package]]
 name = "wit-component"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f550067740e223bfe6c4878998e81cdbe2529dd9a793dc49248dd6613394e8b"
+checksum = "5a8888169acf4c6c4db535beb405b570eedac13215d6821ca9bd03190f7f8b8c"
 dependencies = [
  "anyhow",
  "bitflags",
@@ -6405,17 +6417,17 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder 0.229.0",
+ "wasm-encoder 0.234.0",
  "wasm-metadata",
- "wasmparser 0.229.0",
+ "wasmparser 0.234.0",
  "wit-parser",
 ]
 
 [[package]]
 name = "wit-parser"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "459c6ba62bf511d6b5f2a845a2a736822e38059c1cfa0b644b467bbbfae4efa6"
+checksum = "465492df47d8dcc015a3b7f241aed8ea03688fee7c5e04162285c5b1a3539c8b"
 dependencies = [
  "anyhow",
  "id-arena",
@@ -6426,7 +6438,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser 0.229.0",
+ "wasmparser 0.234.0",
 ]
 
 [[package]]
@@ -6505,7 +6517,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
  "synstructure",
 ]
 
@@ -6517,7 +6529,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
  "synstructure",
 ]
 
@@ -6538,7 +6550,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -6558,7 +6570,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
  "synstructure",
 ]
 
@@ -6603,7 +6615,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
 
 [[package]]
@@ -6614,5 +6626,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.103",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index c4d2a06f4cb..6d3425f4115 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -60,7 +60,7 @@ exclude = [
   "obj",
 ]
 
-[profile.release.package.rustc-rayon-core]
+[profile.release.package.rustc_thread_pool]
 # The rustc fork of Rayon has deadlock detection code which intermittently
 # causes overflows in the CI (see https://github.com/rust-lang/rust/issues/90227)
 # so we turn overflow checks off for now.
@@ -89,3 +89,8 @@ codegen-units = 1
 # FIXME: LTO cannot be enabled for binaries in a workspace
 # <https://github.com/rust-lang/cargo/issues/9330>
 # lto = true
+
+# If you want to use a crate with local modifications, you can set a path or git dependency here.
+# For git dependencies, also add your source to ALLOWED_SOURCES in src/tools/tidy/src/extdeps.rs.
+#[patch.crates-io]
+
diff --git a/RELEASES.md b/RELEASES.md
index 3c72cb1de0a..8a6bb214d2d 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,3 +1,103 @@
+Version 1.88.0 (2025-06-26)
+==========================
+
+<a id="1.88.0-Language"></a>
+
+Language
+--------
+- [Stabilize `#![feature(let_chains)]` in the 2024 edition.](https://github.com/rust-lang/rust/pull/132833)
+  This feature allows `&&`-chaining `let` statements inside `if` and `while`, allowing intermixture with boolean expressions. The patterns inside the `let` sub-expressions can be irrefutable or refutable.
+- [Stabilize `#![feature(naked_functions)]`.](https://github.com/rust-lang/rust/pull/134213)
+  Naked functions allow writing functions with no compiler-generated epilogue and prologue, allowing full control over the generated assembly for a particular function.
+- [Stabilize `#![feature(cfg_boolean_literals)]`.](https://github.com/rust-lang/rust/pull/138632)
+  This allows using boolean literals as `cfg` predicates, e.g. `#[cfg(true)]` and `#[cfg(false)]`.
+- [Fully de-stabilize the `#[bench]` attribute](https://github.com/rust-lang/rust/pull/134273). Usage of `#[bench]` without `#![feature(custom_test_frameworks)]` already triggered a deny-by-default future-incompatibility lint since Rust 1.77, but will now become a hard error.
+- [Add warn-by-default `dangerous_implicit_autorefs` lint against implicit autoref of raw pointer dereference.](https://github.com/rust-lang/rust/pull/123239)
+  The lint [will be bumped to deny-by-default](https://github.com/rust-lang/rust/pull/141661) in the next version of Rust.
+- [Add `invalid_null_arguments` lint to prevent invalid usage of null pointers.](https://github.com/rust-lang/rust/pull/119220)
+  This lint is uplifted from `clippy::invalid_null_ptr_usage`.
+- [Change trait impl candidate preference for builtin impls and trivial where-clauses.](https://github.com/rust-lang/rust/pull/138176)
+- [Check types of generic const parameter defaults](https://github.com/rust-lang/rust/pull/139646)
+
+<a id="1.88.0-Compiler"></a>
+
+Compiler
+--------
+- [Stabilize `-Cdwarf-version` for selecting the version of DWARF debug information to generate.](https://github.com/rust-lang/rust/pull/136926)
+
+
+<a id="1.88.0-Platform-Support"></a>
+
+Platform Support
+----------------
+- [Demote `i686-pc-windows-gnu` to Tier 2.](https://blog.rust-lang.org/2025/05/26/demoting-i686-pc-windows-gnu/)
+
+
+Refer to Rust's [platform support page][platform-support-doc]
+for more information on Rust's tiered platform support.
+
+[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html
+
+<a id="1.88.0-Libraries"></a>
+
+Libraries
+---------
+- [Remove backticks from `#[should_panic]` test failure message.](https://github.com/rust-lang/rust/pull/136160)
+- [Guarantee that `[T; N]::from_fn` is generated in order of increasing indices.](https://github.com/rust-lang/rust/pull/139099), for those passing it a stateful closure.
+- [The libtest flag `--nocapture` is deprecated in favor of the more consistent `--no-capture` flag.](https://github.com/rust-lang/rust/pull/139224)
+- [Guarantee that `{float}::NAN` is a quiet NaN.](https://github.com/rust-lang/rust/pull/139483)
+
+
+<a id="1.88.0-Stabilized-APIs"></a>
+
+Stabilized APIs
+---------------
+
+- [`Cell::update`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#method.update)
+- [`impl Default for *const T`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#impl-Default-for-*const+T)
+- [`impl Default for *mut T`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#impl-Default-for-*mut+T)
+- [`HashMap::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html#method.extract_if)
+- [`HashSet::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.HashSet.html#method.extract_if)
+- [`proc_macro::Span::line`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.line)
+- [`proc_macro::Span::column`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.column)
+- [`proc_macro::Span::start`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.start)
+- [`proc_macro::Span::end`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.end)
+- [`proc_macro::Span::file`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.file)
+- [`proc_macro::Span::local_file`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.local_file)
+
+These previously stable APIs are now stable in const contexts:
+
+- [`NonNull<T>::replace`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.replace)
+- [`<*mut T>::replace`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.replace)
+- [`std::ptr::swap_nonoverlapping`](https://github.com/rust-lang/rust/pull/137280)
+- [`Cell::{replace, get, get_mut, from_mut, as_slice_of_cells}`](https://github.com/rust-lang/rust/pull/137928)
+
+
+<a id="1.88.0-Cargo"></a>
+
+Cargo
+-----
+- [Stabilize automatic garbage collection.](https://github.com/rust-lang/cargo/pull/14287/)
+- [use `zlib-rs` for gzip compression in rust code](https://github.com/rust-lang/cargo/pull/15417/)
+
+<a id="1.88.0-Rustdoc"></a>
+
+Rustdoc
+-----
+- [Doctests can be ignored based on target names using `ignore-*` attributes.](https://github.com/rust-lang/rust/pull/137096)
+- [Stabilize the `--test-runtool` and `--test-runtool-arg` CLI options to specify a program (like qemu) and its arguments to run a doctest.](https://github.com/rust-lang/rust/pull/137096)
+
+<a id="1.88.0-Compatibility-Notes"></a>
+
+Compatibility Notes
+-------------------
+- [Finish changing the internal representation of pasted tokens](https://github.com/rust-lang/rust/pull/124141). Certain invalid declarative macros that were previously accepted in obscure circumstances are now correctly rejected by the compiler. Use of a `tt` fragment specifier can often fix these macros.
+- [Fully de-stabilize the `#[bench]` attribute](https://github.com/rust-lang/rust/pull/134273). Usage of `#[bench]` without `#![feature(custom_test_frameworks)]` already triggered a deny-by-default future-incompatibility lint since Rust 1.77, but will now become a hard error.
+- [Fix borrow checking some always-true patterns.](https://github.com/rust-lang/rust/pull/139042)
+  The borrow checker was overly permissive in some cases, allowing programs that shouldn't have compiled.
+- [Update the minimum external LLVM to 19.](https://github.com/rust-lang/rust/pull/139275)
+- [Make it a hard error to use a vector type with a non-Rust ABI without enabling the required target feature.](https://github.com/rust-lang/rust/pull/139309)
+
 Version 1.87.0 (2025-05-15)
 ==========================
 
diff --git a/bootstrap.example.toml b/bootstrap.example.toml
index 19cf360b0fb..cc1ea796a02 100644
--- a/bootstrap.example.toml
+++ b/bootstrap.example.toml
@@ -8,6 +8,14 @@
 # `bootstrap.toml` in the current directory of a build for build configuration, but
 # a custom configuration file can also be specified with `--config` to the build
 # system.
+#
+# Note that the following are equivelent, for more details see <https://toml.io/en/v1.0.0>.
+#
+#     build.verbose = 1
+#
+#     [build]
+#     verbose = 1
+
 
 # =============================================================================
 # Global Settings
@@ -44,7 +52,6 @@
 # =============================================================================
 # Tweaking how LLVM is compiled
 # =============================================================================
-[llvm]
 
 # Whether to use Rust CI built LLVM instead of locally building it.
 #
@@ -62,50 +69,50 @@
 #
 # Note that many of the LLVM options are not currently supported for
 # downloading. Currently only the "assertions" option can be toggled.
-#download-ci-llvm = true
+#llvm.download-ci-llvm = true
 
 # Indicates whether the LLVM build is a Release or Debug build
-#optimize = true
+#llvm.optimize = true
 
 # Indicates whether LLVM should be built with ThinLTO. Note that this will
 # only succeed if you use clang, lld, llvm-ar, and llvm-ranlib in your C/C++
 # toolchain (see the `cc`, `cxx`, `linker`, `ar`, and `ranlib` options below).
 # More info at: https://clang.llvm.org/docs/ThinLTO.html#clang-bootstrap
-#thin-lto = false
+#llvm.thin-lto = false
 
 # Indicates whether an LLVM Release build should include debug info
-#release-debuginfo = false
+#llvm.release-debuginfo = false
 
 # Indicates whether the LLVM assertions are enabled or not
 # NOTE: When assertions are disabled, bugs in the integration between rustc and LLVM can lead to
 # unsoundness (segfaults, etc.) in the rustc process itself, not just in the generated code.
-#assertions = false
+#llvm.assertions = false
 
 # Indicates whether the LLVM testsuite is enabled in the build or not. Does
 # not execute the tests as part of the build as part of x.py build et al,
 # just makes it possible to do `ninja check-llvm` in the staged LLVM build
 # directory when doing LLVM development as part of Rust development.
-#tests = false
+#llvm.tests = false
 
 # Indicates whether the LLVM plugin is enabled or not
-#plugins = false
+#llvm.plugins = false
 
 # Whether to build Enzyme as AutoDiff backend.
-#enzyme = false
+#llvm.enzyme = false
 
 # Whether to build LLVM with support for it's gpu offload runtime.
-#offload = false
+#llvm.offload = false
 
 # When true, link libstdc++ statically into the rustc_llvm.
 # This is useful if you don't want to use the dynamic version of that
 # library provided by LLVM.
-#static-libstdcpp = false
+#llvm.static-libstdcpp = false
 
 # Enable LLVM to use zstd for compression.
-#libzstd = false
+#llvm.libzstd = false
 
 # Whether to use Ninja to build LLVM. This runs much faster than make.
-#ninja = true
+#llvm.ninja = true
 
 # LLVM targets to build support for.
 # Note: this is NOT related to Rust compilation targets. However, as Rust is
@@ -113,13 +120,13 @@
 # the resulting rustc being unable to compile for the disabled architectures.
 #
 # To add support for new targets, see https://rustc-dev-guide.rust-lang.org/building/new-target.html.
-#targets = "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;Sparc;SystemZ;WebAssembly;X86"
+#llvm.targets = "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;Sparc;SystemZ;WebAssembly;X86"
 
 # LLVM experimental targets to build support for. These targets are specified in
 # the same format as above, but since these targets are experimental, they are
 # not built by default and the experimental Rust compilation targets that depend
 # on them will not work unless the user opts in to building them.
-#experimental-targets = "AVR;M68k;CSKY"
+#llvm.experimental-targets = "AVR;M68k;CSKY"
 
 # Cap the number of parallel linker invocations when compiling LLVM.
 # This can be useful when building LLVM with debug info, which significantly
@@ -127,86 +134,84 @@
 # each linker process.
 # If set to 0, linker invocations are treated like any other job and
 # controlled by bootstrap's -j parameter.
-#link-jobs = 0
+#llvm.link-jobs = 0
 
 # Whether to build LLVM as a dynamically linked library (as opposed to statically linked).
 # Under the hood, this passes `--shared` to llvm-config.
 # NOTE: To avoid performing LTO multiple times, we suggest setting this to `true` when `thin-lto` is enabled.
-#link-shared = llvm.thin-lto
+#llvm.link-shared = llvm.thin-lto
 
 # When building llvm, this configures what is being appended to the version.
 # To use LLVM version as is, provide an empty string.
-#version-suffix = if rust.channel == "dev" { "-rust-dev" } else { "-rust-$version-$channel" }
+#llvm.version-suffix = if rust.channel == "dev" { "-rust-dev" } else { "-rust-$version-$channel" }
 
 # On MSVC you can compile LLVM with clang-cl, but the test suite doesn't pass
 # with clang-cl, so this is special in that it only compiles LLVM with clang-cl.
 # Note that this takes a /path/to/clang-cl, not a boolean.
-#clang-cl = cc
+#llvm.clang-cl = cc
 
 # Pass extra compiler and linker flags to the LLVM CMake build.
-#cflags = ""
-#cxxflags = ""
-#ldflags = ""
+#llvm.cflags = ""
+#llvm.cxxflags = ""
+#llvm.ldflags = ""
 
 # Use libc++ when building LLVM instead of libstdc++. This is the default on
 # platforms already use libc++ as the default C++ library, but this option
 # allows you to use libc++ even on platforms when it's not. You need to ensure
 # that your host compiler ships with libc++.
-#use-libcxx = false
+#llvm.use-libcxx = false
 
 # The value specified here will be passed as `-DLLVM_USE_LINKER` to CMake.
-#use-linker = <none> (path)
+#llvm.use-linker = <none> (path)
 
 # Whether or not to specify `-DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=YES`
-#allow-old-toolchain = false
+#llvm.allow-old-toolchain = false
 
 # Whether to include the Polly optimizer.
-#polly = false
+#llvm.polly = false
 
 # Whether to build the clang compiler.
-#clang = false
+#llvm.clang = false
 
 # Whether to enable llvm compilation warnings.
-#enable-warnings = false
+#llvm.enable-warnings = false
 
 # Custom CMake defines to set when building LLVM.
-#build-config = {}
+#llvm.build-config = {}
 
 # =============================================================================
 # Tweaking how GCC is compiled
 # =============================================================================
-[gcc]
 # Download GCC from CI instead of building it locally.
 # Note that this will attempt to download GCC even if there are local
 # modifications to the `src/gcc` submodule.
 # Currently, this is only supported for the `x86_64-unknown-linux-gnu` target.
-#download-ci-gcc = false
+#gcc.download-ci-gcc = false
 
 # =============================================================================
 # General build configuration options
 # =============================================================================
-[build]
 
 # The default stage to use for the `check` subcommand
-#check-stage = 0
+#build.check-stage = 0
 
 # The default stage to use for the `doc` subcommand
-#doc-stage = 0
+#build.doc-stage = 0
 
 # The default stage to use for the `build` subcommand
-#build-stage = 1
+#build.build-stage = 1
 
 # The default stage to use for the `test` subcommand
-#test-stage = 1
+#build.test-stage = 1
 
 # The default stage to use for the `dist` subcommand
-#dist-stage = 2
+#build.dist-stage = 2
 
 # The default stage to use for the `install` subcommand
-#install-stage = 2
+#build.install-stage = 2
 
 # The default stage to use for the `bench` subcommand
-#bench-stage = 2
+#build.bench-stage = 2
 
 # A descriptive string to be appended to version output (e.g., `rustc --version`),
 # which is also used in places like debuginfo `DW_AT_producer`. This may be useful for
@@ -217,7 +222,7 @@
 # upstream Rust you need to set this to "". However, note that if you set this to "" but
 # are not actually compatible -- for example if you've backported patches that change
 # behavior -- this may lead to miscompilations or other bugs.
-#description = ""
+#build.description = ""
 
 # Build triple for the pre-compiled snapshot compiler. If `rustc` is set, this must match its host
 # triple (see `rustc --version --verbose`; cross-compiling the rust build system itself is NOT
@@ -229,14 +234,14 @@
 # Otherwise, `x.py` will try to infer it from the output of `uname`.
 # If `uname` is not found in PATH, we assume this is `x86_64-pc-windows-msvc`.
 # This may be changed in the future.
-#build = "x86_64-unknown-linux-gnu" (as an example)
+#build.build = "x86_64-unknown-linux-gnu" (as an example)
 
 # Which triples to produce a compiler toolchain for. Each of these triples will be bootstrapped from
 # the build triple themselves. In other words, this is the list of triples for which to build a
 # compiler that can RUN on that triple.
 #
 # Defaults to just the `build` triple.
-#host = [build.build] (list of triples)
+#build.host = [build.build] (list of triples)
 
 # Which triples to build libraries (core/alloc/std/test/proc_macro) for. Each of these triples will
 # be bootstrapped from the build triple themselves. In other words, this is the list of triples for
@@ -245,32 +250,32 @@
 # Defaults to `host`. If you set this explicitly, you likely want to add all
 # host triples to this list as well in order for those host toolchains to be
 # able to compile programs for their native target.
-#target = build.host (list of triples)
+#build.target = build.host (list of triples)
 
 # Use this directory to store build artifacts. Paths are relative to the current directory, not to
 # the root of the repository.
-#build-dir = "build"
+#build.build-dir = "build"
 
 # Instead of downloading the src/stage0 version of Cargo specified, use
 # this Cargo binary instead to build all Rust code
 # If you set this, you likely want to set `rustc` as well.
-#cargo = "/path/to/cargo"
+#build.cargo = "/path/to/cargo"
 
 # Instead of downloading the src/stage0 version of the compiler
 # specified, use this rustc binary instead as the stage0 snapshot compiler.
 # If you set this, you likely want to set `cargo` as well.
-#rustc = "/path/to/rustc"
+#build.rustc = "/path/to/rustc"
 
 # Instead of downloading the src/stage0 version of rustfmt specified,
 # use this rustfmt binary instead as the stage0 snapshot rustfmt.
-#rustfmt = "/path/to/rustfmt"
+#build.rustfmt = "/path/to/rustfmt"
 
 # Instead of downloading the src/stage0 version of cargo-clippy specified,
 # use this cargo-clippy binary instead as the stage0 snapshot cargo-clippy.
 #
 # Note that this option should be used with the same toolchain as the `rustc` option above.
 # Otherwise, clippy is likely to fail due to a toolchain conflict.
-#cargo-clippy = "/path/to/cargo-clippy"
+#build.cargo-clippy = "/path/to/cargo-clippy"
 
 # Whether to build documentation by default. If false, rustdoc and
 # friends will still be compiled but they will not be used to generate any
@@ -278,47 +283,47 @@
 #
 # You can still build documentation when this is disabled by explicitly passing paths,
 # e.g. `x doc library`.
-#docs = true
+#build.docs = true
 
 # Flag to specify whether CSS, JavaScript, and HTML are minified when
 # docs are generated. JSON is always minified, because it's enormous,
 # and generated in already-minified form from the beginning.
-#docs-minification = true
+#build.docs-minification = true
 
 # Flag to specify whether private items should be included in the library docs.
-#library-docs-private-items = false
+#build.library-docs-private-items = false
 
 # Indicate whether to build compiler documentation by default.
 # You can still build documentation when this is disabled by explicitly passing a path: `x doc compiler`.
-#compiler-docs = false
+#build.compiler-docs = false
 
 # Indicate whether git submodules are managed and updated automatically.
-#submodules = true
+#build.submodules = true
 
 # The path to (or name of) the GDB executable to use. This is only used for
 # executing the debuginfo test suite.
-#gdb = "gdb"
+#build.gdb = "gdb"
 
 # The path to (or name of) the LLDB executable to use. This is only used for
 # executing the debuginfo test suite.
-#lldb = "lldb"
+#build.lldb = "lldb"
 
 # The node.js executable to use. Note that this is only used for the emscripten
 # target when running tests, otherwise this can be omitted.
-#nodejs = "node"
+#build.nodejs = "node"
 
 # The npm executable to use. Note that this is used for rustdoc-gui tests,
 # otherwise this can be omitted.
 #
 # Under Windows this should be `npm.cmd` or path to it (verified on nodejs v18.06), or
 # error will be emitted.
-#npm = "npm"
+#build.npm = "npm"
 
 # Python interpreter to use for various tasks throughout the build, notably
 # rustdoc tests, the lldb python interpreter, and some dist bits and pieces.
 #
 # Defaults to the Python interpreter used to execute x.py.
-#python = "python"
+#build.python = "python"
 
 # The path to the REUSE executable to use. Note that REUSE is not required in
 # most cases, as our tooling relies on a cached (and shrunk) copy of the
@@ -328,17 +333,17 @@
 # repository to change, and the cached copy has to be regenerated.
 #
 # Defaults to the "reuse" command in the system path.
-#reuse = "reuse"
+#build.reuse = "reuse"
 
 # Force Cargo to check that Cargo.lock describes the precise dependency
 # set that all the Cargo.toml files create, instead of updating it.
-#locked-deps = false
+#build.locked-deps = false
 
 # Indicate whether the vendored sources are used for Rust dependencies or not.
 #
 # Vendoring requires additional setup. We recommend using the pre-generated source tarballs if you
 # want to use vendoring. See https://forge.rust-lang.org/infra/other-installation-methods.html#source-code.
-#vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false }
+#build.vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false }
 
 # Typically the build system will build the Rust compiler twice. The second
 # compiler, however, will simply use its own libraries to link against. If you
@@ -346,11 +351,11 @@
 # then you can set this option to true.
 #
 # This is only useful for verifying that rustc generates reproducible builds.
-#full-bootstrap = false
+#build.full-bootstrap = false
 
 # Set the bootstrap/download cache path. It is useful when building rust
 # repeatedly in a CI environment.
-#bootstrap-cache-path = /path/to/shared/cache
+#build.bootstrap-cache-path = /path/to/shared/cache
 
 # Enable a build of the extended Rust tool set which is not only the compiler
 # but also tools such as Cargo. This will also produce "combined installers"
@@ -359,7 +364,7 @@
 # which tools should be built if `extended = true`.
 #
 # This is disabled by default.
-#extended = false
+#build.extended = false
 
 # Set of tools to be included in the installation.
 #
@@ -368,7 +373,7 @@
 # If `extended = true`, they are all included.
 #
 # If any enabled tool fails to build, the installation fails.
-#tools = [
+#build.tools = [
 #    "cargo",
 #    "clippy",
 #    "rustdoc",
@@ -388,17 +393,17 @@
 #
 # The default value for the `features` array is `[]`. However, please note that other flags in
 # `bootstrap.toml` might influence the features enabled for some tools.
-#tool.TOOL_NAME.features = [FEATURE1, FEATURE2]
+#build.tool.TOOL_NAME.features = [FEATURE1, FEATURE2]
 
 # Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose, 3 == print environment variables on each rustc invocation
-#verbose = 0
+#build.verbose = 0
 
 # Build the sanitizer runtimes
-#sanitizers = false
+#build.sanitizers = false
 
 # Build the profiler runtime (required when compiling with options that depend
 # on this runtime, such as `-C profile-generate` or `-C instrument-coverage`).
-#profiler = false
+#build.profiler = false
 
 # Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics.
 # Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt`
@@ -406,102 +411,100 @@
 #
 # Setting this to `false` generates slower code, but removes the requirement for a C toolchain in
 # order to run `x check`.
-#optimized-compiler-builtins = if rust.channel == "dev" { false } else { true }
+#build.optimized-compiler-builtins = if rust.channel == "dev" { false } else { true }
 
 # Indicates whether the native libraries linked into Cargo will be statically
 # linked or not.
-#cargo-native-static = false
+#build.cargo-native-static = false
 
 # Run the build with low priority, by setting the process group's "nice" value
 # to +10 on Unix platforms, and by using a "low priority" job object on Windows.
-#low-priority = false
+#build.low-priority = false
 
 # Arguments passed to the `./configure` script, used during distcheck. You
 # probably won't fill this in but rather it's filled in by the `./configure`
 # script. Useful for debugging.
-#configure-args = []
+#build.configure-args = []
 
 # Indicates that a local rebuild is occurring instead of a full bootstrap,
 # essentially skipping stage0 as the local compiler is recompiling itself again.
 # Useful for modifying only the stage2 compiler without having to pass `--keep-stage 0` each time.
-#local-rebuild = false
+#build.local-rebuild = false
 
 # Print out how long each bootstrap step took (mostly intended for CI and
 # tracking over time)
-#print-step-timings = false
+#build.print-step-timings = false
 
 # Print out resource usage data for each bootstrap step, as defined by the Unix
 # struct rusage. (Note that this setting is completely unstable: the data it
 # captures, what platforms it supports, the format of its associated output, and
 # this setting's very existence, are all subject to change.)
-#print-step-rusage = false
+#build.print-step-rusage = false
 
 # Always patch binaries for usage with Nix toolchains. If `true` then binaries
 # will be patched unconditionally. If `false` or unset, binaries will be patched
 # only if the current distribution is NixOS. This option is useful when using
 # a Nix toolchain on non-NixOS distributions.
-#patch-binaries-for-nix = false
+#build.patch-binaries-for-nix = false
 
 # Collect information and statistics about the current build, and write it to
 # disk. Enabling this has no impact on the resulting build output. The
 # schema of the file generated by the build metrics feature is unstable, and
 # this is not intended to be used during local development.
-#metrics = false
+#build.metrics = false
 
 # Specify the location of the Android NDK. Used when targeting Android.
-#android-ndk = "/path/to/android-ndk-r26d"
+#build.android-ndk = "/path/to/android-ndk-r26d"
 
 # Number of parallel jobs to be used for building and testing. If set to `0` or
 # omitted, it will be automatically determined. This is the `-j`/`--jobs` flag
 # passed to cargo invocations.
-#jobs = 0
+#build.jobs = 0
 
 # What custom diff tool to use for displaying compiletest tests.
-#compiletest-diff-tool = <none>
+#build.compiletest-diff-tool = <none>
 
 # Whether to use the precompiled stage0 libtest with compiletest.
-#compiletest-use-stage0-libtest = true
+#build.compiletest-use-stage0-libtest = true
 
 # Indicates whether ccache is used when building certain artifacts (e.g. LLVM).
 # Set to `true` to use the first `ccache` in PATH, or set an absolute path to use
 # a specific version.
-#ccache = false
+#build.ccache = false
 
 # List of paths to exclude from the build and test processes.
 # For example, exclude = ["tests/ui", "src/tools/tidy"].
-#exclude = []
+#build.exclude = []
 
 # =============================================================================
 # General install configuration options
 # =============================================================================
-[install]
 
 # Where to install the generated toolchain. Must be an absolute path.
-#prefix = "/usr/local"
+#install.prefix = "/usr/local"
 
 # Where to install system configuration files.
 # If this is a relative path, it will get installed in `prefix` above
-#sysconfdir = "/etc"
+#install.sysconfdir = "/etc"
 
 # Where to install documentation in `prefix` above
-#docdir = "share/doc/rust"
+#install.docdir = "share/doc/rust"
 
 # Where to install binaries in `prefix` above
-#bindir = "bin"
+#install.bindir = "bin"
 
 # Where to install libraries in `prefix` above
-#libdir = "lib"
+#install.libdir = "lib"
 
 # Where to install man pages in `prefix` above
-#mandir = "share/man"
+#install.mandir = "share/man"
 
 # Where to install data in `prefix` above
-#datadir = "share"
+#install.datadir = "share"
 
 # =============================================================================
 # Options for compiling Rust code itself
 # =============================================================================
-[rust]
 
 # Whether or not to optimize when compiling the compiler and standard library,
 # and what level of optimization to use.
@@ -517,7 +520,7 @@
 # 3 - All optimizations.
 # "s" - Optimize for binary size.
 # "z" - Optimize for binary size, but also turn off loop vectorization.
-#optimize = true
+#rust.optimize = true
 
 # Indicates that the build should be configured for debugging Rust. A
 # `debug`-enabled compiler and standard library will be somewhat
@@ -540,7 +543,7 @@
 #       "maximally debuggable" environment (notably libstd) takes
 #       hours to build.
 #
-#debug = false
+#rust.debug = false
 
 # Whether to download the stage 1 and 2 compilers from CI. This is useful if you
 # are working on tools, doc-comments, or library (you will be able to build the
@@ -553,37 +556,37 @@
 #
 # Set this to `true` to always download or `false` to always use the in-tree
 # compiler.
-#download-rustc = false
+#rust.download-rustc = false
 
 # Number of codegen units to use for each compiler invocation. A value of 0
 # means "the number of cores on this machine", and 1+ is passed through to the
 # compiler.
 #
 # Uses the rustc defaults: https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units
-#codegen-units = if incremental { 256 } else { 16 }
+#rust.codegen-units = if incremental { 256 } else { 16 }
 
 # Sets the number of codegen units to build the standard library with,
 # regardless of what the codegen-unit setting for the rest of the compiler is.
 # NOTE: building with anything other than 1 is known to occasionally have bugs.
-#codegen-units-std = codegen-units
+#rust.codegen-units-std = codegen-units
 
 # Whether or not debug assertions are enabled for the compiler and standard library.
 # These can help find bugs at the cost of a small runtime slowdown.
 #
 # Defaults to rust.debug value
-#debug-assertions = rust.debug (boolean)
+#rust.debug-assertions = rust.debug (boolean)
 
 # Whether or not debug assertions are enabled for the standard library.
 # Overrides the `debug-assertions` option, if defined.
 #
 # Defaults to rust.debug-assertions value
-#debug-assertions-std = rust.debug-assertions (boolean)
+#rust.debug-assertions-std = rust.debug-assertions (boolean)
 
 # Whether or not debug assertions are enabled for the tools built by bootstrap.
 # Overrides the `debug-assertions` option, if defined.
 #
 # Defaults to rust.debug-assertions value
-#debug-assertions-tools = rust.debug-assertions (boolean)
+#rust.debug-assertions-tools = rust.debug-assertions (boolean)
 
 # Whether or not to leave debug! and trace! calls in the rust binary.
 #
@@ -591,22 +594,22 @@
 #
 # If you see a message from `tracing` saying "some trace filter directives would enable traces that
 # are disabled statically" because `max_level_info` is enabled, set this value to `true`.
-#debug-logging = rust.debug-assertions (boolean)
+#rust.debug-logging = rust.debug-assertions (boolean)
 
 # Whether or not to build rustc, tools and the libraries with randomized type layout
-#randomize-layout = false
+#rust.randomize-layout = false
 
 # Whether or not overflow checks are enabled for the compiler and standard
 # library.
 #
 # Defaults to rust.debug value
-#overflow-checks = rust.debug (boolean)
+#rust.overflow-checks = rust.debug (boolean)
 
 # Whether or not overflow checks are enabled for the standard library.
 # Overrides the `overflow-checks` option, if defined.
 #
 # Defaults to rust.overflow-checks value
-#overflow-checks-std = rust.overflow-checks (boolean)
+#rust.overflow-checks-std = rust.overflow-checks (boolean)
 
 # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`.
 # See https://doc.rust-lang.org/rustc/codegen-options/index.html#debuginfo for available options.
@@ -617,20 +620,20 @@
 #
 # Note that debuginfo-level = 2 generates several gigabytes of debuginfo
 # and will slow down the linking process significantly.
-#debuginfo-level = if rust.debug { 1 } else { 0 }
+#rust.debuginfo-level = if rust.debug { 1 } else { 0 }
 
 # Debuginfo level for the compiler.
-#debuginfo-level-rustc = rust.debuginfo-level
+#rust.debuginfo-level-rustc = rust.debuginfo-level
 
 # Debuginfo level for the standard library.
-#debuginfo-level-std = rust.debuginfo-level
+#rust.debuginfo-level-std = rust.debuginfo-level
 
 # Debuginfo level for the tools.
-#debuginfo-level-tools = rust.debuginfo-level
+#rust.debuginfo-level-tools = rust.debuginfo-level
 
 # Debuginfo level for the test suites run with compiletest.
 # FIXME(#61117): Some tests fail when this option is enabled.
-#debuginfo-level-tests = 0
+#rust.debuginfo-level-tests = 0
 
 # Should rustc and the standard library be built with split debuginfo? Default
 # is platform dependent.
@@ -640,13 +643,13 @@
 # The value specified here is only used when targeting the `build.build` triple,
 # and is overridden by `target.<triple>.split-debuginfo` if specified.
 #
-#split-debuginfo = see target.<triple>.split-debuginfo
+#rust.split-debuginfo = see target.<triple>.split-debuginfo
 
 # Whether or not `panic!`s generate backtraces (RUST_BACKTRACE)
-#backtrace = true
+#rust.backtrace = true
 
 # Whether to always use incremental compilation when building rustc
-#incremental = false
+#rust.incremental = false
 
 # The default linker that will be hard-coded into the generated
 # compiler for targets that don't specify a default linker explicitly
@@ -656,7 +659,7 @@
 # setting.
 #
 # See https://doc.rust-lang.org/rustc/codegen-options/index.html#linker for more information.
-#default-linker = <none> (path)
+#rust.default-linker = <none> (path)
 
 # The "channel" for the Rust build to produce. The stable/beta channels only
 # allow using stable features, whereas the nightly and dev channels allow using
@@ -665,7 +668,7 @@
 # You can set the channel to "auto-detect" to load the channel name from `src/ci/channel`.
 #
 # If using tarball sources, default value is "auto-detect", otherwise, it's "dev".
-#channel = if "is a tarball source" { "auto-detect" } else { "dev" }
+#rust.channel = if "is a tarball source" { "auto-detect" } else { "dev" }
 
 # The root location of the musl installation directory. The library directory
 # will also need to contain libunwind.a for an unwinding implementation. Note
@@ -673,65 +676,65 @@
 # linked binaries.
 #
 # Defaults to /usr on musl hosts. Has no default otherwise.
-#musl-root = <platform specific> (path)
+#rust.musl-root = <platform specific> (path)
 
 # By default the `rustc` executable is built with `-Wl,-rpath` flags on Unix
 # platforms to ensure that the compiler is usable by default from the build
 # directory (as it links to a number of dynamic libraries). This may not be
 # desired in distributions, for example.
-#rpath = true
+#rust.rpath = true
 
 # Indicates whether symbols should be stripped using `-Cstrip=symbols`.
-#strip = false
+#rust.strip = false
 
 # Forces frame pointers to be used with `-Cforce-frame-pointers`.
 # This can be helpful for profiling at a small performance cost.
-#frame-pointers = false
+#rust.frame-pointers = false
 
 # Indicates whether stack protectors should be used
 # via the unstable option `-Zstack-protector`.
 #
 # Valid options are : `none`(default),`basic`,`strong`, or `all`.
 # `strong` and `basic` options may be buggy and are not recommended, see rust-lang/rust#114903.
-#stack-protector = "none"
+#rust.stack-protector = "none"
 
 # Prints each test name as it is executed, to help debug issues in the test harness itself.
-#verbose-tests = if is_verbose { true } else { false }
+#rust.verbose-tests = if is_verbose { true } else { false }
 
 # Flag indicating whether tests are compiled with optimizations (the -O flag).
-#optimize-tests = true
+#rust.optimize-tests = true
 
 # Flag indicating whether codegen tests will be run or not. If you get an error
 # saying that the FileCheck executable is missing, you may want to disable this.
 # Also see the target's llvm-filecheck option.
-#codegen-tests = true
+#rust.codegen-tests = true
 
 # Flag indicating whether git info will be retrieved from .git automatically.
 # Having the git information can cause a lot of rebuilds during development.
-#omit-git-hash = if rust.channel == "dev" { true } else { false }
+#rust.omit-git-hash = if rust.channel == "dev" { true } else { false }
 
 # Whether to create a source tarball by default when running `x dist`.
 #
 # You can still build a source tarball when this is disabled by explicitly passing `x dist rustc-src`.
-#dist-src = true
+#rust.dist-src = true
 
 # After building or testing an optional component (e.g. the nomicon or reference), append the
 # result (broken, compiling, testing) into this JSON file.
-#save-toolstates = <none> (path)
+#rust.save-toolstates = <none> (path)
 
 # This is an array of the codegen backends that will be compiled for the rustc
 # that's being compiled. The default is to only build the LLVM codegen backend,
 # and currently the only standard options supported are `"llvm"`, `"cranelift"`
 # and `"gcc"`. The first backend in this list will be used as default by rustc
 # when no explicit backend is specified.
-#codegen-backends = ["llvm"]
+#rust.codegen-backends = ["llvm"]
 
 # Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and
 # whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be
 # when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from
 # the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will
 # make this default to false.
-#lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true
+#rust.lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true
 
 # Indicates whether LLD will be used to link Rust crates during bootstrap on
 # supported platforms.
@@ -742,56 +745,56 @@
 # On MSVC, LLD will not be used if we're cross linking.
 #
 # Explicitly setting the linker for a target will override this option when targeting MSVC.
-#use-lld = false
+#rust.use-lld = false
 
 # Indicates whether some LLVM tools, like llvm-objdump, will be made available in the
 # sysroot.
-#llvm-tools = true
+#rust.llvm-tools = true
 
 # Indicates whether the `self-contained` llvm-bitcode-linker, will be made available
 # in the sysroot. It is required for running nvptx tests.
-#llvm-bitcode-linker = false
+#rust.llvm-bitcode-linker = false
 
 # Whether to deny warnings in crates
-#deny-warnings = true
+#rust.deny-warnings = true
 
 # Print backtrace on internal compiler errors during bootstrap
-#backtrace-on-ice = false
+#rust.backtrace-on-ice = false
 
 # Whether to verify generated LLVM IR
-#verify-llvm-ir = false
+#rust.verify-llvm-ir = false
 
 # Compile the compiler with a non-default ThinLTO import limit. This import
 # limit controls the maximum size of functions imported by ThinLTO. Decreasing
 # will make code compile faster at the expense of lower runtime performance.
-#thin-lto-import-instr-limit = if incremental { 10 } else { LLVM default (currently 100) }
+#rust.thin-lto-import-instr-limit = if incremental { 10 } else { LLVM default (currently 100) }
 
 # Map debuginfo paths to `/rust/$sha/...`.
 # Useful for reproducible builds. Generally only set for releases
-#remap-debuginfo = false
+#rust.remap-debuginfo = false
 
 # Link the compiler and LLVM against `jemalloc` instead of the default libc allocator.
 # This option is only tested on Linux and OSX. It can also be configured per-target in the
 # [target.<tuple>] section.
-#jemalloc = false
+#rust.jemalloc = false
 
 # Run tests in various test suites with the "nll compare mode" in addition to
 # running the tests in normal mode. Largely only used on CI and during local
 # development of NLL
-#test-compare-mode = false
+#rust.test-compare-mode = false
 
 # Global default for llvm-libunwind for all targets. See the target-specific
 # documentation for llvm-libunwind below. Note that the target-specific
 # option will override this if set.
-#llvm-libunwind = 'no'
+#rust.llvm-libunwind = 'no'
 
 # Enable Windows Control Flow Guard checks in the standard library.
 # This only applies from stage 1 onwards, and only for Windows targets.
-#control-flow-guard = false
+#rust.control-flow-guard = false
 
 # Enable Windows EHCont Guard checks in the standard library.
 # This only applies from stage 1 onwards, and only for Windows targets.
-#ehcont-guard = false
+#rust.ehcont-guard = false
 
 # Enable symbol-mangling-version v0. This can be helpful when profiling rustc,
 # as generics will be preserved in symbols (rather than erased into opaque T).
@@ -799,16 +802,16 @@
 # compiler and its tools and the legacy scheme will be used when compiling the
 # standard library.
 # If an explicit setting is given, it will be used for all parts of the codebase.
-#new-symbol-mangling = true|false (see comment)
+#rust.new-symbol-mangling = true|false (see comment)
 
 # Select LTO mode that will be used for compiling rustc. By default, thin local LTO
 # (LTO within a single crate) is used (like for any Rust crate). You can also select
 # "thin" or "fat" to apply Thin/Fat LTO to the `rustc_driver` dylib, or "off" to disable
 # LTO entirely.
-#lto = "thin-local"
+#rust.lto = "thin-local"
 
 # Build compiler with the optimization enabled and -Zvalidate-mir, currently only for `std`
-#validate-mir-opts = 3
+#rust.validate-mir-opts = 3
 
 # Configure `std` features used during bootstrap.
 #
@@ -822,7 +825,57 @@
 #
 # Since libstd also builds libcore and liballoc as dependencies and all their features are mirrored
 # as libstd features, this option can also be used to configure features such as optimize_for_size.
-#std-features = ["panic_unwind"]
+#rust.std-features = ["panic_unwind"]
+
+# =============================================================================
+# Distribution options
+#
+# These options are related to distribution, mostly for the Rust project itself.
+# You probably won't need to concern yourself with any of these options
+# =============================================================================
+
+# This is the folder of artifacts that the build system will sign. All files in
+# this directory will be signed with the default gpg key using the system `gpg`
+# binary. The `asc` and `sha256` files will all be output into the standard dist
+# output folder (currently `build/dist`)
+#
+# This folder should be populated ahead of time before the build system is
+# invoked.
+#dist.sign-folder = <none> (path)
+
+# The remote address that all artifacts will eventually be uploaded to. The
+# build system generates manifests which will point to these urls, and for the
+# manifests to be correct they'll have to have the right URLs encoded.
+#
+# Note that this address should not contain a trailing slash as file names will
+# be appended to it.
+#dist.upload-addr = <none> (URL)
+
+# Whether to build a plain source tarball to upload
+# We disable that on Windows not to override the one already uploaded on S3
+# as the one built on Windows will contain backslashes in paths causing problems
+# on linux
+#dist.src-tarball = true
+
+# List of compression formats to use when generating dist tarballs. The list of
+# formats is provided to rust-installer, which must support all of them.
+#
+# This list must be non-empty.
+#dist.compression-formats = ["gz", "xz"]
+
+# How much time should be spent compressing the tarballs. The better the
+# compression profile, the longer compression will take.
+#
+# Available options: fast, balanced, best
+#dist.compression-profile = "fast"
+
+# Copy the linker, DLLs, and various libraries from MinGW into the Rust toolchain.
+# Only applies when the host or target is pc-windows-gnu.
+#dist.include-mingw-linker = true
+
+# Whether to vendor dependencies for the dist tarball.
+#dist.vendor = if "is a tarball source" || "is a git repository" { true } else { false }
+
 
 # =============================================================================
 # Options for specific targets
@@ -973,53 +1026,3 @@
 # Link the compiler and LLVM against `jemalloc` instead of the default libc allocator.
 # This overrides the global `rust.jemalloc` option. See that option for more info.
 #jemalloc = rust.jemalloc (bool)
-
-# =============================================================================
-# Distribution options
-#
-# These options are related to distribution, mostly for the Rust project itself.
-# You probably won't need to concern yourself with any of these options
-# =============================================================================
-[dist]
-
-# This is the folder of artifacts that the build system will sign. All files in
-# this directory will be signed with the default gpg key using the system `gpg`
-# binary. The `asc` and `sha256` files will all be output into the standard dist
-# output folder (currently `build/dist`)
-#
-# This folder should be populated ahead of time before the build system is
-# invoked.
-#sign-folder = <none> (path)
-
-# The remote address that all artifacts will eventually be uploaded to. The
-# build system generates manifests which will point to these urls, and for the
-# manifests to be correct they'll have to have the right URLs encoded.
-#
-# Note that this address should not contain a trailing slash as file names will
-# be appended to it.
-#upload-addr = <none> (URL)
-
-# Whether to build a plain source tarball to upload
-# We disable that on Windows not to override the one already uploaded on S3
-# as the one built on Windows will contain backslashes in paths causing problems
-# on linux
-#src-tarball = true
-
-# List of compression formats to use when generating dist tarballs. The list of
-# formats is provided to rust-installer, which must support all of them.
-#
-# This list must be non-empty.
-#compression-formats = ["gz", "xz"]
-
-# How much time should be spent compressing the tarballs. The better the
-# compression profile, the longer compression will take.
-#
-# Available options: fast, balanced, best
-#compression-profile = "fast"
-
-# Copy the linker, DLLs, and various libraries from MinGW into the Rust toolchain.
-# Only applies when the host or target is pc-windows-gnu.
-#include-mingw-linker = true
-
-# Whether to vendor dependencies for the dist tarball.
-#vendor = if "is a tarball source" || "is a git repository" { true } else { false }
diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs
index 7c020be6761..13f9a04b286 100644
--- a/compiler/rustc_abi/src/canon_abi.rs
+++ b/compiler/rustc_abi/src/canon_abi.rs
@@ -63,8 +63,8 @@ impl fmt::Display for CanonAbi {
             CanonAbi::Custom => ExternAbi::Custom,
             CanonAbi::Arm(arm_call) => match arm_call {
                 ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
-                ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
-                ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry,
+                ArmCall::CCmseNonSecureCall => ExternAbi::CmseNonSecureCall,
+                ArmCall::CCmseNonSecureEntry => ExternAbi::CmseNonSecureEntry,
             },
             CanonAbi::GpuKernel => ExternAbi::GpuKernel,
             CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
index 7457ae1f033..29a3678abf3 100644
--- a/compiler/rustc_abi/src/extern_abi.rs
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -36,6 +36,10 @@ pub enum ExternAbi {
     /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
     RustCold,
 
+    /// An always-invalid ABI that's used to test "this ABI is not supported by this platform"
+    /// in a platform-agnostic way.
+    RustInvalid,
+
     /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
     /// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
     Unadjusted,
@@ -55,9 +59,9 @@ pub enum ExternAbi {
         unwind: bool,
     },
     /// extremely constrained barely-C ABI for TrustZone
-    CCmseNonSecureCall,
+    CmseNonSecureCall,
     /// extremely constrained barely-C ABI for TrustZone
-    CCmseNonSecureEntry,
+    CmseNonSecureEntry,
 
     /* gpu */
     /// An entry-point function called by the GPU's host
@@ -136,8 +140,6 @@ macro_rules! abi_impls {
 abi_impls! {
     ExternAbi = {
             C { unwind: false } =><= "C",
-            CCmseNonSecureCall =><= "C-cmse-nonsecure-call",
-            CCmseNonSecureEntry =><= "C-cmse-nonsecure-entry",
             C { unwind: true } =><= "C-unwind",
             Rust =><= "Rust",
             Aapcs { unwind: false } =><= "aapcs",
@@ -146,6 +148,8 @@ abi_impls! {
             AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
             Cdecl { unwind: false } =><= "cdecl",
             Cdecl { unwind: true } =><= "cdecl-unwind",
+            CmseNonSecureCall =><= "cmse-nonsecure-call",
+            CmseNonSecureEntry =><= "cmse-nonsecure-entry",
             Custom =><= "custom",
             EfiApi =><= "efiapi",
             Fastcall { unwind: false } =><= "fastcall",
@@ -157,6 +161,7 @@ abi_impls! {
             RiscvInterruptS =><= "riscv-interrupt-s",
             RustCall =><= "rust-call",
             RustCold =><= "rust-cold",
+            RustInvalid =><= "rust-invalid",
             Stdcall { unwind: false } =><= "stdcall",
             Stdcall { unwind: true } =><= "stdcall-unwind",
             System { unwind: false } =><= "system",
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 58a7fcae9f6..80b44e432ee 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -432,7 +432,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
             align = align.min(AbiAlign::new(pack));
         }
         // The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
-        // See documentation on `LayoutS::unadjusted_abi_align`.
+        // See documentation on `LayoutData::unadjusted_abi_align`.
         let unadjusted_abi_align = align.abi;
         if let Some(repr_align) = repr.align {
             align = align.max(AbiAlign::new(repr_align));
@@ -602,10 +602,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         dont_niche_optimize_enum: bool,
     ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
         // Until we've decided whether to use the tagged or
-        // niche filling LayoutS, we don't want to intern the
+        // niche filling LayoutData, we don't want to intern the
         // variant layouts, so we can't store them in the
-        // overall LayoutS. Store the overall LayoutS
-        // and the variant LayoutSs here until then.
+        // overall LayoutData. Store the overall LayoutData
+        // and the variant LayoutDatas here until then.
         struct TmpLayout<FieldIdx: Idx, VariantIdx: Idx> {
             layout: LayoutData<FieldIdx, VariantIdx>,
             variants: IndexVec<VariantIdx, LayoutData<FieldIdx, VariantIdx>>,
@@ -1214,7 +1214,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
 
                 match kind {
                     StructKind::AlwaysSized | StructKind::MaybeUnsized => {
-                        // Currently `LayoutS` only exposes a single niche so sorting is usually
+                        // Currently `LayoutData` only exposes a single niche so sorting is usually
                         // sufficient to get one niche into the preferred position. If it ever
                         // supported multiple niches then a more advanced pick-and-pack approach could
                         // provide better results. But even for the single-niche cache it's not
@@ -1333,7 +1333,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         }
 
         // The unadjusted ABI alignment does not include repr(align), but does include repr(pack).
-        // See documentation on `LayoutS::unadjusted_abi_align`.
+        // See documentation on `LayoutData::unadjusted_abi_align`.
         let unadjusted_abi_align = align.abi;
         if let Some(repr_align) = repr.align {
             align = align.max(AbiAlign::new(repr_align));
diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs
index bb880a58e52..18f0750aaa1 100644
--- a/compiler/rustc_abi/src/layout/ty.rs
+++ b/compiler/rustc_abi/src/layout/ty.rs
@@ -71,7 +71,7 @@ pub struct Layout<'a>(pub Interned<'a, LayoutData<FieldIdx, VariantIdx>>);
 
 impl<'a> fmt::Debug for Layout<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // See comment on `<LayoutS as Debug>::fmt` above.
+        // See comment on `<LayoutData as Debug>::fmt` above.
         self.0.0.fmt(f)
     }
 }
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 4268e68b2e4..6d729b6919a 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -1785,7 +1785,7 @@ where
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // This is how `Layout` used to print before it become
-        // `Interned<LayoutS>`. We print it like this to avoid having to update
+        // `Interned<LayoutData>`. We print it like this to avoid having to update
         // expected output in a lot of tests.
         let LayoutData {
             size,
diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml
index b2d3b90fc44..5de2e69072f 100644
--- a/compiler/rustc_ast/Cargo.toml
+++ b/compiler/rustc_ast/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2024"
 # tidy-alphabetical-start
 bitflags = "2.4.1"
 memchr = "2.7.4"
-rustc-literal-escaper = "0.0.2"
+rustc-literal-escaper = "0.0.4"
 rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_index = { path = "../rustc_index" }
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 11afd359e5a..ab8dac16026 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -904,6 +904,10 @@ pub enum BorrowKind {
     /// The resulting type is either `*const T` or `*mut T`
     /// where `T = typeof($expr)`.
     Raw,
+    /// A pinned borrow, `&pin const $expr` or `&pin mut $expr`.
+    /// The resulting type is either `Pin<&'a T>` or `Pin<&'a mut T>`
+    /// where `T = typeof($expr)` and `'a` is some lifetime.
+    Pin,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs
index 797ab297319..9d91f41d6c7 100644
--- a/compiler/rustc_ast/src/ast_traits.rs
+++ b/compiler/rustc_ast/src/ast_traits.rs
@@ -321,6 +321,13 @@ impl<Wrapped, Tag> AstNodeWrapper<Wrapped, Tag> {
     }
 }
 
+// FIXME: remove after `stmt_expr_attributes` is stabilized.
+impl<T, Tag> From<AstNodeWrapper<P<T>, Tag>> for AstNodeWrapper<T, Tag> {
+    fn from(value: AstNodeWrapper<P<T>, Tag>) -> Self {
+        AstNodeWrapper { wrapped: *value.wrapped, tag: value.tag }
+    }
+}
+
 impl<Wrapped: HasNodeId, Tag> HasNodeId for AstNodeWrapper<Wrapped, Tag> {
     fn node_id(&self) -> NodeId {
         self.wrapped.node_id()
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 621e3042b62..44865c493b3 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -206,12 +206,24 @@ impl AttributeExt for Attribute {
         }
     }
 
-    fn style(&self) -> AttrStyle {
-        self.style
+    fn doc_resolution_scope(&self) -> Option<AttrStyle> {
+        match &self.kind {
+            AttrKind::DocComment(..) => Some(self.style),
+            AttrKind::Normal(normal)
+                if normal.item.path == sym::doc && normal.item.value_str().is_some() =>
+            {
+                Some(self.style)
+            }
+            _ => None,
+        }
     }
 }
 
 impl Attribute {
+    pub fn style(&self) -> AttrStyle {
+        self.style
+    }
+
     pub fn may_have_doc_links(&self) -> bool {
         self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
     }
@@ -806,7 +818,14 @@ pub trait AttributeExt: Debug {
     /// * `#[doc(...)]` returns `None`.
     fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
 
-    fn style(&self) -> AttrStyle;
+    /// Returns outer or inner if this is a doc attribute or a sugared doc
+    /// comment, otherwise None.
+    ///
+    /// This is used in the case of doc comments on modules, to decide whether
+    /// to resolve intra-doc links against the symbols in scope within the
+    /// commented module (for inner doc) vs within its parent module (for outer
+    /// doc).
+    fn doc_resolution_scope(&self) -> Option<AttrStyle>;
 }
 
 // FIXME(fn_delegation): use function delegation instead of manually forwarding
@@ -881,8 +900,4 @@ impl Attribute {
     pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
         AttributeExt::doc_str_and_comment_kind(self)
     }
-
-    pub fn style(&self) -> AttrStyle {
-        AttributeExt::style(self)
-    }
 }
diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs
index b611ddea1d9..28d260419c5 100644
--- a/compiler/rustc_ast/src/format.rs
+++ b/compiler/rustc_ast/src/format.rs
@@ -50,6 +50,14 @@ pub struct FormatArgs {
     ///
     /// Generally only useful for lints that care about the raw bytes the user wrote.
     pub uncooked_fmt_str: (LitKind, Symbol),
+    /// Was the format literal written in the source?
+    /// - `format!("boo")` => true,
+    /// - `format!(concat!("b", "o", "o"))` => false,
+    /// - `format!(include_str!("boo.txt"))` => false,
+    ///
+    /// If it wasn't written in the source then we have to be careful with spans pointing into it
+    /// and suggestions about rewriting it.
+    pub is_source_literal: bool,
 }
 
 /// A piece of a format template string.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 07fbe8045fc..3eae19f4daa 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -13,7 +13,7 @@ use std::panic;
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_span::source_map::Spanned;
 use rustc_span::{Ident, Span};
-use smallvec::{Array, SmallVec, smallvec};
+use smallvec::{SmallVec, smallvec};
 use thin_vec::ThinVec;
 
 use crate::ast::*;
@@ -21,17 +21,6 @@ use crate::ptr::P;
 use crate::tokenstream::*;
 use crate::visit::{AssocCtxt, BoundKind, FnCtxt, VisitorResult, try_visit, visit_opt, walk_list};
 
-pub trait ExpectOne<A: Array> {
-    fn expect_one(self, err: &'static str) -> A::Item;
-}
-
-impl<A: Array> ExpectOne<A> for SmallVec<A> {
-    fn expect_one(self, err: &'static str) -> A::Item {
-        assert!(self.len() == 1, "{}", err);
-        self.into_iter().next().unwrap()
-    }
-}
-
 mod sealed {
     use rustc_ast_ir::visit::VisitorResult;
 
@@ -47,323 +36,6 @@ mod sealed {
 
 use sealed::MutVisitorResult;
 
-pub trait MutVisitor: Sized + MutVisitorResult<Result = ()> {
-    // Methods in this trait have one of three forms:
-    //
-    //   fn visit_t(&mut self, t: &mut T);                      // common
-    //   fn flat_map_t(&mut self, t: T) -> SmallVec<[T; 1]>;    // rare
-    //   fn filter_map_t(&mut self, t: T) -> Option<T>;         // rarest
-    //
-    // When writing these methods, it is better to use destructuring like this:
-    //
-    //   fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
-    //       visit_a(a);
-    //       visit_b(b);
-    //   }
-    //
-    // than to use field access like this:
-    //
-    //   fn visit_abc(&mut self, abc: &mut ABC) {
-    //       visit_a(&mut abc.a);
-    //       visit_b(&mut abc.b);
-    //       // ignore abc.c
-    //   }
-    //
-    // As well as being more concise, the former is explicit about which fields
-    // are skipped. Furthermore, if a new field is added, the destructuring
-    // version will cause a compile error, which is good. In comparison, the
-    // field access version will continue working and it would be easy to
-    // forget to add handling for it.
-
-    fn visit_crate(&mut self, c: &mut Crate) {
-        walk_crate(self, c)
-    }
-
-    fn visit_meta_list_item(&mut self, list_item: &mut MetaItemInner) {
-        walk_meta_list_item(self, list_item);
-    }
-
-    fn visit_meta_item(&mut self, meta_item: &mut MetaItem) {
-        walk_meta_item(self, meta_item);
-    }
-
-    fn visit_use_tree(&mut self, use_tree: &mut UseTree) {
-        walk_use_tree(self, use_tree);
-    }
-
-    fn visit_foreign_item(&mut self, ni: &mut ForeignItem) {
-        walk_item(self, ni);
-    }
-
-    fn flat_map_foreign_item(&mut self, ni: P<ForeignItem>) -> SmallVec<[P<ForeignItem>; 1]> {
-        walk_flat_map_foreign_item(self, ni)
-    }
-
-    fn visit_item(&mut self, i: &mut Item) {
-        walk_item(self, i);
-    }
-
-    fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
-        walk_flat_map_item(self, i)
-    }
-
-    fn visit_fn_header(&mut self, header: &mut FnHeader) {
-        walk_fn_header(self, header);
-    }
-
-    fn visit_field_def(&mut self, fd: &mut FieldDef) {
-        walk_field_def(self, fd);
-    }
-
-    fn flat_map_field_def(&mut self, fd: FieldDef) -> SmallVec<[FieldDef; 1]> {
-        walk_flat_map_field_def(self, fd)
-    }
-
-    fn visit_assoc_item(&mut self, i: &mut AssocItem, ctxt: AssocCtxt) {
-        walk_assoc_item(self, i, ctxt)
-    }
-
-    fn flat_map_assoc_item(
-        &mut self,
-        i: P<AssocItem>,
-        ctxt: AssocCtxt,
-    ) -> SmallVec<[P<AssocItem>; 1]> {
-        walk_flat_map_assoc_item(self, i, ctxt)
-    }
-
-    fn visit_contract(&mut self, c: &mut FnContract) {
-        walk_contract(self, c);
-    }
-
-    fn visit_fn_decl(&mut self, d: &mut FnDecl) {
-        walk_fn_decl(self, d);
-    }
-
-    /// `Span` and `NodeId` are mutated at the caller site.
-    fn visit_fn(&mut self, fk: FnKind<'_>, _: Span, _: NodeId) {
-        walk_fn(self, fk)
-    }
-
-    fn visit_coroutine_kind(&mut self, a: &mut CoroutineKind) {
-        walk_coroutine_kind(self, a);
-    }
-
-    fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
-        walk_closure_binder(self, b);
-    }
-
-    fn visit_block(&mut self, b: &mut Block) {
-        walk_block(self, b);
-    }
-
-    fn flat_map_stmt(&mut self, s: Stmt) -> SmallVec<[Stmt; 1]> {
-        walk_flat_map_stmt(self, s)
-    }
-
-    fn visit_arm(&mut self, arm: &mut Arm) {
-        walk_arm(self, arm);
-    }
-
-    fn flat_map_arm(&mut self, arm: Arm) -> SmallVec<[Arm; 1]> {
-        walk_flat_map_arm(self, arm)
-    }
-
-    fn visit_pat(&mut self, p: &mut Pat) {
-        walk_pat(self, p);
-    }
-
-    fn visit_anon_const(&mut self, c: &mut AnonConst) {
-        walk_anon_const(self, c);
-    }
-
-    fn visit_expr(&mut self, e: &mut Expr) {
-        walk_expr(self, e);
-    }
-
-    /// This method is a hack to workaround unstable of `stmt_expr_attributes`.
-    /// It can be removed once that feature is stabilized.
-    fn visit_method_receiver_expr(&mut self, ex: &mut P<Expr>) {
-        self.visit_expr(ex)
-    }
-
-    fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
-        walk_filter_map_expr(self, e)
-    }
-
-    fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
-        walk_generic_arg(self, arg);
-    }
-
-    fn visit_ty(&mut self, t: &mut Ty) {
-        walk_ty(self, t);
-    }
-
-    fn visit_ty_pat(&mut self, t: &mut TyPat) {
-        walk_ty_pat(self, t);
-    }
-
-    fn visit_lifetime(&mut self, l: &mut Lifetime) {
-        walk_lifetime(self, l);
-    }
-
-    fn visit_assoc_item_constraint(&mut self, c: &mut AssocItemConstraint) {
-        walk_assoc_item_constraint(self, c);
-    }
-
-    fn visit_foreign_mod(&mut self, nm: &mut ForeignMod) {
-        walk_foreign_mod(self, nm);
-    }
-
-    fn visit_variant(&mut self, v: &mut Variant) {
-        walk_variant(self, v);
-    }
-
-    fn flat_map_variant(&mut self, v: Variant) -> SmallVec<[Variant; 1]> {
-        walk_flat_map_variant(self, v)
-    }
-
-    fn visit_ident(&mut self, i: &mut Ident) {
-        self.visit_span(&mut i.span);
-    }
-
-    fn visit_path(&mut self, p: &mut Path) {
-        walk_path(self, p);
-    }
-
-    fn visit_path_segment(&mut self, p: &mut PathSegment) {
-        walk_path_segment(self, p)
-    }
-
-    fn visit_qself(&mut self, qs: &mut Option<P<QSelf>>) {
-        walk_qself(self, qs);
-    }
-
-    fn visit_generic_args(&mut self, p: &mut GenericArgs) {
-        walk_generic_args(self, p);
-    }
-
-    fn visit_local(&mut self, l: &mut Local) {
-        walk_local(self, l);
-    }
-
-    fn visit_mac_call(&mut self, mac: &mut MacCall) {
-        walk_mac(self, mac);
-    }
-
-    fn visit_macro_def(&mut self, def: &mut MacroDef) {
-        walk_macro_def(self, def);
-    }
-
-    fn visit_label(&mut self, label: &mut Label) {
-        walk_label(self, label);
-    }
-
-    fn visit_attribute(&mut self, at: &mut Attribute) {
-        walk_attribute(self, at);
-    }
-
-    fn visit_param(&mut self, param: &mut Param) {
-        walk_param(self, param);
-    }
-
-    fn flat_map_param(&mut self, param: Param) -> SmallVec<[Param; 1]> {
-        walk_flat_map_param(self, param)
-    }
-
-    fn visit_generics(&mut self, generics: &mut Generics) {
-        walk_generics(self, generics);
-    }
-
-    fn visit_trait_ref(&mut self, tr: &mut TraitRef) {
-        walk_trait_ref(self, tr);
-    }
-
-    fn visit_poly_trait_ref(&mut self, p: &mut PolyTraitRef) {
-        walk_poly_trait_ref(self, p);
-    }
-
-    fn visit_variant_data(&mut self, vdata: &mut VariantData) {
-        walk_variant_data(self, vdata);
-    }
-
-    fn visit_generic_param(&mut self, param: &mut GenericParam) {
-        walk_generic_param(self, param)
-    }
-
-    fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> {
-        walk_flat_map_generic_param(self, param)
-    }
-
-    fn visit_param_bound(&mut self, tpb: &mut GenericBound, _ctxt: BoundKind) {
-        walk_param_bound(self, tpb);
-    }
-
-    fn visit_precise_capturing_arg(&mut self, arg: &mut PreciseCapturingArg) {
-        walk_precise_capturing_arg(self, arg);
-    }
-
-    fn visit_expr_field(&mut self, f: &mut ExprField) {
-        walk_expr_field(self, f);
-    }
-
-    fn flat_map_expr_field(&mut self, f: ExprField) -> SmallVec<[ExprField; 1]> {
-        walk_flat_map_expr_field(self, f)
-    }
-
-    fn flat_map_where_predicate(
-        &mut self,
-        where_predicate: WherePredicate,
-    ) -> SmallVec<[WherePredicate; 1]> {
-        walk_flat_map_where_predicate(self, where_predicate)
-    }
-
-    fn visit_where_predicate_kind(&mut self, kind: &mut WherePredicateKind) {
-        walk_where_predicate_kind(self, kind)
-    }
-
-    fn visit_vis(&mut self, vis: &mut Visibility) {
-        walk_vis(self, vis);
-    }
-
-    fn visit_id(&mut self, _id: &mut NodeId) {
-        // Do nothing.
-    }
-
-    // Span visiting is no longer used, but we keep it for now,
-    // in case it's needed for something like #127241.
-    fn visit_span(&mut self, _sp: &mut Span) {
-        // Do nothing.
-    }
-
-    fn visit_pat_field(&mut self, fp: &mut PatField) {
-        walk_pat_field(self, fp)
-    }
-
-    fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> {
-        walk_flat_map_pat_field(self, fp)
-    }
-
-    fn visit_inline_asm(&mut self, asm: &mut InlineAsm) {
-        walk_inline_asm(self, asm)
-    }
-
-    fn visit_inline_asm_sym(&mut self, sym: &mut InlineAsmSym) {
-        walk_inline_asm_sym(self, sym)
-    }
-
-    fn visit_format_args(&mut self, fmt: &mut FormatArgs) {
-        walk_format_args(self, fmt)
-    }
-
-    fn visit_capture_by(&mut self, capture_by: &mut CaptureBy) {
-        walk_capture_by(self, capture_by)
-    }
-
-    fn visit_fn_ret_ty(&mut self, fn_ret_ty: &mut FnRetTy) {
-        walk_fn_ret_ty(self, fn_ret_ty)
-    }
-}
-
 super::common_visitor_and_walkers!((mut) MutVisitor);
 
 macro_rules! generate_flat_map_visitor_fns {
@@ -398,22 +70,6 @@ generate_flat_map_visitor_fns! {
     visit_arms, Arm, flat_map_arm;
 }
 
-#[inline]
-fn visit_thin_vec<T, F>(elems: &mut ThinVec<T>, mut visit_elem: F)
-where
-    F: FnMut(&mut T),
-{
-    for elem in elems {
-        visit_elem(elem);
-    }
-}
-
-fn visit_attrs<T: MutVisitor>(vis: &mut T, attrs: &mut AttrVec) {
-    for attr in attrs.iter_mut() {
-        vis.visit_attribute(attr);
-    }
-}
-
 pub fn walk_flat_map_pat_field<T: MutVisitor>(
     vis: &mut T,
     mut fp: PatField,
@@ -431,47 +87,26 @@ fn visit_nested_use_tree<V: MutVisitor>(
     vis.visit_use_tree(nested_tree);
 }
 
-pub fn walk_flat_map_arm<T: MutVisitor>(vis: &mut T, mut arm: Arm) -> SmallVec<[Arm; 1]> {
-    vis.visit_arm(&mut arm);
-    smallvec![arm]
-}
-
-pub fn walk_flat_map_variant<T: MutVisitor>(
-    vis: &mut T,
-    mut variant: Variant,
-) -> SmallVec<[Variant; 1]> {
-    vis.visit_variant(&mut variant);
-    smallvec![variant]
-}
-
-fn walk_meta_list_item<T: MutVisitor>(vis: &mut T, li: &mut MetaItemInner) {
-    match li {
-        MetaItemInner::MetaItem(mi) => vis.visit_meta_item(mi),
-        MetaItemInner::Lit(_lit) => {}
-    }
-}
-
-fn walk_meta_item<T: MutVisitor>(vis: &mut T, mi: &mut MetaItem) {
-    let MetaItem { unsafety: _, path: _, kind, span } = mi;
-    match kind {
-        MetaItemKind::Word => {}
-        MetaItemKind::List(mis) => visit_thin_vec(mis, |mi| vis.visit_meta_list_item(mi)),
-        MetaItemKind::NameValue(_s) => {}
-    }
-    vis.visit_span(span);
-}
-
-pub fn walk_flat_map_param<T: MutVisitor>(vis: &mut T, mut param: Param) -> SmallVec<[Param; 1]> {
-    vis.visit_param(&mut param);
-    smallvec![param]
+macro_rules! generate_walk_flat_map_fns {
+    ($($fn_name:ident($Ty:ty$(,$extra_name:ident: $ExtraTy:ty)*) => $visit_fn_name:ident;)+) => {$(
+        pub fn $fn_name<V: MutVisitor>(vis: &mut V, mut value: $Ty$(,$extra_name: $ExtraTy)*) -> SmallVec<[$Ty; 1]> {
+            vis.$visit_fn_name(&mut value$(,$extra_name)*);
+            smallvec![value]
+        }
+    )+};
 }
 
-pub fn walk_flat_map_generic_param<T: MutVisitor>(
-    vis: &mut T,
-    mut param: GenericParam,
-) -> SmallVec<[GenericParam; 1]> {
-    vis.visit_generic_param(&mut param);
-    smallvec![param]
+generate_walk_flat_map_fns! {
+    walk_flat_map_arm(Arm) => visit_arm;
+    walk_flat_map_variant(Variant) => visit_variant;
+    walk_flat_map_param(Param) => visit_param;
+    walk_flat_map_generic_param(GenericParam) => visit_generic_param;
+    walk_flat_map_where_predicate(WherePredicate) => visit_where_predicate;
+    walk_flat_map_field_def(FieldDef) => visit_field_def;
+    walk_flat_map_expr_field(ExprField) => visit_expr_field;
+    walk_flat_map_item(P<Item>) => visit_item;
+    walk_flat_map_foreign_item(P<ForeignItem>) => visit_foreign_item;
+    walk_flat_map_assoc_item(P<AssocItem>, ctxt: AssocCtxt) => visit_assoc_item;
 }
 
 fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWhereClauses) {
@@ -482,63 +117,6 @@ fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWh
     vis.visit_span(span_after);
 }
 
-pub fn walk_flat_map_where_predicate<T: MutVisitor>(
-    vis: &mut T,
-    mut pred: WherePredicate,
-) -> SmallVec<[WherePredicate; 1]> {
-    walk_where_predicate(vis, &mut pred);
-    smallvec![pred]
-}
-
-pub fn walk_flat_map_field_def<T: MutVisitor>(
-    vis: &mut T,
-    mut fd: FieldDef,
-) -> SmallVec<[FieldDef; 1]> {
-    vis.visit_field_def(&mut fd);
-    smallvec![fd]
-}
-
-pub fn walk_flat_map_expr_field<T: MutVisitor>(
-    vis: &mut T,
-    mut f: ExprField,
-) -> SmallVec<[ExprField; 1]> {
-    vis.visit_expr_field(&mut f);
-    smallvec![f]
-}
-
-pub fn walk_item_kind<K: WalkItemKind>(
-    kind: &mut K,
-    span: Span,
-    id: NodeId,
-    visibility: &mut Visibility,
-    ctxt: K::Ctxt,
-    vis: &mut impl MutVisitor,
-) {
-    kind.walk(span, id, visibility, ctxt, vis)
-}
-
-pub fn walk_flat_map_item(vis: &mut impl MutVisitor, mut item: P<Item>) -> SmallVec<[P<Item>; 1]> {
-    vis.visit_item(&mut item);
-    smallvec![item]
-}
-
-pub fn walk_flat_map_foreign_item(
-    vis: &mut impl MutVisitor,
-    mut item: P<ForeignItem>,
-) -> SmallVec<[P<ForeignItem>; 1]> {
-    vis.visit_foreign_item(&mut item);
-    smallvec![item]
-}
-
-pub fn walk_flat_map_assoc_item(
-    vis: &mut impl MutVisitor,
-    mut item: P<AssocItem>,
-    ctxt: AssocCtxt,
-) -> SmallVec<[P<AssocItem>; 1]> {
-    vis.visit_assoc_item(&mut item, ctxt);
-    smallvec![item]
-}
-
 pub fn walk_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: P<Expr>) -> Option<P<Expr>> {
     vis.visit_expr(&mut e);
     Some(e)
@@ -576,35 +154,11 @@ fn walk_flat_map_stmt_kind<T: MutVisitor>(vis: &mut T, kind: StmtKind) -> SmallV
         StmtKind::Empty => smallvec![StmtKind::Empty],
         StmtKind::MacCall(mut mac) => {
             let MacCallStmt { mac: mac_, style: _, attrs, tokens: _ } = mac.deref_mut();
-            visit_attrs(vis, attrs);
+            for attr in attrs {
+                vis.visit_attribute(attr);
+            }
             vis.visit_mac_call(mac_);
             smallvec![StmtKind::MacCall(mac)]
         }
     }
 }
-
-fn walk_capture_by<T: MutVisitor>(vis: &mut T, capture_by: &mut CaptureBy) {
-    match capture_by {
-        CaptureBy::Ref => {}
-        CaptureBy::Value { move_kw } => {
-            vis.visit_span(move_kw);
-        }
-        CaptureBy::Use { use_kw } => {
-            vis.visit_span(use_kw);
-        }
-    }
-}
-
-#[derive(Debug)]
-pub enum FnKind<'a> {
-    /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
-    Fn(FnCtxt, &'a mut Visibility, &'a mut Fn),
-
-    /// E.g., `|x, y| body`.
-    Closure(
-        &'a mut ClosureBinder,
-        &'a mut Option<CoroutineKind>,
-        &'a mut P<FnDecl>,
-        &'a mut P<Expr>,
-    ),
-}
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 54781e8235e..9b4535dcfbc 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -1085,6 +1085,7 @@ pub enum NtExprKind {
     Expr2021 { inferred: bool },
 }
 
+/// A macro nonterminal, known in documentation as a fragment specifier.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)]
 pub enum NonterminalKind {
     Item,
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
index b8526cf9d95..ad9e5d1468b 100644
--- a/compiler/rustc_ast/src/util/literal.rs
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -3,7 +3,7 @@
 use std::{ascii, fmt, str};
 
 use rustc_literal_escaper::{
-    MixedUnit, Mode, byte_from_char, unescape_byte, unescape_char, unescape_mixed, unescape_unicode,
+    MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str,
 };
 use rustc_span::{Span, Symbol, kw, sym};
 use tracing::debug;
@@ -87,11 +87,10 @@ impl LitKind {
                     // Force-inlining here is aggressive but the closure is
                     // called on every char in the string, so it can be hot in
                     // programs with many long strings containing escapes.
-                    unescape_unicode(
+                    unescape_str(
                         s,
-                        Mode::Str,
-                        &mut #[inline(always)]
-                        |_, c| match c {
+                        #[inline(always)]
+                        |_, res| match res {
                             Ok(c) => buf.push(c),
                             Err(err) => {
                                 assert!(!err.is_fatal(), "failed to unescape string literal")
@@ -111,8 +110,8 @@ impl LitKind {
             token::ByteStr => {
                 let s = symbol.as_str();
                 let mut buf = Vec::with_capacity(s.len());
-                unescape_unicode(s, Mode::ByteStr, &mut |_, c| match c {
-                    Ok(c) => buf.push(byte_from_char(c)),
+                unescape_byte_str(s, |_, res| match res {
+                    Ok(b) => buf.push(b),
                     Err(err) => {
                         assert!(!err.is_fatal(), "failed to unescape string literal")
                     }
@@ -128,7 +127,7 @@ impl LitKind {
             token::CStr => {
                 let s = symbol.as_str();
                 let mut buf = Vec::with_capacity(s.len());
-                unescape_mixed(s, Mode::CStr, &mut |_span, c| match c {
+                unescape_c_str(s, |_span, c| match c {
                     Ok(MixedUnit::Char(c)) => {
                         buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
                     }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index bd2ab34bfc1..d0c2b2bf68b 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -66,45 +66,6 @@ impl BoundKind {
 }
 
 #[derive(Copy, Clone, Debug)]
-pub enum FnKind<'a> {
-    /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
-    Fn(FnCtxt, &'a Visibility, &'a Fn),
-
-    /// E.g., `|x, y| body`.
-    Closure(&'a ClosureBinder, &'a Option<CoroutineKind>, &'a FnDecl, &'a Expr),
-}
-
-impl<'a> FnKind<'a> {
-    pub fn header(&self) -> Option<&'a FnHeader> {
-        match *self {
-            FnKind::Fn(_, _, Fn { sig, .. }) => Some(&sig.header),
-            FnKind::Closure(..) => None,
-        }
-    }
-
-    pub fn ident(&self) -> Option<&Ident> {
-        match self {
-            FnKind::Fn(_, _, Fn { ident, .. }) => Some(ident),
-            _ => None,
-        }
-    }
-
-    pub fn decl(&self) -> &'a FnDecl {
-        match self {
-            FnKind::Fn(_, _, Fn { sig, .. }) => &sig.decl,
-            FnKind::Closure(_, _, decl, _) => decl,
-        }
-    }
-
-    pub fn ctxt(&self) -> Option<FnCtxt> {
-        match self {
-            FnKind::Fn(ctxt, ..) => Some(*ctxt),
-            FnKind::Closure(..) => None,
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug)]
 pub enum LifetimeCtxt {
     /// Appears in a reference type.
     Ref,
@@ -114,206 +75,405 @@ pub enum LifetimeCtxt {
     GenericArg,
 }
 
-/// Each method of the `Visitor` trait is a hook to be potentially
-/// overridden. Each method's default implementation recursively visits
-/// the substructure of the input via the corresponding `walk` method;
-/// e.g., the `visit_item` method by default calls `visit::walk_item`.
-///
-/// If you want to ensure that your code handles every variant
-/// explicitly, you need to override each method. (And you also need
-/// to monitor future changes to `Visitor` in case a new method with a
-/// new default implementation gets introduced.)
-///
-/// Every `walk_*` method uses deconstruction to access fields of structs and
-/// enums. This will result in a compile error if a field is added, which makes
-/// it more likely the appropriate visit call will be added for it.
-pub trait Visitor<'ast>: Sized {
-    /// The result type of the `visit_*` methods. Can be either `()`,
-    /// or `ControlFlow<T>`.
-    type Result: VisitorResult = ();
-
-    fn visit_ident(&mut self, _ident: &'ast Ident) -> Self::Result {
-        Self::Result::output()
-    }
-    fn visit_foreign_mod(&mut self, nm: &'ast ForeignMod) -> Self::Result {
-        walk_foreign_mod(self, nm)
-    }
-    fn visit_foreign_item(&mut self, i: &'ast ForeignItem) -> Self::Result {
-        walk_item(self, i)
-    }
-    fn visit_item(&mut self, i: &'ast Item) -> Self::Result {
-        walk_item(self, i)
-    }
-    fn visit_local(&mut self, l: &'ast Local) -> Self::Result {
-        walk_local(self, l)
-    }
-    fn visit_block(&mut self, b: &'ast Block) -> Self::Result {
-        walk_block(self, b)
-    }
-    fn visit_stmt(&mut self, s: &'ast Stmt) -> Self::Result {
-        walk_stmt(self, s)
-    }
-    fn visit_param(&mut self, param: &'ast Param) -> Self::Result {
-        walk_param(self, param)
-    }
-    fn visit_arm(&mut self, a: &'ast Arm) -> Self::Result {
-        walk_arm(self, a)
-    }
-    fn visit_pat(&mut self, p: &'ast Pat) -> Self::Result {
-        walk_pat(self, p)
-    }
-    fn visit_anon_const(&mut self, c: &'ast AnonConst) -> Self::Result {
-        walk_anon_const(self, c)
-    }
-    fn visit_expr(&mut self, ex: &'ast Expr) -> Self::Result {
-        walk_expr(self, ex)
-    }
-    /// This method is a hack to workaround unstable of `stmt_expr_attributes`.
-    /// It can be removed once that feature is stabilized.
-    fn visit_method_receiver_expr(&mut self, ex: &'ast Expr) -> Self::Result {
-        self.visit_expr(ex)
-    }
-    fn visit_ty(&mut self, t: &'ast Ty) -> Self::Result {
-        walk_ty(self, t)
-    }
-    fn visit_ty_pat(&mut self, t: &'ast TyPat) -> Self::Result {
-        walk_ty_pat(self, t)
-    }
-    fn visit_generic_param(&mut self, param: &'ast GenericParam) -> Self::Result {
-        walk_generic_param(self, param)
-    }
-    fn visit_generics(&mut self, g: &'ast Generics) -> Self::Result {
-        walk_generics(self, g)
-    }
-    fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result {
-        walk_closure_binder(self, b)
-    }
-    fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result {
-        walk_contract(self, c)
-    }
-    fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result {
-        walk_where_predicate(self, p)
-    }
-    fn visit_where_predicate_kind(&mut self, k: &'ast WherePredicateKind) -> Self::Result {
-        walk_where_predicate_kind(self, k)
-    }
-    fn visit_fn(&mut self, fk: FnKind<'ast>, _: Span, _: NodeId) -> Self::Result {
-        walk_fn(self, fk)
-    }
-    fn visit_assoc_item(&mut self, i: &'ast AssocItem, ctxt: AssocCtxt) -> Self::Result {
-        walk_assoc_item(self, i, ctxt)
-    }
-    fn visit_trait_ref(&mut self, t: &'ast TraitRef) -> Self::Result {
-        walk_trait_ref(self, t)
-    }
-    fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) -> Self::Result {
-        walk_param_bound(self, bounds)
-    }
-    fn visit_precise_capturing_arg(&mut self, arg: &'ast PreciseCapturingArg) -> Self::Result {
-        walk_precise_capturing_arg(self, arg)
-    }
-    fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef) -> Self::Result {
-        walk_poly_trait_ref(self, t)
-    }
-    fn visit_variant_data(&mut self, s: &'ast VariantData) -> Self::Result {
-        walk_variant_data(self, s)
-    }
-    fn visit_field_def(&mut self, s: &'ast FieldDef) -> Self::Result {
-        walk_field_def(self, s)
-    }
-    fn visit_variant(&mut self, v: &'ast Variant) -> Self::Result {
-        walk_variant(self, v)
-    }
-    fn visit_variant_discr(&mut self, discr: &'ast AnonConst) -> Self::Result {
-        self.visit_anon_const(discr)
-    }
-    fn visit_label(&mut self, label: &'ast Label) -> Self::Result {
-        walk_label(self, label)
-    }
-    fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) -> Self::Result {
-        walk_lifetime(self, lifetime)
-    }
-    fn visit_mac_call(&mut self, mac: &'ast MacCall) -> Self::Result {
-        walk_mac(self, mac)
-    }
-    fn visit_id(&mut self, _id: NodeId) -> Self::Result {
-        Self::Result::output()
-    }
-    fn visit_macro_def(&mut self, macro_def: &'ast MacroDef) -> Self::Result {
-        walk_macro_def(self, macro_def)
-    }
-    fn visit_path(&mut self, path: &'ast Path) -> Self::Result {
-        walk_path(self, path)
-    }
-    fn visit_use_tree(&mut self, use_tree: &'ast UseTree) -> Self::Result {
-        walk_use_tree(self, use_tree)
-    }
-    fn visit_nested_use_tree(&mut self, use_tree: &'ast UseTree, id: NodeId) -> Self::Result {
-        try_visit!(self.visit_id(id));
-        self.visit_use_tree(use_tree)
-    }
-    fn visit_path_segment(&mut self, path_segment: &'ast PathSegment) -> Self::Result {
-        walk_path_segment(self, path_segment)
-    }
-    fn visit_generic_args(&mut self, generic_args: &'ast GenericArgs) -> Self::Result {
-        walk_generic_args(self, generic_args)
-    }
-    fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) -> Self::Result {
-        walk_generic_arg(self, generic_arg)
-    }
-    fn visit_assoc_item_constraint(
-        &mut self,
-        constraint: &'ast AssocItemConstraint,
-    ) -> Self::Result {
-        walk_assoc_item_constraint(self, constraint)
-    }
-    fn visit_attribute(&mut self, attr: &'ast Attribute) -> Self::Result {
-        walk_attribute(self, attr)
-    }
-    fn visit_vis(&mut self, vis: &'ast Visibility) -> Self::Result {
-        walk_vis(self, vis)
-    }
-    fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FnRetTy) -> Self::Result {
-        walk_fn_ret_ty(self, ret_ty)
-    }
-    fn visit_fn_header(&mut self, header: &'ast FnHeader) -> Self::Result {
-        walk_fn_header(self, header)
-    }
-    fn visit_expr_field(&mut self, f: &'ast ExprField) -> Self::Result {
-        walk_expr_field(self, f)
-    }
-    fn visit_pat_field(&mut self, fp: &'ast PatField) -> Self::Result {
-        walk_pat_field(self, fp)
-    }
-    fn visit_crate(&mut self, krate: &'ast Crate) -> Self::Result {
-        walk_crate(self, krate)
-    }
-    fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) -> Self::Result {
-        walk_inline_asm(self, asm)
-    }
-    fn visit_format_args(&mut self, fmt: &'ast FormatArgs) -> Self::Result {
-        walk_format_args(self, fmt)
-    }
-    fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) -> Self::Result {
-        walk_inline_asm_sym(self, sym)
-    }
-    fn visit_capture_by(&mut self, _capture_by: &'ast CaptureBy) -> Self::Result {
-        Self::Result::output()
-    }
-    fn visit_coroutine_kind(&mut self, coroutine_kind: &'ast CoroutineKind) -> Self::Result {
-        walk_coroutine_kind(self, coroutine_kind)
-    }
-    fn visit_fn_decl(&mut self, fn_decl: &'ast FnDecl) -> Self::Result {
-        walk_fn_decl(self, fn_decl)
-    }
-    fn visit_qself(&mut self, qs: &'ast Option<P<QSelf>>) -> Self::Result {
-        walk_qself(self, qs)
-    }
-}
-
 #[macro_export]
 macro_rules! common_visitor_and_walkers {
     ($(($mut: ident))? $Visitor:ident$(<$lt:lifetime>)?) => {
+        $(${ignore($lt)}
+            #[derive(Copy, Clone)]
+        )?
+        #[derive(Debug)]
+        pub enum FnKind<'a> {
+            /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
+            Fn(FnCtxt, &'a $($mut)? Visibility, &'a $($mut)? Fn),
+
+            /// E.g., `|x, y| body`.
+            Closure(&'a $($mut)? ClosureBinder, &'a $($mut)? Option<CoroutineKind>, &'a $($mut)? P<FnDecl>, &'a $($mut)? P<Expr>),
+        }
+
+        impl<'a> FnKind<'a> {
+            pub fn header(&'a $($mut)? self) -> Option<&'a $($mut)? FnHeader> {
+                match *self {
+                    FnKind::Fn(_, _, Fn { sig, .. }) => Some(&$($mut)? sig.header),
+                    FnKind::Closure(..) => None,
+                }
+            }
+
+            pub fn ident(&'a $($mut)? self) -> Option<&'a $($mut)? Ident> {
+                match self {
+                    FnKind::Fn(_, _, Fn { ident, .. }) => Some(ident),
+                    _ => None,
+                }
+            }
+
+            pub fn decl(&'a $($mut)? self) -> &'a $($mut)? FnDecl {
+                match self {
+                    FnKind::Fn(_, _, Fn { sig, .. }) => &$($mut)? sig.decl,
+                    FnKind::Closure(_, _, decl, _) => decl,
+                }
+            }
+
+            pub fn ctxt(&self) -> Option<FnCtxt> {
+                match self {
+                    FnKind::Fn(ctxt, ..) => Some(*ctxt),
+                    FnKind::Closure(..) => None,
+                }
+            }
+        }
+
+        /// Each method of this trait is a hook to be potentially
+        /// overridden. Each method's default implementation recursively visits
+        /// the substructure of the input via the corresponding `walk` method;
+        #[doc = concat!(" e.g., the `visit_item` method by default calls `visit"$(, "_", stringify!($mut))?, "::walk_item`.")]
+        ///
+        /// If you want to ensure that your code handles every variant
+        /// explicitly, you need to override each method. (And you also need
+        /// to monitor future changes to this trait in case a new method with a
+        /// new default implementation gets introduced.)
+        ///
+        /// Every `walk_*` method uses deconstruction to access fields of structs and
+        /// enums. This will result in a compile error if a field is added, which makes
+        /// it more likely the appropriate visit call will be added for it.
+        pub trait $Visitor<$($lt)?> : Sized $(${ignore($mut)} + MutVisitorResult<Result = ()>)? {
+            $(
+                ${ignore($lt)}
+                /// The result type of the `visit_*` methods. Can be either `()`,
+                /// or `ControlFlow<T>`.
+                type Result: VisitorResult = ();
+            )?
+
+            // Methods in this trait have one of three forms, with the last two forms
+            // only occuring on `MutVisitor`:
+            //
+            //   fn visit_t(&mut self, t: &mut T);                      // common
+            //   fn flat_map_t(&mut self, t: T) -> SmallVec<[T; 1]>;    // rare
+            //   fn filter_map_t(&mut self, t: T) -> Option<T>;         // rarest
+            //
+            // When writing these methods, it is better to use destructuring like this:
+            //
+            //   fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) {
+            //       visit_a(a);
+            //       visit_b(b);
+            //   }
+            //
+            // than to use field access like this:
+            //
+            //   fn visit_abc(&mut self, abc: &mut ABC) {
+            //       visit_a(&mut abc.a);
+            //       visit_b(&mut abc.b);
+            //       // ignore abc.c
+            //   }
+            //
+            // As well as being more concise, the former is explicit about which fields
+            // are skipped. Furthermore, if a new field is added, the destructuring
+            // version will cause a compile error, which is good. In comparison, the
+            // field access version will continue working and it would be easy to
+            // forget to add handling for it.
+            fn visit_ident(&mut self, Ident { name: _, span }: &$($lt)? $($mut)? Ident) -> Self::Result {
+                visit_span(self, span)
+            }
+
+            fn visit_foreign_mod(&mut self, nm: &$($lt)? $($mut)? ForeignMod) -> Self::Result {
+                walk_foreign_mod(self, nm)
+            }
+
+            fn visit_foreign_item(&mut self, i: &$($lt)? $($mut)? ForeignItem) -> Self::Result {
+                walk_item(self, i)
+            }
+
+            fn visit_item(&mut self, i: &$($lt)? $($mut)? Item) -> Self::Result {
+                walk_item(self, i)
+            }
+
+            fn visit_local(&mut self, l: &$($lt)? $($mut)? Local) -> Self::Result {
+                walk_local(self, l)
+            }
+
+            fn visit_block(&mut self, b: &$($lt)? $($mut)? Block) -> Self::Result {
+                walk_block(self, b)
+            }
+
+            fn visit_param(&mut self, param: &$($lt)? $($mut)? Param) -> Self::Result {
+                walk_param(self, param)
+            }
+
+            fn visit_arm(&mut self, a: &$($lt)? $($mut)? Arm) -> Self::Result {
+                walk_arm(self, a)
+            }
+
+            fn visit_pat(&mut self, p: &$($lt)? $($mut)? Pat) -> Self::Result {
+                walk_pat(self, p)
+            }
+
+            fn visit_anon_const(&mut self, c: &$($lt)? $($mut)? AnonConst) -> Self::Result {
+                walk_anon_const(self, c)
+            }
+
+            fn visit_expr(&mut self, ex: &$($lt)? $($mut)? Expr) -> Self::Result {
+                walk_expr(self, ex)
+            }
+
+            /// This method is a hack to workaround unstable of `stmt_expr_attributes`.
+            /// It can be removed once that feature is stabilized.
+            fn visit_method_receiver_expr(&mut self, ex: &$($lt)? $($mut)? Expr) -> Self::Result {
+                self.visit_expr(ex)
+            }
+
+            fn visit_ty(&mut self, t: &$($lt)? $($mut)? Ty) -> Self::Result {
+                walk_ty(self, t)
+            }
+
+            fn visit_ty_pat(&mut self, t: &$($lt)? $($mut)? TyPat) -> Self::Result {
+                walk_ty_pat(self, t)
+            }
+
+            fn visit_generic_param(&mut self, param: &$($lt)? $($mut)? GenericParam) -> Self::Result {
+                walk_generic_param(self, param)
+            }
+
+            fn visit_generics(&mut self, g: &$($lt)? $($mut)? Generics) -> Self::Result {
+                walk_generics(self, g)
+            }
+            fn visit_closure_binder(&mut self, b: &$($lt)? $($mut)? ClosureBinder) -> Self::Result {
+                walk_closure_binder(self, b)
+            }
+            fn visit_contract(&mut self, c: &$($lt)? $($mut)? FnContract) -> Self::Result {
+                walk_contract(self, c)
+            }
+
+            fn visit_where_predicate(&mut self, p: &$($lt)? $($mut)? WherePredicate) -> Self::Result {
+                walk_where_predicate(self, p)
+            }
+
+            fn visit_where_predicate_kind(&mut self, k: &$($lt)? $($mut)? WherePredicateKind) -> Self::Result {
+                walk_where_predicate_kind(self, k)
+            }
+
+            // for `MutVisitor`: `Span` and `NodeId` are mutated at the caller site.
+            fn visit_fn(
+                &mut self,
+                fk: FnKind<$($lt)? $(${ignore($mut)} '_)?>,
+                _: Span,
+                _: NodeId
+            ) -> Self::Result {
+                walk_fn(self, fk)
+            }
+
+            fn visit_assoc_item(&mut self, i: &$($lt)? $($mut)? AssocItem, ctxt: AssocCtxt) -> Self::Result {
+                walk_assoc_item(self, i, ctxt)
+            }
+
+            fn visit_trait_ref(&mut self, t: &$($lt)? $($mut)? TraitRef) -> Self::Result {
+                walk_trait_ref(self, t)
+            }
+
+            fn visit_param_bound(&mut self, bounds: &$($lt)? $($mut)? GenericBound, _ctxt: BoundKind) -> Self::Result {
+                walk_param_bound(self, bounds)
+            }
+
+            fn visit_precise_capturing_arg(&mut self, arg: &$($lt)? $($mut)? PreciseCapturingArg) -> Self::Result {
+                walk_precise_capturing_arg(self, arg)
+            }
+
+            fn visit_poly_trait_ref(&mut self, t: &$($lt)? $($mut)? PolyTraitRef) -> Self::Result {
+                walk_poly_trait_ref(self, t)
+            }
+
+            fn visit_variant_data(&mut self, s: &$($lt)? $($mut)? VariantData) -> Self::Result {
+                walk_variant_data(self, s)
+            }
+
+            fn visit_field_def(&mut self, s: &$($lt)? $($mut)? FieldDef) -> Self::Result {
+                walk_field_def(self, s)
+            }
+
+            fn visit_variant(&mut self, v: &$($lt)? $($mut)? Variant) -> Self::Result {
+                walk_variant(self, v)
+            }
+
+            fn visit_label(&mut self, label: &$($lt)? $($mut)? Label) -> Self::Result {
+                walk_label(self, label)
+            }
+
+            fn visit_lifetime(&mut self, lifetime: &$($lt)? $($mut)? Lifetime, $(${ignore($lt)} _: LifetimeCtxt )?) -> Self::Result {
+                walk_lifetime(self, lifetime)
+            }
+
+            fn visit_mac_call(&mut self, mac: &$($lt)? $($mut)? MacCall) -> Self::Result {
+                walk_mac(self, mac)
+            }
+
+            fn visit_id(&mut self, _id: $(&$mut)? NodeId) -> Self::Result {
+                Self::Result::output()
+            }
+
+            fn visit_macro_def(&mut self, macro_def: &$($lt)? $($mut)? MacroDef) -> Self::Result {
+                walk_macro_def(self, macro_def)
+            }
+
+            fn visit_path(&mut self, path: &$($lt)? $($mut)? Path) -> Self::Result {
+                walk_path(self, path)
+            }
+
+            fn visit_use_tree(&mut self, use_tree: &$($lt)? $($mut)? UseTree) -> Self::Result {
+                walk_use_tree(self, use_tree)
+            }
+
+            fn visit_path_segment(&mut self, path_segment: &$($lt)? $($mut)? PathSegment) -> Self::Result {
+                walk_path_segment(self, path_segment)
+            }
+
+            fn visit_generic_args(&mut self, generic_args: &$($lt)? $($mut)? GenericArgs) -> Self::Result {
+                walk_generic_args(self, generic_args)
+            }
+
+            fn visit_generic_arg(&mut self, generic_arg: &$($lt)? $($mut)? GenericArg) -> Self::Result {
+                walk_generic_arg(self, generic_arg)
+            }
+
+            fn visit_assoc_item_constraint(
+                &mut self,
+                constraint: &$($lt)? $($mut)? AssocItemConstraint,
+            ) -> Self::Result {
+                walk_assoc_item_constraint(self, constraint)
+            }
+
+            fn visit_attribute(&mut self, attr: &$($lt)? $($mut)? Attribute) -> Self::Result {
+                walk_attribute(self, attr)
+            }
+
+            fn visit_vis(&mut self, vis: &$($lt)? $($mut)? Visibility) -> Self::Result {
+                walk_vis(self, vis)
+            }
+
+            fn visit_fn_ret_ty(&mut self, ret_ty: &$($lt)? $($mut)? FnRetTy) -> Self::Result {
+                walk_fn_ret_ty(self, ret_ty)
+            }
+
+            fn visit_fn_header(&mut self, header: &$($lt)? $($mut)? FnHeader) -> Self::Result {
+                walk_fn_header(self, header)
+            }
+
+            fn visit_expr_field(&mut self, f: &$($lt)? $($mut)? ExprField) -> Self::Result {
+                walk_expr_field(self, f)
+            }
+
+            fn visit_pat_field(&mut self, fp: &$($lt)? $($mut)? PatField) -> Self::Result {
+                walk_pat_field(self, fp)
+            }
+
+            fn visit_crate(&mut self, krate: &$($lt)? $($mut)? Crate) -> Self::Result {
+                walk_crate(self, krate)
+            }
+
+            fn visit_inline_asm(&mut self, asm: &$($lt)? $($mut)? InlineAsm) -> Self::Result {
+                walk_inline_asm(self, asm)
+            }
+
+            fn visit_format_args(&mut self, fmt: &$($lt)? $($mut)? FormatArgs) -> Self::Result {
+                walk_format_args(self, fmt)
+            }
+
+            fn visit_inline_asm_sym(&mut self, sym: &$($lt)? $($mut)? InlineAsmSym) -> Self::Result {
+                walk_inline_asm_sym(self, sym)
+            }
+
+            fn visit_capture_by(&mut self, capture_by: &$($lt)? $($mut)? CaptureBy) -> Self::Result {
+                walk_capture_by(self, capture_by)
+            }
+
+            fn visit_coroutine_kind(&mut self, coroutine_kind: &$($lt)? $($mut)? CoroutineKind) -> Self::Result {
+                walk_coroutine_kind(self, coroutine_kind)
+            }
+
+            fn visit_fn_decl(&mut self, fn_decl: &$($lt)? $($mut)? FnDecl) -> Self::Result {
+                walk_fn_decl(self, fn_decl)
+            }
+
+            fn visit_qself(&mut self, qs: &$($lt)? $($mut)? Option<P<QSelf>>) -> Self::Result {
+                walk_qself(self, qs)
+            }
+
+            // (non-mut) `Visitor`-only methods
+            $(
+                fn visit_stmt(&mut self, s: &$lt Stmt) -> Self::Result {
+                    walk_stmt(self, s)
+                }
+
+                fn visit_nested_use_tree(&mut self, use_tree: &$lt UseTree, id: NodeId) -> Self::Result {
+                    try_visit!(self.visit_id(id));
+                    self.visit_use_tree(use_tree)
+                }
+            )?
+
+            // `MutVisitor`-only methods
+            $(
+                fn flat_map_foreign_item(&mut self, ni: P<ForeignItem>) -> SmallVec<[P<ForeignItem>; 1]> {
+                    walk_flat_map_foreign_item(self, ni)
+                }
+
+                fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
+                    walk_flat_map_item(self, i)
+                }
+
+                fn flat_map_field_def(&mut self, fd: FieldDef) -> SmallVec<[FieldDef; 1]> {
+                    walk_flat_map_field_def(self, fd)
+                }
+
+                fn flat_map_assoc_item(
+                    &mut self,
+                    i: P<AssocItem>,
+                    ctxt: AssocCtxt,
+                ) -> SmallVec<[P<AssocItem>; 1]> {
+                    walk_flat_map_assoc_item(self, i, ctxt)
+                }
+
+                fn flat_map_stmt(&mut self, s: Stmt) -> SmallVec<[Stmt; 1]> {
+                    walk_flat_map_stmt(self, s)
+                }
+
+                fn flat_map_arm(&mut self, arm: Arm) -> SmallVec<[Arm; 1]> {
+                    walk_flat_map_arm(self, arm)
+                }
+
+                fn filter_map_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
+                    walk_filter_map_expr(self, e)
+                }
+
+                fn flat_map_variant(&mut self, v: Variant) -> SmallVec<[Variant; 1]> {
+                    walk_flat_map_variant(self, v)
+                }
+
+                fn flat_map_param(&mut self, param: Param) -> SmallVec<[Param; 1]> {
+                    walk_flat_map_param(self, param)
+                }
+
+                fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> {
+                    walk_flat_map_generic_param(self, param)
+                }
+
+                fn flat_map_expr_field(&mut self, f: ExprField) -> SmallVec<[ExprField; 1]> {
+                    walk_flat_map_expr_field(self, f)
+                }
+
+                fn flat_map_where_predicate(
+                    &mut self,
+                    where_predicate: WherePredicate,
+                ) -> SmallVec<[WherePredicate; 1]> {
+                    walk_flat_map_where_predicate(self, where_predicate)
+                }
+
+                // Span visiting is no longer used, but we keep it for now,
+                // in case it's needed for something like #127241.
+                fn visit_span(&mut self, _sp: &$mut Span) {
+                    // Do nothing.
+                }
+
+                fn flat_map_pat_field(&mut self, fp: PatField) -> SmallVec<[PatField; 1]> {
+                    walk_flat_map_pat_field(self, fp)
+                }
+            )?
+        }
+
         pub trait WalkItemKind {
             type Ctxt;
             fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
@@ -409,6 +569,24 @@ macro_rules! common_visitor_and_walkers {
             V::Result::output()
         }
 
+        $(${ignore($lt)}
+            #[inline]
+        )?
+        fn walk_capture_by<$($lt,)? V: $Visitor$(<$lt>)?>(
+            vis: &mut V,
+            capture_by: &$($lt)? $($mut)? CaptureBy
+        ) -> V::Result {
+            match capture_by {
+                CaptureBy::Ref => { V::Result::output() }
+                CaptureBy::Value { move_kw } => {
+                    visit_span(vis, move_kw)
+                }
+                CaptureBy::Use { use_kw } => {
+                    visit_span(vis, use_kw)
+                }
+            }
+        }
+
         fn visit_bounds<$($lt,)? V: $Visitor$(<$lt>)?>(visitor: &mut V, bounds: &$($lt)? $($mut)? GenericBounds, ctxt: BoundKind) -> V::Result {
             walk_list!(visitor, visit_param_bound, bounds, ctxt);
             V::Result::output()
@@ -989,8 +1167,7 @@ macro_rules! common_visitor_and_walkers {
             try_visit!(vis.visit_vis(visibility));
             try_visit!(vis.visit_ident(ident));
             try_visit!(vis.visit_variant_data(data));
-            $(${ignore($lt)} visit_opt!(vis, visit_variant_discr, disr_expr); )?
-            $(${ignore($mut)} visit_opt!(vis, visit_anon_const, disr_expr); )?
+            visit_opt!(vis, visit_anon_const, disr_expr);
             visit_span(vis, span)
         }
 
@@ -1389,7 +1566,7 @@ macro_rules! common_visitor_and_walkers {
 
         // FIXME: visit the template exhaustively.
         pub fn walk_format_args<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, fmt: &$($lt)? $($mut)? FormatArgs) -> V::Result {
-            let FormatArgs { span, template: _, arguments, uncooked_fmt_str: _ } = fmt;
+            let FormatArgs { span, template: _, arguments, uncooked_fmt_str: _, is_source_literal: _ } = fmt;
             let args = $(${ignore($mut)} arguments.all_args_mut())? $(${ignore($lt)} arguments.all_args())? ;
             for FormatArgument { kind, expr } in args {
                 match kind {
diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml
index 6ac258155fe..dc571f5c367 100644
--- a/compiler/rustc_ast_lowering/Cargo.toml
+++ b/compiler/rustc_ast_lowering/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
+rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
 rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 718edad0cc6..c2140514e31 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -4,6 +4,7 @@ use std::sync::Arc;
 use rustc_ast::ptr::P as AstP;
 use rustc_ast::*;
 use rustc_ast_pretty::pprust::expr_to_string;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir as hir;
 use rustc_hir::HirId;
@@ -831,7 +832,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) {
         if self.tcx.features().async_fn_track_caller()
             && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id)
-            && attrs.into_iter().any(|attr| attr.has_name(sym::track_caller))
+            && find_attr!(*attrs, AttributeKind::TrackCaller(_))
         {
             let unstable_span = self.mark_span_with_reason(
                 DesugaringKind::Async,
@@ -2289,12 +2290,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
         span: Span,
         elements: &'hir [hir::Expr<'hir>],
     ) -> hir::Expr<'hir> {
-        let addrof = hir::ExprKind::AddrOf(
-            hir::BorrowKind::Ref,
-            hir::Mutability::Not,
-            self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements))),
-        );
-        self.expr(span, addrof)
+        let array = self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements)));
+        self.expr_ref(span, array)
+    }
+
+    pub(super) fn expr_ref(&mut self, span: Span, expr: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
+        self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr))
     }
 
     pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index 17b443b8ecc..943cde90dd2 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -1,12 +1,10 @@
-use core::ops::ControlFlow;
 use std::borrow::Cow;
 
-use rustc_ast::visit::Visitor;
 use rustc_ast::*;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_hir as hir;
 use rustc_session::config::FmtDebug;
-use rustc_span::{Ident, Span, Symbol, sym};
+use rustc_span::{DesugaringKind, Ident, Span, Symbol, sym};
 
 use super::LoweringContext;
 
@@ -16,6 +14,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // format_args!() had any arguments _before_ flattening/inlining.
         let allow_const = fmt.arguments.all_args().is_empty();
         let mut fmt = Cow::Borrowed(fmt);
+
+        let sp = self.mark_span_with_reason(
+            DesugaringKind::FormatLiteral { source: fmt.is_source_literal },
+            sp,
+            sp.ctxt().outer_expn_data().allow_internal_unstable,
+        );
+
         if self.tcx.sess.opts.unstable_opts.flatten_format_args {
             fmt = flatten_format_args(fmt);
             fmt = self.inline_literals(fmt);
@@ -476,77 +481,52 @@ fn expand_format_args<'hir>(
         return hir::ExprKind::Call(new, new_args);
     }
 
-    // If the args array contains exactly all the original arguments once,
-    // in order, we can use a simple array instead of a `match` construction.
-    // However, if there's a yield point in any argument except the first one,
-    // we don't do this, because an Argument cannot be kept across yield points.
-    //
-    // This is an optimization, speeding up compilation about 1-2% in some cases.
-    // See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
-    let use_simple_array = argmap.len() == arguments.len()
-        && argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
-        && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
-
-    let args = if arguments.is_empty() {
+    let (let_statements, args) = if arguments.is_empty() {
         // Generate:
-        //    &<core::fmt::Argument>::none()
-        //
-        // Note:
-        //     `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime.
-        //
-        //     This makes sure that this still fails to compile, even when the argument is inlined:
+        //     []
+        (vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
+    } else if argmap.len() == 1 && arguments.len() == 1 {
+        // Only one argument, so we don't need to make the `args` tuple.
         //
-        //     ```
-        //     let f = format_args!("{}", "a");
-        //     println!("{f}"); // error E0716
-        //     ```
-        //
-        //     Cases where keeping the object around is allowed, such as `format_args!("a")`,
-        //     are handled above by the `allow_const` case.
-        let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
-            macsp,
-            hir::LangItem::FormatArgument,
-            sym::none,
-        ));
-        let none = ctx.expr_call(macsp, none_fn, &[]);
-        ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none))
-    } else if use_simple_array {
         // Generate:
-        //     &[
-        //         <core::fmt::Argument>::new_display(&arg0),
-        //         <core::fmt::Argument>::new_lower_hex(&arg1),
-        //         <core::fmt::Argument>::new_debug(&arg2),
-        //         …
-        //     ]
-        let elements = ctx.arena.alloc_from_iter(arguments.iter().zip(argmap).map(
-            |(arg, ((_, ty), placeholder_span))| {
+        //     super let args = [<core::fmt::Argument>::new_display(&arg)];
+        let args = ctx.arena.alloc_from_iter(argmap.iter().map(
+            |(&(arg_index, ty), &placeholder_span)| {
+                let arg = &arguments[arg_index];
                 let placeholder_span =
                     placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
-                let arg_span = match arg.kind {
-                    FormatArgumentKind::Captured(_) => placeholder_span,
-                    _ => arg.expr.span.with_ctxt(macsp.ctxt()),
-                };
                 let arg = ctx.lower_expr(&arg.expr);
-                let ref_arg = ctx.arena.alloc(ctx.expr(
-                    arg_span,
-                    hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
-                ));
+                let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg));
                 make_argument(ctx, placeholder_span, ref_arg, ty)
             },
         ));
-        ctx.expr_array_ref(macsp, elements)
+        let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
+        let args_ident = Ident::new(sym::args, macsp);
+        let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+        let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
+        (vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)))
     } else {
         // Generate:
-        //     &match (&arg0, &arg1, &…) {
-        //         args => [
-        //             <core::fmt::Argument>::new_display(args.0),
-        //             <core::fmt::Argument>::new_lower_hex(args.1),
-        //             <core::fmt::Argument>::new_debug(args.0),
-        //             …
-        //         ]
-        //     }
+        //     super let args = (&arg0, &arg1, &…);
         let args_ident = Ident::new(sym::args, macsp);
         let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+        let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
+            let arg_expr = ctx.lower_expr(&arg.expr);
+            ctx.expr(
+                arg.expr.span.with_ctxt(macsp.ctxt()),
+                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
+            )
+        }));
+        let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
+        let let_statement_1 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args_tuple));
+
+        // Generate:
+        //     super let args = [
+        //         <core::fmt::Argument>::new_display(args.0),
+        //         <core::fmt::Argument>::new_lower_hex(args.1),
+        //         <core::fmt::Argument>::new_debug(args.0),
+        //         …
+        //     ];
         let args = ctx.arena.alloc_from_iter(argmap.iter().map(
             |(&(arg_index, ty), &placeholder_span)| {
                 let arg = &arguments[arg_index];
@@ -567,58 +547,47 @@ fn expand_format_args<'hir>(
                 make_argument(ctx, placeholder_span, arg, ty)
             },
         ));
-        let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
-            let arg_expr = ctx.lower_expr(&arg.expr);
-            ctx.expr(
-                arg.expr.span.with_ctxt(macsp.ctxt()),
-                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
-            )
-        }));
-        let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
-        let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
-        let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
-        let match_expr = ctx.arena.alloc(ctx.expr_match(
-            macsp,
-            args_tuple,
-            match_arms,
-            hir::MatchSource::FormatArgs,
-        ));
-        ctx.expr(
-            macsp,
-            hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
+        let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
+        let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+        let let_statement_2 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
+        (
+            vec![let_statement_1, let_statement_2],
+            ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)),
         )
     };
 
-    if let Some(format_options) = format_options {
+    // Generate:
+    //     &args
+    let args = ctx.expr_ref(macsp, args);
+
+    let call = if let Some(format_options) = format_options {
         // Generate:
-        //     <core::fmt::Arguments>::new_v1_formatted(
-        //         lit_pieces,
-        //         args,
-        //         format_options,
-        //         unsafe { ::core::fmt::UnsafeArg::new() }
-        //     )
+        //     unsafe {
+        //         <core::fmt::Arguments>::new_v1_formatted(
+        //             lit_pieces,
+        //             args,
+        //             format_options,
+        //         )
+        //     }
         let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
             macsp,
             hir::LangItem::FormatArguments,
             sym::new_v1_formatted,
         ));
-        let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
-            macsp,
-            hir::LangItem::FormatUnsafeArg,
-            sym::new,
-        ));
-        let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
+        let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options]);
+        let call = ctx.expr_call(macsp, new_v1_formatted, args);
         let hir_id = ctx.next_id();
-        let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
-            stmts: &[],
-            expr: Some(unsafe_arg_new_call),
-            hir_id,
-            rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
-            span: macsp,
-            targeted_by_break: false,
-        }));
-        let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
-        hir::ExprKind::Call(new_v1_formatted, args)
+        hir::ExprKind::Block(
+            ctx.arena.alloc(hir::Block {
+                stmts: &[],
+                expr: Some(call),
+                hir_id,
+                rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
+                span: macsp,
+                targeted_by_break: false,
+            }),
+            None,
+        )
     } else {
         // Generate:
         //     <core::fmt::Arguments>::new_v1(
@@ -632,35 +601,21 @@ fn expand_format_args<'hir>(
         ));
         let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
         hir::ExprKind::Call(new_v1, new_args)
-    }
-}
-
-fn may_contain_yield_point(e: &ast::Expr) -> bool {
-    struct MayContainYieldPoint;
-
-    impl Visitor<'_> for MayContainYieldPoint {
-        type Result = ControlFlow<()>;
-
-        fn visit_expr(&mut self, e: &ast::Expr) -> ControlFlow<()> {
-            if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
-                ControlFlow::Break(())
-            } else {
-                visit::walk_expr(self, e)
-            }
-        }
-
-        fn visit_mac_call(&mut self, _: &ast::MacCall) -> ControlFlow<()> {
-            // Macros should be expanded at this point.
-            unreachable!("unexpanded macro in ast lowering");
-        }
+    };
 
-        fn visit_item(&mut self, _: &ast::Item) -> ControlFlow<()> {
-            // Do not recurse into nested items.
-            ControlFlow::Continue(())
-        }
+    if !let_statements.is_empty() {
+        // Generate:
+        //     {
+        //         super let …
+        //         super let …
+        //         <core::fmt::Arguments>::new_…(…)
+        //     }
+        let call = ctx.arena.alloc(ctx.expr(macsp, call));
+        let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call));
+        hir::ExprKind::Block(block, None)
+    } else {
+        call
     }
-
-    MayContainYieldPoint.visit_expr(e).is_break()
 }
 
 fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index ef27d0ef69b..8acb5105773 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -2,7 +2,7 @@ use rustc_abi::ExternAbi;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::AssocCtxt;
 use rustc_ast::*;
-use rustc_errors::ErrorGuaranteed;
+use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
 use rustc_hir::def::{DefKind, PerNS, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
 use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin};
@@ -1644,9 +1644,29 @@ impl<'hir> LoweringContext<'_, 'hir> {
             self.error_on_invalid_abi(abi_str);
             ExternAbi::Rust
         });
-        let sess = self.tcx.sess;
-        let features = self.tcx.features();
-        gate_unstable_abi(sess, features, span, extern_abi);
+        let tcx = self.tcx;
+
+        // we can't do codegen for unsupported ABIs, so error now so we won't get farther
+        if !tcx.sess.target.is_abi_supported(extern_abi) {
+            let mut err = struct_span_code_err!(
+                tcx.dcx(),
+                span,
+                E0570,
+                "{extern_abi} is not a supported ABI for the current target",
+            );
+
+            if let ExternAbi::Stdcall { unwind } = extern_abi {
+                let c_abi = ExternAbi::C { unwind };
+                let system_abi = ExternAbi::System { unwind };
+                err.help(format!("if you need `extern {extern_abi}` on win32 and `extern {c_abi}` everywhere else, \
+                    use `extern {system_abi}`"
+                ));
+            }
+            err.emit();
+        }
+        // Show required feature gate even if we already errored, as the user is likely to build the code
+        // for the actually intended target next and then they will need the feature gate.
+        gate_unstable_abi(tcx.sess, tcx.features(), span, extern_abi);
         extern_abi
     }
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index e74fd1db15b..26d7c0cd6d3 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -2292,6 +2292,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
     }
 
+    fn stmt_super_let_pat(
+        &mut self,
+        span: Span,
+        pat: &'hir hir::Pat<'hir>,
+        init: Option<&'hir hir::Expr<'hir>>,
+    ) -> hir::Stmt<'hir> {
+        let hir_id = self.next_id();
+        let local = hir::LetStmt {
+            super_: Some(span),
+            hir_id,
+            init,
+            pat,
+            els: None,
+            source: hir::LocalSource::Normal,
+            span: self.lower_span(span),
+            ty: None,
+        };
+        self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
+    }
+
     fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
         self.block_all(expr.span, &[], Some(expr))
     }
diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs
index b8fa2dd3dd6..6752218fa0d 100644
--- a/compiler/rustc_ast_lowering/src/stability.rs
+++ b/compiler/rustc_ast_lowering/src/stability.rs
@@ -96,6 +96,9 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
         ExternAbi::RustCold => {
             Err(UnstableAbi { abi, feature: sym::rust_cold_cc, explain: GateReason::Experimental })
         }
+        ExternAbi::RustInvalid => {
+            Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail })
+        }
         ExternAbi::GpuKernel => Err(UnstableAbi {
             abi,
             feature: sym::abi_gpu_kernel,
@@ -124,12 +127,12 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
             feature: sym::abi_riscv_interrupt,
             explain: GateReason::Experimental,
         }),
-        ExternAbi::CCmseNonSecureCall => Err(UnstableAbi {
+        ExternAbi::CmseNonSecureCall => Err(UnstableAbi {
             abi,
-            feature: sym::abi_c_cmse_nonsecure_call,
+            feature: sym::abi_cmse_nonsecure_call,
             explain: GateReason::Experimental,
         }),
-        ExternAbi::CCmseNonSecureEntry => Err(UnstableAbi {
+        ExternAbi::CmseNonSecureEntry => Err(UnstableAbi {
             abi,
             feature: sym::cmse_nonsecure_entry,
             explain: GateReason::Experimental,
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
index c738cb2aa2f..1940628b44a 100644
--- a/compiler/rustc_ast_passes/Cargo.toml
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -18,5 +18,6 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_parse = { path = "../rustc_parse" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
+rustc_target = { path = "../rustc_target" }
 thin-vec = "0.2.12"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 9a267501230..6eddce7b590 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -1,20 +1,25 @@
-ast_passes_abi_custom_coroutine =
-    functions with the `"custom"` ABI cannot be `{$coroutine_kind_str}`
+ast_passes_abi_cannot_be_coroutine =
+    functions with the {$abi} ABI cannot be `{$coroutine_kind_str}`
     .suggestion = remove the `{$coroutine_kind_str}` keyword from this definiton
 
-ast_passes_abi_custom_invalid_signature =
-    invalid signature for `extern "custom"` function
-    .note = functions with the `"custom"` ABI cannot have any parameters or return type
-    .suggestion = remove the parameters and return type
-
 ast_passes_abi_custom_safe_foreign_function =
-    foreign functions with the `"custom"` ABI cannot be safe
+    foreign functions with the "custom" ABI cannot be safe
     .suggestion = remove the `safe` keyword from this definition
 
 ast_passes_abi_custom_safe_function =
-    functions with the `"custom"` ABI must be unsafe
+    functions with the "custom" ABI must be unsafe
     .suggestion = add the `unsafe` keyword to this definition
 
+ast_passes_abi_must_not_have_parameters_or_return_type=
+    invalid signature for `extern {$abi}` function
+    .note = functions with the {$abi} ABI cannot have any parameters or return type
+    .suggestion = remove the parameters and return type
+
+ast_passes_abi_must_not_have_return_type=
+    invalid signature for `extern {$abi}` function
+    .note = functions with the "custom" ABI cannot have a return type
+    .help = remove the return type
+
 ast_passes_assoc_const_without_body =
     associated constant in `impl` without body
     .suggestion = provide a definition for the constant
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index b69a91e2f5f..da248251203 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -21,7 +21,7 @@ use std::ops::{Deref, DerefMut};
 use std::str::FromStr;
 
 use itertools::{Either, Itertools};
-use rustc_abi::ExternAbi;
+use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
 use rustc_ast::*;
@@ -37,6 +37,7 @@ use rustc_session::lint::builtin::{
 };
 use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
 use rustc_span::{Ident, Span, kw, sym};
+use rustc_target::spec::{AbiMap, AbiMapping};
 use thin_vec::thin_vec;
 
 use crate::errors::{self, TildeConstReason};
@@ -365,31 +366,77 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    /// An `extern "custom"` function must be unsafe, and must not have any parameters or return
-    /// type.
-    fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
+    /// Check that the signature of this function does not violate the constraints of its ABI.
+    fn check_extern_fn_signature(&self, abi: ExternAbi, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
+        match AbiMap::from_target(&self.sess.target).canonize_abi(abi, false) {
+            AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => {
+                match canon_abi {
+                    CanonAbi::C
+                    | CanonAbi::Rust
+                    | CanonAbi::RustCold
+                    | CanonAbi::Arm(_)
+                    | CanonAbi::GpuKernel
+                    | CanonAbi::X86(_) => { /* nothing to check */ }
+
+                    CanonAbi::Custom => {
+                        // An `extern "custom"` function must be unsafe.
+                        self.reject_safe_fn(abi, ctxt, sig);
+
+                        // An `extern "custom"` function cannot be `async` and/or `gen`.
+                        self.reject_coroutine(abi, sig);
+
+                        // An `extern "custom"` function must have type `fn()`.
+                        self.reject_params_or_return(abi, ident, sig);
+                    }
+
+                    CanonAbi::Interrupt(interrupt_kind) => {
+                        // An interrupt handler cannot be `async` and/or `gen`.
+                        self.reject_coroutine(abi, sig);
+
+                        if let InterruptKind::X86 = interrupt_kind {
+                            // "x86-interrupt" is special because it does have arguments.
+                            // FIXME(workingjubilee): properly lint on acceptable input types.
+                            if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
+                                self.dcx().emit_err(errors::AbiMustNotHaveReturnType {
+                                    span: ret_ty.span,
+                                    abi,
+                                });
+                            }
+                        } else {
+                            // An `extern "interrupt"` function must have type `fn()`.
+                            self.reject_params_or_return(abi, ident, sig);
+                        }
+                    }
+                }
+            }
+            AbiMapping::Invalid => { /* ignore */ }
+        }
+    }
+
+    fn reject_safe_fn(&self, abi: ExternAbi, ctxt: FnCtxt, sig: &FnSig) {
         let dcx = self.dcx();
 
-        // An `extern "custom"` function must be unsafe.
         match sig.header.safety {
             Safety::Unsafe(_) => { /* all good */ }
             Safety::Safe(safe_span) => {
-                let safe_span =
-                    self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span));
+                let source_map = self.sess.psess.source_map();
+                let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span));
                 dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span });
             }
             Safety::Default => match ctxt {
                 FnCtxt::Foreign => { /* all good */ }
                 FnCtxt::Free | FnCtxt::Assoc(_) => {
-                    self.dcx().emit_err(errors::AbiCustomSafeFunction {
+                    dcx.emit_err(errors::AbiCustomSafeFunction {
                         span: sig.span,
+                        abi,
                         unsafe_span: sig.span.shrink_to_lo(),
                     });
                 }
             },
         }
+    }
 
-        // An `extern "custom"` function cannot be `async` and/or `gen`.
+    fn reject_coroutine(&self, abi: ExternAbi, sig: &FnSig) {
         if let Some(coroutine_kind) = sig.header.coroutine_kind {
             let coroutine_kind_span = self
                 .sess
@@ -397,14 +444,16 @@ impl<'a> AstValidator<'a> {
                 .source_map()
                 .span_until_non_whitespace(coroutine_kind.span().to(sig.span));
 
-            self.dcx().emit_err(errors::AbiCustomCoroutine {
+            self.dcx().emit_err(errors::AbiCannotBeCoroutine {
                 span: sig.span,
+                abi,
                 coroutine_kind_span,
                 coroutine_kind_str: coroutine_kind.as_str(),
             });
         }
+    }
 
-        // An `extern "custom"` function must not have any parameters or return type.
+    fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) {
         let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
         if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
             spans.push(ret_ty.span);
@@ -415,11 +464,12 @@ impl<'a> AstValidator<'a> {
             let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span());
             let padding = if header_span.is_empty() { "" } else { " " };
 
-            self.dcx().emit_err(errors::AbiCustomInvalidSignature {
+            self.dcx().emit_err(errors::AbiMustNotHaveParametersOrReturnType {
                 spans,
                 symbol: ident.name,
                 suggestion_span,
                 padding,
+                abi,
             });
         }
     }
@@ -1199,9 +1249,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self.check_foreign_fn_bodyless(*ident, body.as_deref());
                 self.check_foreign_fn_headerless(sig.header);
                 self.check_foreign_item_ascii_only(*ident);
-                if self.extern_mod_abi == Some(ExternAbi::Custom) {
-                    self.check_custom_abi(FnCtxt::Foreign, ident, sig);
-                }
+                self.check_extern_fn_signature(
+                    self.extern_mod_abi.unwrap_or(ExternAbi::FALLBACK),
+                    FnCtxt::Foreign,
+                    ident,
+                    sig,
+                );
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 defaultness,
@@ -1411,9 +1464,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
         if let FnKind::Fn(ctxt, _, fun) = fk
             && let Extern::Explicit(str_lit, _) = fun.sig.header.ext
-            && let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str())
+            && let Ok(abi) = ExternAbi::from_str(str_lit.symbol.as_str())
         {
-            self.check_custom_abi(ctxt, &fun.ident, &fun.sig);
+            self.check_extern_fn_signature(abi, ctxt, &fun.ident, &fun.sig);
         }
 
         self.check_c_variadic_type(fk);
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index c437e62f4d3..d387a4a310e 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -1,5 +1,6 @@
 //! Errors emitted by ast_passes.
 
+use rustc_abi::ExternAbi;
 use rustc_ast::ParamKindOrd;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
@@ -845,6 +846,7 @@ pub(crate) struct AbiCustomSafeForeignFunction {
 pub(crate) struct AbiCustomSafeFunction {
     #[primary_span]
     pub span: Span,
+    pub abi: ExternAbi,
 
     #[suggestion(
         ast_passes_suggestion,
@@ -856,10 +858,11 @@ pub(crate) struct AbiCustomSafeFunction {
 }
 
 #[derive(Diagnostic)]
-#[diag(ast_passes_abi_custom_coroutine)]
-pub(crate) struct AbiCustomCoroutine {
+#[diag(ast_passes_abi_cannot_be_coroutine)]
+pub(crate) struct AbiCannotBeCoroutine {
     #[primary_span]
     pub span: Span,
+    pub abi: ExternAbi,
 
     #[suggestion(
         ast_passes_suggestion,
@@ -872,11 +875,12 @@ pub(crate) struct AbiCustomCoroutine {
 }
 
 #[derive(Diagnostic)]
-#[diag(ast_passes_abi_custom_invalid_signature)]
+#[diag(ast_passes_abi_must_not_have_parameters_or_return_type)]
 #[note]
-pub(crate) struct AbiCustomInvalidSignature {
+pub(crate) struct AbiMustNotHaveParametersOrReturnType {
     #[primary_span]
     pub spans: Vec<Span>,
+    pub abi: ExternAbi,
 
     #[suggestion(
         ast_passes_suggestion,
@@ -888,3 +892,13 @@ pub(crate) struct AbiCustomInvalidSignature {
     pub symbol: Symbol,
     pub padding: &'static str,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_must_not_have_return_type)]
+#[note]
+pub(crate) struct AbiMustNotHaveReturnType {
+    #[primary_span]
+    #[help]
+    pub span: Span,
+    pub abi: ExternAbi,
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index ee49246a4bb..7651e8365a2 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -357,6 +357,10 @@ impl<'a> State<'a> {
                 self.word_nbsp("raw");
                 self.print_mutability(mutability, true);
             }
+            ast::BorrowKind::Pin => {
+                self.word_nbsp("pin");
+                self.print_mutability(mutability, true);
+            }
         }
         self.print_expr_cond_paren(
             expr,
@@ -386,18 +390,44 @@ impl<'a> State<'a> {
 
         let ib = self.ibox(INDENT_UNIT);
 
-        // The Match subexpression in `match x {} - 1` must be parenthesized if
-        // it is the leftmost subexpression in a statement:
-        //
-        //     (match x {}) - 1;
-        //
-        // But not otherwise:
-        //
-        //     let _ = match x {} - 1;
-        //
-        // Same applies to a small set of other expression kinds which eagerly
-        // terminate a statement which opens with them.
-        let needs_par = fixup.would_cause_statement_boundary(expr);
+        let needs_par = {
+            // The Match subexpression in `match x {} - 1` must be parenthesized
+            // if it is the leftmost subexpression in a statement:
+            //
+            //     (match x {}) - 1;
+            //
+            // But not otherwise:
+            //
+            //     let _ = match x {} - 1;
+            //
+            // Same applies to a small set of other expression kinds which
+            // eagerly terminate a statement which opens with them.
+            fixup.would_cause_statement_boundary(expr)
+        } || {
+            // If a binary operation ends up with an attribute, such as
+            // resulting from the following macro expansion, then parentheses
+            // are required so that the attribute encompasses the right
+            // subexpression and not just the left one.
+            //
+            //     #![feature(stmt_expr_attributes)]
+            //
+            //     macro_rules! add_attr {
+            //         ($e:expr) => { #[attr] $e };
+            //     }
+            //
+            //     let _ = add_attr!(1 + 1);
+            //
+            // We must pretty-print `#[attr] (1 + 1)` not `#[attr] 1 + 1`.
+            !attrs.is_empty()
+                && matches!(
+                    expr.kind,
+                    ast::ExprKind::Binary(..)
+                        | ast::ExprKind::Cast(..)
+                        | ast::ExprKind::Assign(..)
+                        | ast::ExprKind::AssignOp(..)
+                        | ast::ExprKind::Range(..)
+                )
+        };
         if needs_par {
             self.popen();
             fixup = FixupContext::default();
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index d4c43456049..dc3598bcc36 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -38,7 +38,8 @@ pub enum InstructionSetAttr {
     ArmT32,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)]
+#[derive(Encodable, Decodable, HashStable_Generic)]
 pub enum OptimizeAttr {
     /// No `#[optimize(..)]` attribute
     #[default]
@@ -182,6 +183,9 @@ impl Deprecation {
 #[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
 pub enum AttributeKind {
     // tidy-alphabetical-start
+    /// Represents `#[align(N)]`.
+    Align { align: Align, span: Span },
+
     /// Represents `#[rustc_allow_const_fn_unstable]`.
     AllowConstFnUnstable(ThinVec<Symbol>),
 
@@ -198,6 +202,9 @@ pub enum AttributeKind {
         span: Span,
     },
 
+    /// Represents `#[cold]`.
+    Cold(Span),
+
     /// Represents `#[rustc_confusables]`.
     Confusables {
         symbols: ThinVec<Symbol>,
@@ -205,6 +212,9 @@ pub enum AttributeKind {
         first_span: Span,
     },
 
+    /// Represents `#[const_continue]`.
+    ConstContinue(Span),
+
     /// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
     ConstStability {
         stability: PartialConstStability,
@@ -224,17 +234,48 @@ pub enum AttributeKind {
     /// Represents `#[inline]` and `#[rustc_force_inline]`.
     Inline(InlineAttr, Span),
 
+    /// Represents `#[loop_match]`.
+    LoopMatch(Span),
+
     /// Represents `#[rustc_macro_transparency]`.
     MacroTransparency(Transparency),
 
+    /// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html).
+    MayDangle(Span),
+
+    /// Represents `#[must_use]`.
+    MustUse {
+        span: Span,
+        /// must_use can optionally have a reason: `#[must_use = "reason this must be used"]`
+        reason: Option<Symbol>,
+    },
+
+    /// Represents `#[naked]`
+    Naked(Span),
+
+    /// Represents `#[no_mangle]`
+    NoMangle(Span),
+
+    /// Represents `#[optimize(size|speed)]`
+    Optimize(OptimizeAttr, Span),
+
+    /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
+    PubTransparent(Span),
+
     /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
     Repr(ThinVec<(ReprAttr, Span)>),
 
+    /// Represents `#[rustc_skip_during_method_dispatch]`.
+    SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
+
     /// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
     Stability {
         stability: Stability,
         /// Span of the attribute.
         span: Span,
     },
+
+    /// Represents `#[track_caller]`
+    TrackCaller(Span),
     // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
new file mode 100644
index 00000000000..e41dd8bde8f
--- /dev/null
+++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
@@ -0,0 +1,42 @@
+use crate::AttributeKind;
+
+#[derive(PartialEq)]
+pub enum EncodeCrossCrate {
+    Yes,
+    No,
+}
+
+impl AttributeKind {
+    pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
+        use AttributeKind::*;
+        use EncodeCrossCrate::*;
+
+        match self {
+            Align { .. } => No,
+            AllowConstFnUnstable(..) => No,
+            AllowInternalUnstable(..) => Yes,
+            AsPtr(..) => Yes,
+            BodyStability { .. } => No,
+            Confusables { .. } => Yes,
+            ConstStability { .. } => Yes,
+            ConstStabilityIndirect => No,
+            Deprecation { .. } => Yes,
+            DocComment { .. } => Yes,
+            Inline(..) => No,
+            MacroTransparency(..) => Yes,
+            Repr(..) => No,
+            Stability { .. } => Yes,
+            Cold(..) => No,
+            ConstContinue(..) => No,
+            LoopMatch(..) => No,
+            MayDangle(..) => No,
+            MustUse { .. } => Yes,
+            Naked(..) => No,
+            NoMangle(..) => No,
+            Optimize(..) => No,
+            PubTransparent(..) => Yes,
+            SkipDuringMethodDispatch { .. } => No,
+            TrackCaller(..) => Yes,
+        }
+    }
+}
diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs
index f8355be09ad..86c73f0d9a0 100644
--- a/compiler/rustc_attr_data_structures/src/lib.rs
+++ b/compiler/rustc_attr_data_structures/src/lib.rs
@@ -9,6 +9,7 @@
 // tidy-alphabetical-end
 
 mod attributes;
+mod encode_cross_crate;
 mod stability;
 mod version;
 
@@ -17,6 +18,7 @@ pub mod lints;
 use std::num::NonZero;
 
 pub use attributes::*;
+pub use encode_cross_crate::EncodeCrossCrate;
 use rustc_abi::Align;
 use rustc_ast::token::CommentKind;
 use rustc_ast::{AttrStyle, IntTy, UintTy};
diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl
index b9b386635f6..2bb27ede860 100644
--- a/compiler/rustc_attr_parsing/messages.ftl
+++ b/compiler/rustc_attr_parsing/messages.ftl
@@ -44,6 +44,9 @@ attr_parsing_incorrect_repr_format_packed_expect_integer =
 attr_parsing_incorrect_repr_format_packed_one_or_zero_arg =
     incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all
 
+attr_parsing_invalid_alignment_value =
+    invalid alignment value: {$error_part}
+
 attr_parsing_invalid_issue_string =
     `issue` must be a non-zero numeric string or "none"
     .must_not_be_zero = `issue` must not be "0", use "none" instead
@@ -86,6 +89,10 @@ attr_parsing_missing_since =
 attr_parsing_multiple_stability_levels =
     multiple stability levels
 
+attr_parsing_naked_functions_incompatible_attribute =
+    attribute incompatible with `#[unsafe(naked)]`
+    .label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`
+    .naked_attribute = function marked with `#[unsafe(naked)]` here
 attr_parsing_non_ident_feature =
     'feature' is not an identifier
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
new file mode 100644
index 00000000000..eadf8657a0f
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -0,0 +1,203 @@
+use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
+use rustc_feature::{AttributeTemplate, template};
+use rustc_session::parse::feature_err;
+use rustc_span::{Span, Symbol, sym};
+
+use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, FinalizeContext, Stage};
+use crate::parser::ArgParser;
+use crate::session_diagnostics::NakedFunctionIncompatibleAttribute;
+
+pub(crate) struct OptimizeParser;
+
+impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
+    const PATH: &[Symbol] = &[sym::optimize];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
+    const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        let Some(list) = args.list() else {
+            cx.expected_list(cx.attr_span);
+            return None;
+        };
+
+        let Some(single) = list.single() else {
+            cx.expected_single_argument(list.span);
+            return None;
+        };
+
+        let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
+            Some(sym::size) => OptimizeAttr::Size,
+            Some(sym::speed) => OptimizeAttr::Speed,
+            Some(sym::none) => OptimizeAttr::DoNotOptimize,
+            _ => {
+                cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
+                OptimizeAttr::Default
+            }
+        };
+
+        Some(AttributeKind::Optimize(res, cx.attr_span))
+    }
+}
+
+pub(crate) struct ColdParser;
+
+impl<S: Stage> SingleAttributeParser<S> for ColdParser {
+    const PATH: &[Symbol] = &[sym::cold];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+            return None;
+        }
+
+        Some(AttributeKind::Cold(cx.attr_span))
+    }
+}
+
+#[derive(Default)]
+pub(crate) struct NakedParser {
+    span: Option<Span>,
+}
+
+impl<S: Stage> AttributeParser<S> for NakedParser {
+    const ATTRIBUTES: AcceptMapping<Self, S> =
+        &[(&[sym::naked], template!(Word), |this, cx, args| {
+            if let Err(span) = args.no_args() {
+                cx.expected_no_args(span);
+                return;
+            }
+
+            if let Some(earlier) = this.span {
+                let span = cx.attr_span;
+                cx.warn_unused_duplicate(earlier, span);
+            } else {
+                this.span = Some(cx.attr_span);
+            }
+        })];
+
+    fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
+        // FIXME(jdonszelmann): upgrade this list to *parsed* attributes
+        // once all of these have parsed forms. That'd make the check much nicer...
+        //
+        // many attributes don't make sense in combination with #[naked].
+        // Notable attributes that are incompatible with `#[naked]` are:
+        //
+        // * `#[inline]`
+        // * `#[track_caller]`
+        // * `#[test]`, `#[ignore]`, `#[should_panic]`
+        //
+        // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
+        // accurate.
+        const ALLOW_LIST: &[rustc_span::Symbol] = &[
+            // conditional compilation
+            sym::cfg_trace,
+            sym::cfg_attr_trace,
+            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
+            sym::test,
+            sym::ignore,
+            sym::should_panic,
+            sym::bench,
+            // diagnostics
+            sym::allow,
+            sym::warn,
+            sym::deny,
+            sym::forbid,
+            sym::deprecated,
+            sym::must_use,
+            // abi, linking and FFI
+            sym::cold,
+            sym::export_name,
+            sym::link_section,
+            sym::linkage,
+            sym::no_mangle,
+            sym::instruction_set,
+            sym::repr,
+            sym::rustc_std_internal_symbol,
+            sym::align,
+            // obviously compatible with self
+            sym::naked,
+            // documentation
+            sym::doc,
+        ];
+
+        let span = self.span?;
+
+        // only if we found a naked attribute do we do the somewhat expensive check
+        'outer: for other_attr in cx.all_attrs {
+            for allowed_attr in ALLOW_LIST {
+                if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
+                    // effectively skips the error message  being emitted below
+                    // if it's a tool attribute
+                    continue 'outer;
+                }
+                if other_attr.word_is(*allowed_attr) {
+                    // effectively skips the error message  being emitted below
+                    // if its an allowed attribute
+                    continue 'outer;
+                }
+
+                if other_attr.word_is(sym::target_feature) {
+                    if !cx.features().naked_functions_target_feature() {
+                        feature_err(
+                            &cx.sess(),
+                            sym::naked_functions_target_feature,
+                            other_attr.span(),
+                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
+                        ).emit();
+                    }
+
+                    continue 'outer;
+                }
+            }
+
+            cx.emit_err(NakedFunctionIncompatibleAttribute {
+                span: other_attr.span(),
+                naked_span: span,
+                attr: other_attr.get_attribute_path().to_string(),
+            });
+        }
+
+        Some(AttributeKind::Naked(span))
+    }
+}
+
+pub(crate) struct TrackCallerParser;
+
+impl<S: Stage> SingleAttributeParser<S> for TrackCallerParser {
+    const PATH: &[Symbol] = &[sym::track_caller];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+            return None;
+        }
+
+        Some(AttributeKind::TrackCaller(cx.attr_span))
+    }
+}
+
+pub(crate) struct NoMangleParser;
+
+impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {
+    const PATH: &[rustc_span::Symbol] = &[sym::no_mangle];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+            return None;
+        }
+
+        Some(AttributeKind::NoMangle(cx.attr_span))
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs
index 25efc3ae49b..11844f4cd95 100644
--- a/compiler/rustc_attr_parsing/src/attributes/inline.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs
@@ -45,10 +45,8 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
             ArgParser::NameValue(_) => {
                 let suggestions =
                     <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "inline");
-                cx.emit_lint(
-                    AttributeLintKind::IllFormedAttributeInput { suggestions },
-                    cx.attr_span,
-                );
+                let span = cx.attr_span;
+                cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
                 return None;
             }
         }
diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
index d4c846de56e..1c8fc5079da 100644
--- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
@@ -14,8 +14,25 @@ impl<S: Stage> SingleAttributeParser<S> for AsPtrParser {
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
     const TEMPLATE: AttributeTemplate = template!(Word);
 
-    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
-        // FIXME: check that there's no args (this is currently checked elsewhere)
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+        }
         Some(AttributeKind::AsPtr(cx.attr_span))
     }
 }
+
+pub(crate) struct PubTransparentParser;
+impl<S: Stage> SingleAttributeParser<S> for PubTransparentParser {
+    const PATH: &[Symbol] = &[sym::rustc_pub_transparent];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+        }
+        Some(AttributeKind::PubTransparent(cx.attr_span))
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
new file mode 100644
index 00000000000..f6c7ac5e3a3
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
@@ -0,0 +1,31 @@
+use rustc_attr_data_structures::AttributeKind;
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Symbol, sym};
+
+use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
+pub(crate) struct LoopMatchParser;
+impl<S: Stage> SingleAttributeParser<S> for LoopMatchParser {
+    const PATH: &[Symbol] = &[sym::loop_match];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+        Some(AttributeKind::LoopMatch(cx.attr_span))
+    }
+}
+
+pub(crate) struct ConstContinueParser;
+impl<S: Stage> SingleAttributeParser<S> for ConstContinueParser {
+    const PATH: &[Symbol] = &[sym::const_continue];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+        Some(AttributeKind::ConstContinue(cx.attr_span))
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index fa2a6087506..d407669cb41 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -17,7 +17,6 @@
 use std::marker::PhantomData;
 
 use rustc_attr_data_structures::AttributeKind;
-use rustc_attr_data_structures::lints::AttributeLintKind;
 use rustc_feature::AttributeTemplate;
 use rustc_span::{Span, Symbol};
 use thin_vec::ThinVec;
@@ -28,12 +27,17 @@ use crate::session_diagnostics::UnusedMultiple;
 
 pub(crate) mod allow_unstable;
 pub(crate) mod cfg;
+pub(crate) mod codegen_attrs;
 pub(crate) mod confusables;
 pub(crate) mod deprecation;
 pub(crate) mod inline;
 pub(crate) mod lint_helpers;
+pub(crate) mod loop_match;
+pub(crate) mod must_use;
 pub(crate) mod repr;
+pub(crate) mod semantics;
 pub(crate) mod stability;
+pub(crate) mod traits;
 pub(crate) mod transparency;
 pub(crate) mod util;
 
@@ -86,8 +90,19 @@ pub(crate) trait AttributeParser<S: Stage>: Default + 'static {
 /// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
 /// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
 pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
+    /// The single path of the attribute this parser accepts.
+    ///
+    /// If you need the parser to accept more than one path, use [`AttributeParser`] instead
     const PATH: &[Symbol];
+
+    /// Configures the precedence of attributes with the same `PATH` on a syntax node.
     const ATTRIBUTE_ORDER: AttributeOrder;
+
+    /// Configures what to do when when the same attribute is
+    /// applied more than once on the same syntax node.
+    ///
+    /// [`ATTRIBUTE_ORDER`](Self::ATTRIBUTE_ORDER) specified which one is assumed to be correct,
+    /// and this specified whether to, for example, warn or error on the other one.
     const ON_DUPLICATE: OnDuplicate<S>;
 
     /// The template this attribute parser should implement. Used for diagnostics.
@@ -97,6 +112,8 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static {
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>;
 }
 
+/// Use in combination with [`SingleAttributeParser`].
+/// `Single<T: SingleAttributeParser>` implements [`AttributeParser`].
 pub(crate) struct Single<T: SingleAttributeParser<S>, S: Stage>(
     PhantomData<(S, T)>,
     Option<(AttributeKind, Span)>,
@@ -173,14 +190,8 @@ impl<S: Stage> OnDuplicate<S> {
         unused: Span,
     ) {
         match self {
-            OnDuplicate::Warn => cx.emit_lint(
-                AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: false },
-                unused,
-            ),
-            OnDuplicate::WarnButFutureError => cx.emit_lint(
-                AttributeLintKind::UnusedDuplicate { this: unused, other: used, warning: true },
-                unused,
-            ),
+            OnDuplicate::Warn => cx.warn_unused_duplicate(used, unused),
+            OnDuplicate::WarnButFutureError => cx.warn_unused_duplicate_future_error(used, unused),
             OnDuplicate::Error => {
                 cx.emit_err(UnusedMultiple {
                     this: used,
@@ -229,6 +240,10 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
     const PATH: &[rustc_span::Symbol];
 
     type Item;
+    /// A function that converts individual items (of type [`Item`](Self::Item)) into the final attribute.
+    ///
+    /// For example, individual representations fomr `#[repr(...)]` attributes into an `AttributeKind::Repr(x)`,
+    ///  where `x` is a vec of these individual reprs.
     const CONVERT: ConvertFn<Self::Item>;
 
     /// The template this attribute parser should implement. Used for diagnostics.
@@ -241,6 +256,8 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
     ) -> impl IntoIterator<Item = Self::Item> + 'c;
 }
 
+/// Use in combination with [`CombineAttributeParser`].
+/// `Combine<T: CombineAttributeParser>` implements [`AttributeParser`].
 pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>(
     PhantomData<(S, T)>,
     ThinVec<<T as CombineAttributeParser<S>>::Item>,
diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
new file mode 100644
index 00000000000..a672d956127
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
@@ -0,0 +1,40 @@
+use rustc_attr_data_structures::AttributeKind;
+use rustc_errors::DiagArgValue;
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Symbol, sym};
+
+use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+use crate::session_diagnostics;
+
+pub(crate) struct MustUseParser;
+
+impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
+    const PATH: &[Symbol] = &[sym::must_use];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
+    const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason");
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        Some(AttributeKind::MustUse {
+            span: cx.attr_span,
+            reason: match args {
+                ArgParser::NoArgs => None,
+                ArgParser::NameValue(name_value) => name_value.value_as_str(),
+                ArgParser::List(_) => {
+                    let suggestions =
+                        <Self as SingleAttributeParser<S>>::TEMPLATE.suggestions(false, "must_use");
+                    cx.emit_err(session_diagnostics::MustUseIllFormedAttributeInput {
+                        num_suggestions: suggestions.len(),
+                        suggestions: DiagArgValue::StrListSepByAnd(
+                            suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
+                        ),
+                        span: cx.attr_span,
+                    });
+                    return None;
+                }
+            },
+        })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index ae9e7871874..4aa27043e98 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -4,7 +4,7 @@ use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
 use rustc_feature::{AttributeTemplate, template};
 use rustc_span::{DUMMY_SP, Span, Symbol, sym};
 
-use super::{CombineAttributeParser, ConvertFn};
+use super::{AcceptMapping, AttributeParser, CombineAttributeParser, ConvertFn, FinalizeContext};
 use crate::context::{AcceptContext, Stage};
 use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
 use crate::session_diagnostics;
@@ -25,7 +25,8 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
     const PATH: &[Symbol] = &[sym::repr];
     const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
     // FIXME(jdonszelmann): never used
-    const TEMPLATE: AttributeTemplate = template!(List: "C");
+    const TEMPLATE: AttributeTemplate =
+        template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");
 
     fn extend<'c>(
         cx: &'c mut AcceptContext<'_, '_, S>,
@@ -203,7 +204,7 @@ fn parse_repr_align<S: Stage>(
                 });
             }
             Align => {
-                cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
+                cx.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
                     span: param_span,
                 });
             }
@@ -266,3 +267,57 @@ fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
         Err("not an unsuffixed integer")
     }
 }
+
+/// Parse #[align(N)].
+#[derive(Default)]
+pub(crate) struct AlignParser(Option<(Align, Span)>);
+
+impl AlignParser {
+    const PATH: &'static [Symbol] = &[sym::align];
+    const TEMPLATE: AttributeTemplate = template!(List: "<alignment in bytes>");
+
+    fn parse<'c, S: Stage>(
+        &mut self,
+        cx: &'c mut AcceptContext<'_, '_, S>,
+        args: &'c ArgParser<'_>,
+    ) {
+        match args {
+            ArgParser::NoArgs | ArgParser::NameValue(_) => {
+                cx.expected_list(cx.attr_span);
+            }
+            ArgParser::List(list) => {
+                let Some(align) = list.single() else {
+                    cx.expected_single_argument(list.span);
+                    return;
+                };
+
+                let Some(lit) = align.lit() else {
+                    cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
+                        span: align.span(),
+                    });
+
+                    return;
+                };
+
+                match parse_alignment(&lit.kind) {
+                    Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
+                    Err(message) => {
+                        cx.emit_err(session_diagnostics::InvalidAlignmentValue {
+                            span: lit.span,
+                            error_part: message,
+                        });
+                    }
+                }
+            }
+        }
+    }
+}
+
+impl<S: Stage> AttributeParser<S> for AlignParser {
+    const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
+
+    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
+        let (align, span) = self.0?;
+        Some(AttributeKind::Align { align, span })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
new file mode 100644
index 00000000000..54f50445fbd
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
@@ -0,0 +1,22 @@
+use rustc_attr_data_structures::AttributeKind;
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Symbol, sym};
+
+use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
+pub(crate) struct MayDangleParser;
+impl<S: Stage> SingleAttributeParser<S> for MayDangleParser {
+    const PATH: &[Symbol] = &[sym::may_dangle];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+        }
+        Some(AttributeKind::MayDangle(cx.attr_span))
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index 6871ff4ec9f..37104855623 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -139,7 +139,10 @@ impl<S: Stage> SingleAttributeParser<S> for ConstStabilityIndirectParser {
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
     const TEMPLATE: AttributeTemplate = template!(Word);
 
-    fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+        }
         Some(AttributeKind::ConstStabilityIndirect)
     }
 }
@@ -361,8 +364,8 @@ pub(crate) fn parse_unstability<S: Stage>(
                 };
             }
             Some(sym::soft) => {
-                if !param.args().no_args() {
-                    cx.emit_err(session_diagnostics::SoftNoArgs { span: param.span() });
+                if let Err(span) = args.no_args() {
+                    cx.emit_err(session_diagnostics::SoftNoArgs { span });
                 }
                 is_soft = true;
             }
diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs
new file mode 100644
index 00000000000..83a98c53c7f
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs
@@ -0,0 +1,54 @@
+use core::mem;
+
+use rustc_attr_data_structures::AttributeKind;
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Symbol, sym};
+
+use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
+pub(crate) struct SkipDuringMethodDispatchParser;
+
+impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
+    const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
+
+    const TEMPLATE: AttributeTemplate = template!(List: "array, boxed_slice");
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        let mut array = false;
+        let mut boxed_slice = false;
+        let Some(args) = args.list() else {
+            cx.expected_list(cx.attr_span);
+            return None;
+        };
+        if args.is_empty() {
+            cx.expected_at_least_one_argument(args.span);
+            return None;
+        }
+        for arg in args.mixed() {
+            let Some(arg) = arg.meta_item() else {
+                cx.unexpected_literal(arg.span());
+                continue;
+            };
+            if let Err(span) = arg.args().no_args() {
+                cx.expected_no_args(span);
+            }
+            let path = arg.path();
+            let (key, skip): (Symbol, &mut bool) = match path.word_sym() {
+                Some(key @ sym::array) => (key, &mut array),
+                Some(key @ sym::boxed_slice) => (key, &mut boxed_slice),
+                _ => {
+                    cx.expected_specific_argument(path.span(), vec!["array", "boxed_slice"]);
+                    continue;
+                }
+            };
+            if mem::replace(skip, true) {
+                cx.duplicate_key(arg.span(), key);
+            }
+        }
+        Some(AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: cx.attr_span })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 51c1760da30..6ca5c64e0bc 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -15,17 +15,24 @@ use rustc_session::Session;
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
 
 use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
+use crate::attributes::codegen_attrs::{
+    ColdParser, NakedParser, NoMangleParser, OptimizeParser, TrackCallerParser,
+};
 use crate::attributes::confusables::ConfusablesParser;
 use crate::attributes::deprecation::DeprecationParser;
 use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
-use crate::attributes::lint_helpers::AsPtrParser;
-use crate::attributes::repr::ReprParser;
+use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser};
+use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
+use crate::attributes::must_use::MustUseParser;
+use crate::attributes::repr::{AlignParser, ReprParser};
+use crate::attributes::semantics::MayDangleParser;
 use crate::attributes::stability::{
     BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
 };
+use crate::attributes::traits::SkipDuringMethodDispatchParser;
 use crate::attributes::transparency::TransparencyParser;
 use crate::attributes::{AttributeParser as _, Combine, Single};
-use crate::parser::{ArgParser, MetaItemParser};
+use crate::parser::{ArgParser, MetaItemParser, PathParser};
 use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
 
 macro_rules! group_type {
@@ -90,9 +97,11 @@ macro_rules! attribute_parsers {
 attribute_parsers!(
     pub(crate) static ATTRIBUTE_PARSERS = [
         // tidy-alphabetical-start
+        AlignParser,
         BodyStabilityParser,
         ConfusablesParser,
         ConstStabilityParser,
+        NakedParser,
         StabilityParser,
         // tidy-alphabetical-end
 
@@ -104,10 +113,20 @@ attribute_parsers!(
 
         // tidy-alphabetical-start
         Single<AsPtrParser>,
+        Single<ColdParser>,
+        Single<ConstContinueParser>,
         Single<ConstStabilityIndirectParser>,
         Single<DeprecationParser>,
         Single<InlineParser>,
+        Single<LoopMatchParser>,
+        Single<MayDangleParser>,
+        Single<MustUseParser>,
+        Single<NoMangleParser>,
+        Single<OptimizeParser>,
+        Single<PubTransparentParser>,
         Single<RustcForceInlineParser>,
+        Single<SkipDuringMethodDispatchParser>,
+        Single<TrackCallerParser>,
         Single<TransparencyParser>,
         // tidy-alphabetical-end
     ];
@@ -164,7 +183,7 @@ pub struct Late;
 ///
 /// Gives [`AttributeParser`]s enough information to create errors, for example.
 pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
-    pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
+    pub(crate) shared: SharedContext<'f, 'sess, S>,
     /// The span of the attribute currently being parsed
     pub(crate) attr_span: Span,
 
@@ -177,7 +196,7 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
     pub(crate) attr_path: AttrPath,
 }
 
-impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
+impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
     pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
         S::emit_err(&self.sess, diag)
     }
@@ -190,6 +209,34 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         (self.emit_lint)(AttributeLint { id, span, kind: lint });
     }
 
+    pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) {
+        self.emit_lint(
+            AttributeLintKind::UnusedDuplicate {
+                this: unused_span,
+                other: used_span,
+                warning: false,
+            },
+            unused_span,
+        )
+    }
+
+    pub(crate) fn warn_unused_duplicate_future_error(
+        &mut self,
+        used_span: Span,
+        unused_span: Span,
+    ) {
+        self.emit_lint(
+            AttributeLintKind::UnusedDuplicate {
+                this: unused_span,
+                other: used_span,
+                warning: true,
+            },
+            unused_span,
+        )
+    }
+}
+
+impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
     pub(crate) fn unknown_key(
         &self,
         span: Span,
@@ -231,6 +278,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         })
     }
 
+    pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span: args_span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedNoArgs,
+        })
+    }
+
     /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
     /// a nicer error message talking about the specific name that was found lacking a value.
     pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
@@ -276,6 +333,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         })
     }
 
+    pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
+        })
+    }
+
     pub(crate) fn expected_specific_argument(
         &self,
         span: Span,
@@ -312,16 +379,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
 }
 
 impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
-    type Target = FinalizeContext<'f, 'sess, S>;
+    type Target = SharedContext<'f, 'sess, S>;
 
     fn deref(&self) -> &Self::Target {
-        &self.finalize_cx
+        &self.shared
     }
 }
 
 impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
     fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.finalize_cx
+        &mut self.shared
     }
 }
 
@@ -329,7 +396,7 @@ impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
 ///
 /// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
 /// errors, for example.
-pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
+pub(crate) struct SharedContext<'p, 'sess, S: Stage> {
     /// The parse context, gives access to the session and the
     /// diagnostics context.
     pub(crate) cx: &'p mut AttributeParser<'sess, S>,
@@ -338,10 +405,40 @@ pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
     /// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to
     pub(crate) target_id: S::Id,
 
-    pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
+    emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
+}
+
+/// Context given to every attribute parser during finalization.
+///
+/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
+/// errors, for example.
+pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
+    pub(crate) shared: SharedContext<'p, 'sess, S>,
+
+    /// A list of all attribute on this syntax node.
+    ///
+    /// Useful for compatibility checks with other attributes in [`finalize`](crate::attributes::AttributeParser::finalize)
+    ///
+    /// Usually, you should use normal attribute parsing logic instead,
+    /// especially when making a *denylist* of other attributes.
+    pub(crate) all_attrs: &'p [PathParser<'p>],
 }
 
 impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
+    type Target = SharedContext<'p, 'sess, S>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.shared
+    }
+}
+
+impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.shared
+    }
+}
+
+impl<'p, 'sess: 'p, S: Stage> Deref for SharedContext<'p, 'sess, S> {
     type Target = AttributeParser<'sess, S>;
 
     fn deref(&self) -> &Self::Target {
@@ -349,7 +446,7 @@ impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
     }
 }
 
-impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> {
+impl<'p, 'sess: 'p, S: Stage> DerefMut for SharedContext<'p, 'sess, S> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         self.cx
     }
@@ -364,8 +461,7 @@ pub enum OmitDoc {
 /// Context created once, for example as part of the ast lowering
 /// context, through which all attributes can be lowered.
 pub struct AttributeParser<'sess, S: Stage = Late> {
-    #[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
-    tools: Vec<Symbol>,
+    pub(crate) tools: Vec<Symbol>,
     features: Option<&'sess Features>,
     sess: &'sess Session,
     stage: PhantomData<S>,
@@ -419,19 +515,13 @@ impl<'sess> AttributeParser<'sess, Early> {
 
         parsed.pop()
     }
-
-    pub fn new_early(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
-        Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
-    }
 }
 
-impl<'sess> AttributeParser<'sess, Late> {
+impl<'sess, S: Stage> AttributeParser<'sess, S> {
     pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
         Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
     }
-}
 
-impl<'sess, S: Stage> AttributeParser<'sess, S> {
     pub(crate) fn sess(&self) -> &'sess Session {
         &self.sess
     }
@@ -459,6 +549,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
         mut emit_lint: impl FnMut(AttributeLint<S::Id>),
     ) -> Vec<Attribute> {
         let mut attributes = Vec::new();
+        let mut attr_paths = Vec::new();
 
         for attr in attrs {
             // If we're only looking for a single attribute, skip all the ones we don't care about.
@@ -502,6 +593,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
                 //     }))
                 // }
                 ast::AttrKind::Normal(n) => {
+                    attr_paths.push(PathParser::Ast(&n.item.path));
+
                     let parser = MetaItemParser::from_attr(n, self.dcx());
                     let path = parser.path();
                     let args = parser.args();
@@ -510,7 +603,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
                     if let Some(accepts) = S::parsers().0.get(parts.as_slice()) {
                         for (template, accept) in accepts {
                             let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
-                                finalize_cx: FinalizeContext {
+                                shared: SharedContext {
                                     cx: self,
                                     target_span,
                                     target_id,
@@ -554,10 +647,13 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
         let mut parsed_attributes = Vec::new();
         for f in &S::parsers().1 {
             if let Some(attr) = f(&mut FinalizeContext {
-                cx: self,
-                target_span,
-                target_id,
-                emit_lint: &mut emit_lint,
+                shared: SharedContext {
+                    cx: self,
+                    target_span,
+                    target_id,
+                    emit_lint: &mut emit_lint,
+                },
+                all_attrs: &attr_paths,
             }) {
                 parsed_attributes.push(Attribute::Parsed(attr));
             }
diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs
index 1edbe3a9d27..aecaae947c9 100644
--- a/compiler/rustc_attr_parsing/src/parser.rs
+++ b/compiler/rustc_attr_parsing/src/parser.rs
@@ -87,6 +87,14 @@ impl<'a> PathParser<'a> {
     pub fn word_is(&self, sym: Symbol) -> bool {
         self.word().map(|i| i.name == sym).unwrap_or(false)
     }
+
+    /// Checks whether the first segments match the givens.
+    ///
+    /// Unlike [`segments_is`](Self::segments_is),
+    /// `self` may contain more segments than the number matched  against.
+    pub fn starts_with(&self, segments: &[Symbol]) -> bool {
+        segments.len() < self.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
+    }
 }
 
 impl Display for PathParser<'_> {
@@ -161,9 +169,15 @@ impl<'a> ArgParser<'a> {
         }
     }
 
-    /// Asserts that there are no arguments
-    pub fn no_args(&self) -> bool {
-        matches!(self, Self::NoArgs)
+    /// Assert that there were no args.
+    /// If there were, get a span to the arguments
+    /// (to pass to [`AcceptContext::expected_no_args`](crate::context::AcceptContext::expected_no_args)).
+    pub fn no_args(&self) -> Result<(), Span> {
+        match self {
+            Self::NoArgs => Ok(()),
+            Self::List(args) => Err(args.span),
+            Self::NameValue(args) => Err(args.eq_span.to(args.value_span)),
+        }
     }
 }
 
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 57ac92a0ca1..263b323e3eb 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -437,6 +437,15 @@ pub(crate) struct IllFormedAttributeInput {
 }
 
 #[derive(Diagnostic)]
+#[diag(attr_parsing_ill_formed_attribute_input)]
+pub(crate) struct MustUseIllFormedAttributeInput {
+    #[primary_span]
+    pub span: Span,
+    pub num_suggestions: usize,
+    pub suggestions: DiagArgValue,
+}
+
+#[derive(Diagnostic)]
 #[diag(attr_parsing_stability_outside_std, code = E0734)]
 pub(crate) struct StabilityOutsideStd {
     #[primary_span]
@@ -451,6 +460,14 @@ pub(crate) struct EmptyConfusables {
 }
 
 #[derive(Diagnostic)]
+#[diag(attr_parsing_invalid_alignment_value, code = E0589)]
+pub(crate) struct InvalidAlignmentValue {
+    #[primary_span]
+    pub span: Span,
+    pub error_part: &'static str,
+}
+
+#[derive(Diagnostic)]
 #[diag(attr_parsing_repr_ident, code = E0565)]
 pub(crate) struct ReprIdent {
     #[primary_span]
@@ -465,8 +482,21 @@ pub(crate) struct UnrecognizedReprHint {
     pub span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)]
+pub(crate) struct NakedFunctionIncompatibleAttribute {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(attr_parsing_naked_attribute)]
+    pub naked_span: Span,
+    pub attr: String,
+}
+
 pub(crate) enum AttributeParseErrorReason {
+    ExpectedNoArgs,
     ExpectedStringLiteral { byte_string: Option<Span> },
+    ExpectedAtLeastOneArgument,
     ExpectedSingleArgument,
     ExpectedList,
     UnexpectedLiteral,
@@ -510,6 +540,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
                 diag.span_label(self.span, "expected a single argument here");
                 diag.code(E0805);
             }
+            AttributeParseErrorReason::ExpectedAtLeastOneArgument => {
+                diag.span_label(self.span, "expected at least 1 argument here");
+            }
             AttributeParseErrorReason::ExpectedList => {
                 diag.span_label(self.span, "expected this to be a list");
             }
@@ -521,6 +554,10 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
                 diag.span_label(self.span, format!("didn't expect a literal here"));
                 diag.code(E0565);
             }
+            AttributeParseErrorReason::ExpectedNoArgs => {
+                diag.span_label(self.span, format!("didn't expect any arguments here"));
+                diag.code(E0565);
+            }
             AttributeParseErrorReason::ExpectedNameValue(None) => {
                 diag.span_label(
                     self.span,
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 34d36849939..98dc898db23 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -3201,14 +3201,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                         let expr_ty: Option<Ty<'_>> =
                             visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());
 
-                        let is_format_arguments_item = if let Some(expr_ty) = expr_ty
-                            && let ty::Adt(adt, _) = expr_ty.kind()
-                        {
-                            self.infcx.tcx.is_lang_item(adt.did(), LangItem::FormatArguments)
-                        } else {
-                            false
-                        };
-
                         if visitor.found == 0
                             && stmt.span.contains(proper_span)
                             && let Some(p) = sm.span_to_margin(stmt.span)
@@ -3236,25 +3228,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                                 ""
                             };
 
-                            if !is_format_arguments_item {
-                                let addition = format!(
-                                    "let {}binding = {};\n{}",
-                                    mutability,
-                                    s,
-                                    " ".repeat(p)
-                                );
-                                err.multipart_suggestion_verbose(
-                                    msg,
-                                    vec![
-                                        (stmt.span.shrink_to_lo(), addition),
-                                        (proper_span, "binding".to_string()),
-                                    ],
-                                    Applicability::MaybeIncorrect,
-                                );
-                            } else {
-                                err.note("the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used");
-                                err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
-                            }
+                            let addition =
+                                format!("let {}binding = {};\n{}", mutability, s, " ".repeat(p));
+                            err.multipart_suggestion_verbose(
+                                msg,
+                                vec![
+                                    (stmt.span.shrink_to_lo(), addition),
+                                    (proper_span, "binding".to_string()),
+                                ],
+                                Applicability::MaybeIncorrect,
+                            );
+
                             suggested = true;
                             break;
                         }
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 095c0df98ac..f9e52239d6f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -71,7 +71,6 @@ impl<'tcx> BorrowExplanation<'tcx> {
     ) {
         let tcx = cx.infcx.tcx;
         let body = cx.body;
-        let local_names = &cx.local_names;
 
         if let Some(span) = borrow_span {
             let def_id = body.source.def_id();
@@ -220,7 +219,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
                     _ => ("destructor", format!("type `{}`", local_decl.ty)),
                 };
 
-                match local_names[dropped_local] {
+                match cx.local_name(dropped_local) {
                     Some(local_name) if !local_decl.from_compiler_desugaring() => {
                         let message = format!(
                             "{borrow_desc}borrow might be used here, when `{local_name}` is dropped \
@@ -670,10 +669,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
 
             Some(Cause::DropVar(local, location)) => {
                 let mut should_note_order = false;
-                if self.local_names[local].is_some()
+                if self.local_name(local).is_some()
                     && let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place
                     && let Some(borrowed_local) = place.as_local()
-                    && self.local_names[borrowed_local].is_some()
+                    && self.local_name(borrowed_local).is_some()
                     && local != borrowed_local
                 {
                     should_note_order = true;
@@ -748,7 +747,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                         Operand::Copy(place) | Operand::Move(place) => {
                             if let Some(l) = place.as_local() {
                                 let local_decl = &self.body.local_decls[l];
-                                if self.local_names[l].is_none() {
+                                if self.local_name(l).is_none() {
                                     local_decl.source_info.span
                                 } else {
                                     span
@@ -793,7 +792,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                             Operand::Copy(place) | Operand::Move(place) => {
                                 if let Some(l) = place.as_local() {
                                     let local_decl = &self.body.local_decls[l];
-                                    if self.local_names[l].is_none() {
+                                    if self.local_name(l).is_none() {
                                         local_decl.source_info.span
                                     } else {
                                         span
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 5e3f3ffa2ea..9ad91d605a7 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -7,17 +7,17 @@ use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::{self as hir, CoroutineKind, LangItem};
-use rustc_index::IndexSlice;
+use rustc_index::{IndexSlice, IndexVec};
 use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
 use rustc_infer::traits::SelectionError;
-use rustc_middle::bug;
 use rustc_middle::mir::{
     AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
     LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement,
-    StatementKind, Terminator, TerminatorKind, find_self_call,
+    StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, find_self_call,
 };
 use rustc_middle::ty::print::Print;
 use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::{bug, span_bug};
 use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Spanned;
@@ -190,6 +190,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     ) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
         self.diags_buffer.buffered_move_errors.get(move_out_indices)
     }
+
+    /// Uses `body.var_debug_info` to find the symbol
+    fn local_name(&self, index: Local) -> Option<Symbol> {
+        *self.local_names().get(index)?
+    }
+
+    fn local_names(&self) -> &IndexSlice<Local, Option<Symbol>> {
+        self.local_names.get_or_init(|| {
+            let mut local_names = IndexVec::from_elem(None, &self.body.local_decls);
+            for var_debug_info in &self.body.var_debug_info {
+                if let VarDebugInfoContents::Place(place) = var_debug_info.value {
+                    if let Some(local) = place.as_local() {
+                        if let Some(prev_name) = local_names[local]
+                            && var_debug_info.name != prev_name
+                        {
+                            span_bug!(
+                                var_debug_info.source_info.span,
+                                "local {:?} has many names (`{}` vs `{}`)",
+                                local,
+                                prev_name,
+                                var_debug_info.name
+                            );
+                        }
+                        local_names[local] = Some(var_debug_info.name);
+                    }
+                }
+            }
+            local_names
+        })
+    }
 }
 
 impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
@@ -430,7 +460,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     /// a name, or its name was generated by the compiler, then `Err` is returned
     fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
         let decl = &self.body.local_decls[local];
-        match self.local_names[local] {
+        match self.local_name(local) {
             Some(name) if !decl.from_compiler_desugaring() => {
                 buf.push_str(name.as_str());
                 Ok(())
@@ -1254,8 +1284,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                         && !spans.is_empty()
                     {
                         let mut span: MultiSpan = spans.clone().into();
+                        err.arg("ty", param_ty.to_string());
+                        let msg = err.dcx.eagerly_translate_to_string(
+                            fluent::borrowck_moved_a_fn_once_in_call_def,
+                            err.args.iter(),
+                        );
+                        err.remove_arg("ty");
                         for sp in spans {
-                            span.push_span_label(sp, fluent::borrowck_moved_a_fn_once_in_call_def);
+                            span.push_span_label(sp, msg.clone());
                         }
                         span.push_span_label(
                             fn_call_span,
@@ -1500,4 +1536,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
         }
     }
+
+    /// Skip over locals that begin with an underscore or have no name
+    pub(crate) fn local_excluded_from_unused_mut_lint(&self, index: Local) -> bool {
+        self.local_name(index).is_none_or(|name| name.as_str().starts_with('_'))
+    }
 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 0394a42ea9c..b21d348183f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -465,11 +465,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         if let PlaceRef { local, projection: [] } = deref_base {
             let decl = &self.body.local_decls[local];
+            let local_name = self.local_name(local).map(|sym| format!("`{sym}`"));
             if decl.is_ref_for_guard() {
                 return self
                     .cannot_move_out_of(
                         span,
-                        &format!("`{}` in pattern guard", self.local_names[local].unwrap()),
+                        &format!(
+                            "{} in pattern guard",
+                            local_name.as_deref().unwrap_or("the place")
+                        ),
                     )
                     .with_note(
                         "variables bound in patterns cannot be moved from \
@@ -825,7 +829,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
 
             if binds_to.len() == 1 {
-                let place_desc = &format!("`{}`", self.local_names[*local].unwrap());
+                let place_desc = self.local_name(*local).map(|sym| format!("`{sym}`"));
 
                 if let Some(expr) = self.find_expr(binding_span) {
                     self.suggest_cloning(err, bind_to.ty, expr, None);
@@ -834,7 +838,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
                     is_partial_move: false,
                     ty: bind_to.ty,
-                    place: place_desc,
+                    place: place_desc.as_deref().unwrap_or("the place"),
                     span: binding_span,
                 });
             }
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index a5c9bad3ac2..fd8a2a6bc35 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -60,7 +60,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 if access_place.as_local().is_some() {
                     reason = ", as it is not declared as mutable".to_string();
                 } else {
-                    let name = self.local_names[local].expect("immutable unnamed local");
+                    let name = self.local_name(local).expect("immutable unnamed local");
                     reason = format!(", as `{name}` is not declared as mutable");
                 }
             }
@@ -285,7 +285,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     .body
                     .local_decls
                     .get(local)
-                    .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) =>
+                    .is_some_and(|l| mut_borrow_of_mutable_ref(l, self.local_name(local))) =>
             {
                 let decl = &self.body.local_decls[local];
                 err.span_label(span, format!("cannot {act}"));
@@ -481,7 +481,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 let (pointer_sigil, pointer_desc) =
                     if local_decl.ty.is_ref() { ("&", "reference") } else { ("*const", "pointer") };
 
-                match self.local_names[local] {
+                match self.local_name(local) {
                     Some(name) if !local_decl.from_compiler_desugaring() => {
                         err.span_label(
                             span,
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 3bec07afa0f..d27e08573e0 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -664,14 +664,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
             self.infcx.tcx,
             self.body,
-            &self.local_names,
+            &self.local_names(),
             &self.upvars,
             errci.fr,
         );
         let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
             self.infcx.tcx,
             self.body,
-            &self.local_names,
+            &self.local_names(),
             &self.upvars,
             errci.outlived_fr,
         );
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 487f78058a8..1ad629ad167 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -399,7 +399,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             [implicit_inputs + argument_index];
         let (_, span) = self.regioncx.get_argument_name_and_span_for_region(
             self.body,
-            &self.local_names,
+            self.local_names(),
             argument_index,
         );
 
@@ -973,7 +973,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         {
             let (arg_name, arg_span) = self.regioncx.get_argument_name_and_span_for_region(
                 self.body,
-                &self.local_names,
+                self.local_names(),
                 arg_index,
             );
             let region_name = self.synthesize_region_name();
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 4d85f109020..82b300dcb17 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -16,7 +16,7 @@
 // tidy-alphabetical-end
 
 use std::borrow::Cow;
-use std::cell::RefCell;
+use std::cell::{OnceCell, RefCell};
 use std::marker::PhantomData;
 use std::ops::{ControlFlow, Deref};
 
@@ -391,7 +391,7 @@ fn do_mir_borrowck<'tcx>(
             used_mut_upvars: SmallVec::new(),
             borrow_set: &borrow_set,
             upvars: &[],
-            local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
+            local_names: OnceCell::from(IndexVec::from_elem(None, &promoted_body.local_decls)),
             region_names: RefCell::default(),
             next_region_name: RefCell::new(1),
             polonius_output: None,
@@ -414,26 +414,6 @@ fn do_mir_borrowck<'tcx>(
         promoted_mbcx.report_move_errors();
     }
 
-    let mut local_names = IndexVec::from_elem(None, &body.local_decls);
-    for var_debug_info in &body.var_debug_info {
-        if let VarDebugInfoContents::Place(place) = var_debug_info.value {
-            if let Some(local) = place.as_local() {
-                if let Some(prev_name) = local_names[local]
-                    && var_debug_info.name != prev_name
-                {
-                    span_bug!(
-                        var_debug_info.source_info.span,
-                        "local {:?} has many names (`{}` vs `{}`)",
-                        local,
-                        prev_name,
-                        var_debug_info.name
-                    );
-                }
-                local_names[local] = Some(var_debug_info.name);
-            }
-        }
-    }
-
     let mut mbcx = MirBorrowckCtxt {
         root_cx,
         infcx: &infcx,
@@ -450,7 +430,7 @@ fn do_mir_borrowck<'tcx>(
         used_mut_upvars: SmallVec::new(),
         borrow_set: &borrow_set,
         upvars: tcx.closure_captures(def),
-        local_names,
+        local_names: OnceCell::new(),
         region_names: RefCell::default(),
         next_region_name: RefCell::new(1),
         move_errors: Vec::new(),
@@ -682,7 +662,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
     upvars: &'tcx [&'tcx ty::CapturedPlace<'tcx>],
 
     /// Names of local (user) variables (extracted from `var_debug_info`).
-    local_names: IndexVec<Local, Option<Symbol>>,
+    local_names: OnceCell<IndexVec<Local, Option<Symbol>>>,
 
     /// Record the region names generated for each region in the given
     /// MIR def so that we can reuse them later in help/error messages.
@@ -2610,7 +2590,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
             };
 
             // Skip over locals that begin with an underscore or have no name
-            if self.local_names[local].is_none_or(|name| name.as_str().starts_with('_')) {
+            if self.local_excluded_from_unused_mut_lint(local) {
                 continue;
             }
 
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index d32e6f1558e..3594c7ec210 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -104,6 +104,8 @@ builtin_macros_concat_bytes_bad_repeat = repeat count is not a positive number
 builtin_macros_concat_bytes_invalid = cannot concatenate {$lit_kind} literals
     .byte_char = try using a byte character
     .byte_str = try using a byte string
+    .c_str = try using a null-terminated byte string
+    .c_str_note = concatenating C strings is ambiguous about including the '\0'
     .number_array = try wrapping the number in an array
 
 builtin_macros_concat_bytes_missing_literal = expected a byte literal
@@ -116,10 +118,6 @@ builtin_macros_concat_bytes_oob = numeric literal is out of bounds
 builtin_macros_concat_bytestr = cannot concatenate a byte string literal
 builtin_macros_concat_c_str_lit = cannot concatenate a C string literal
 
-builtin_macros_concat_idents_ident_args = `concat_idents!()` requires ident args
-
-builtin_macros_concat_idents_missing_args = `concat_idents!()` takes 1 or more arguments
-builtin_macros_concat_idents_missing_comma = `concat_idents!()` expecting comma
 builtin_macros_concat_missing_literal = expected a literal
     .note = only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()`
 
diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
index ea406e70666..e75bc944d7e 100644
--- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
+++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
@@ -62,8 +62,8 @@ pub(crate) fn expand(
 fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span) -> Stmt {
     let usize = cx.path_ident(span, Ident::new(sym::usize, span));
     let ty_usize = cx.ty_path(usize);
-    let size = Ident::from_str_and_span("size", span);
-    let align = Ident::from_str_and_span("align", span);
+    let size = Ident::new(sym::size, span);
+    let align = Ident::new(sym::align, span);
 
     let layout_new = cx.std_path(&[sym::alloc, sym::Layout, sym::from_size_align_unchecked]);
     let layout_new = cx.expr_path(cx.path(span, layout_new));
diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs
index dc3bb8ab52a..df1b1eb60e1 100644
--- a/compiler/rustc_builtin_macros/src/autodiff.rs
+++ b/compiler/rustc_builtin_macros/src/autodiff.rs
@@ -652,8 +652,10 @@ mod llvm_enzyme {
                 exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]);
             } else {
                 let q = QSelf { ty: d_ret_ty, path_span: span, position: 0 };
-                let y =
-                    ExprKind::Path(Some(P(q)), ecx.path_ident(span, Ident::from_str("default")));
+                let y = ExprKind::Path(
+                    Some(P(q)),
+                    ecx.path_ident(span, Ident::with_dummy_span(kw::Default)),
+                );
                 let default_call_expr = ecx.expr(span, y);
                 let default_call_expr =
                     ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs
index fe44350863c..ec3b87467a9 100644
--- a/compiler/rustc_builtin_macros/src/cfg_eval.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs
@@ -161,7 +161,7 @@ impl MutVisitor for CfgEval<'_> {
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn visit_method_receiver_expr(&mut self, expr: &mut P<ast::Expr>) {
+    fn visit_method_receiver_expr(&mut self, expr: &mut ast::Expr) {
         self.0.configure_expr(expr, true);
         mut_visit::walk_expr(self, expr);
     }
diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs
index 456f2b9ab31..92d011fb9d1 100644
--- a/compiler/rustc_builtin_macros/src/concat_bytes.rs
+++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs
@@ -1,6 +1,6 @@
 use rustc_ast::ptr::P;
 use rustc_ast::tokenstream::TokenStream;
-use rustc_ast::{ExprKind, LitIntType, LitKind, UintTy, token};
+use rustc_ast::{ExprKind, LitIntType, LitKind, StrStyle, UintTy, token};
 use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
 use rustc_session::errors::report_lit_error;
 use rustc_span::{ErrorGuaranteed, Span};
@@ -21,15 +21,32 @@ fn invalid_type_err(
     let snippet = cx.sess.source_map().span_to_snippet(span).ok();
     let dcx = cx.dcx();
     match LitKind::from_token_lit(token_lit) {
-        Ok(LitKind::CStr(_, _)) => {
+        Ok(LitKind::CStr(_, style)) => {
             // Avoid ambiguity in handling of terminal `NUL` by refusing to
             // concatenate C string literals as bytes.
-            dcx.emit_err(errors::ConcatCStrLit { span })
+            let sugg = if let Some(mut as_bstr) = snippet
+                && style == StrStyle::Cooked
+                && as_bstr.starts_with('c')
+                && as_bstr.ends_with('"')
+            {
+                // Suggest`c"foo"` -> `b"foo\0"` if we can
+                as_bstr.replace_range(0..1, "b");
+                as_bstr.pop();
+                as_bstr.push_str(r#"\0""#);
+                Some(ConcatBytesInvalidSuggestion::CStrLit { span, as_bstr })
+            } else {
+                // No suggestion for a missing snippet, raw strings, or if for some reason we have
+                // a span that doesn't match `c"foo"` (possible if a proc macro assigns a span
+                // that doesn't actually point to a C string).
+                None
+            };
+            // We can only provide a suggestion if we have a snip and it is not a raw string
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "C string", sugg, cs_note: Some(()) })
         }
         Ok(LitKind::Char(_)) => {
             let sugg =
                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg })
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg, cs_note: None })
         }
         Ok(LitKind::Str(_, _)) => {
             // suggestion would be invalid if we are nested
@@ -38,18 +55,21 @@ fn invalid_type_err(
             } else {
                 None
             };
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg })
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg, cs_note: None })
         }
         Ok(LitKind::Float(_, _)) => {
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None })
-        }
-        Ok(LitKind::Bool(_)) => {
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "boolean", sugg: None })
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None, cs_note: None })
         }
+        Ok(LitKind::Bool(_)) => dcx.emit_err(ConcatBytesInvalid {
+            span,
+            lit_kind: "boolean",
+            sugg: None,
+            cs_note: None,
+        }),
         Ok(LitKind::Int(_, _)) if !is_nested => {
             let sugg =
                 snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span, snippet });
-            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg })
+            dcx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg, cs_note: None })
         }
         Ok(LitKind::Int(val, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::U8))) => {
             assert!(val.get() > u8::MAX.into()); // must be an error
diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs
deleted file mode 100644
index a721f5b84c5..00000000000
--- a/compiler/rustc_builtin_macros/src/concat_idents.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Token};
-use rustc_ast::tokenstream::{TokenStream, TokenTree};
-use rustc_ast::{AttrVec, DUMMY_NODE_ID, Expr, ExprKind, Path, Ty, TyKind};
-use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult};
-use rustc_span::{Ident, Span, Symbol};
-
-use crate::errors;
-
-pub(crate) fn expand_concat_idents<'cx>(
-    cx: &'cx mut ExtCtxt<'_>,
-    sp: Span,
-    tts: TokenStream,
-) -> MacroExpanderResult<'cx> {
-    if tts.is_empty() {
-        let guar = cx.dcx().emit_err(errors::ConcatIdentsMissingArgs { span: sp });
-        return ExpandResult::Ready(DummyResult::any(sp, guar));
-    }
-
-    let mut res_str = String::new();
-    for (i, e) in tts.iter().enumerate() {
-        if i & 1 == 1 {
-            match e {
-                TokenTree::Token(Token { kind: token::Comma, .. }, _) => {}
-                _ => {
-                    let guar = cx.dcx().emit_err(errors::ConcatIdentsMissingComma { span: sp });
-                    return ExpandResult::Ready(DummyResult::any(sp, guar));
-                }
-            }
-        } else {
-            if let TokenTree::Token(token, _) = e {
-                if let Some((ident, _)) = token.ident() {
-                    res_str.push_str(ident.name.as_str());
-                    continue;
-                }
-            }
-
-            let guar = cx.dcx().emit_err(errors::ConcatIdentsIdentArgs { span: sp });
-            return ExpandResult::Ready(DummyResult::any(sp, guar));
-        }
-    }
-
-    let ident = Ident::new(Symbol::intern(&res_str), cx.with_call_site_ctxt(sp));
-
-    struct ConcatIdentsResult {
-        ident: Ident,
-    }
-
-    impl MacResult for ConcatIdentsResult {
-        fn make_expr(self: Box<Self>) -> Option<P<Expr>> {
-            Some(P(Expr {
-                id: DUMMY_NODE_ID,
-                kind: ExprKind::Path(None, Path::from_ident(self.ident)),
-                span: self.ident.span,
-                attrs: AttrVec::new(),
-                tokens: None,
-            }))
-        }
-
-        fn make_ty(self: Box<Self>) -> Option<P<Ty>> {
-            Some(P(Ty {
-                id: DUMMY_NODE_ID,
-                kind: TyKind::Path(None, Path::from_ident(self.ident)),
-                span: self.ident.span,
-                tokens: None,
-            }))
-        }
-    }
-
-    ExpandResult::Ready(Box::new(ConcatIdentsResult { ident }))
-}
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 3a2e96a5e5a..a5ee7349fc6 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -215,6 +215,8 @@ pub(crate) struct ConcatBytesInvalid {
     pub(crate) lit_kind: &'static str,
     #[subdiagnostic]
     pub(crate) sugg: Option<ConcatBytesInvalidSuggestion>,
+    #[note(builtin_macros_c_str_note)]
+    pub(crate) cs_note: Option<()>,
 }
 
 #[derive(Subdiagnostic)]
@@ -239,6 +241,13 @@ pub(crate) enum ConcatBytesInvalidSuggestion {
         span: Span,
         snippet: String,
     },
+    #[note(builtin_macros_c_str_note)]
+    #[suggestion(builtin_macros_c_str, code = "{as_bstr}", applicability = "machine-applicable")]
+    CStrLit {
+        #[primary_span]
+        span: Span,
+        as_bstr: String,
+    },
     #[suggestion(
         builtin_macros_number_array,
         code = "[{snippet}]",
@@ -291,27 +300,6 @@ pub(crate) struct ConcatBytesBadRepeat {
 }
 
 #[derive(Diagnostic)]
-#[diag(builtin_macros_concat_idents_missing_args)]
-pub(crate) struct ConcatIdentsMissingArgs {
-    #[primary_span]
-    pub(crate) span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(builtin_macros_concat_idents_missing_comma)]
-pub(crate) struct ConcatIdentsMissingComma {
-    #[primary_span]
-    pub(crate) span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(builtin_macros_concat_idents_ident_args)]
-pub(crate) struct ConcatIdentsIdentArgs {
-    #[primary_span]
-    pub(crate) span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(builtin_macros_bad_derive_target, code = E0774)]
 pub(crate) struct BadDeriveTarget {
     #[primary_span]
@@ -672,6 +660,7 @@ impl Subdiagnostic for FormatUnusedArg {
     fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
         diag.arg("named", self.named);
         let msg = diag.eagerly_translate(crate::fluent_generated::builtin_macros_format_unused_arg);
+        diag.remove_arg("named");
         diag.span_label(self.span, msg);
     }
 }
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 39f9d5f9005..6785cb6aef5 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -606,6 +606,7 @@ fn make_format_args(
         template,
         arguments: args,
         uncooked_fmt_str,
+        is_source_literal,
     }))
 }
 
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 9e7d0ec9e81..9b6dea21438 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -36,7 +36,6 @@ mod cfg_eval;
 mod compile_error;
 mod concat;
 mod concat_bytes;
-mod concat_idents;
 mod define_opaque;
 mod derive;
 mod deriving;
@@ -84,7 +83,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
         compile_error: compile_error::expand_compile_error,
         concat: concat::expand_concat,
         concat_bytes: concat_bytes::expand_concat_bytes,
-        concat_idents: concat_idents::expand_concat_idents,
         const_format_args: format::expand_format_args,
         core_panic: edition_panic::expand_panic,
         env: env::expand_env,
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index daf480a9ce4..42b7e0e06d1 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -56,7 +56,7 @@ pub fn inject(
     is_test_crate: bool,
     dcx: DiagCtxtHandle<'_>,
 ) {
-    let ecfg = ExpansionConfig::default("proc_macro".to_string(), features);
+    let ecfg = ExpansionConfig::default(sym::proc_macro, features);
     let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
 
     let mut collect = CollectProcMacros {
diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
index a1ee53b7ca2..682e7c9b17a 100644
--- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs
+++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
@@ -36,7 +36,7 @@ pub fn inject(
     let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id());
     let call_site = DUMMY_SP.with_call_site_ctxt(expn_id.to_expn_id());
 
-    let ecfg = ExpansionConfig::default("std_lib_injection".to_string(), features);
+    let ecfg = ExpansionConfig::default(sym::std_lib_injection, features);
     let cx = ExtCtxt::new(sess, ecfg, resolver, None);
 
     let ident_span = if edition >= Edition2018 { span } else { call_site };
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 0bc313cbdac..111c85d49eb 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -6,7 +6,7 @@ use rustc_ast as ast;
 use rustc_ast::entry::EntryPointType;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
-use rustc_ast::visit::{Visitor, walk_item};
+use rustc_ast::visit::Visitor;
 use rustc_ast::{ModKind, attr};
 use rustc_errors::DiagCtxtHandle;
 use rustc_expand::base::{ExtCtxt, ResolverExpand};
@@ -146,11 +146,11 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
         ) = item.kind
         {
             let prev_tests = mem::take(&mut self.tests);
-            walk_item_kind(&mut item.kind, item.span, item.id, &mut item.vis, (), self);
+            ast::mut_visit::walk_item(self, item);
             self.add_test_cases(item.id, span, prev_tests);
         } else {
             // But in those cases, we emit a lint to warn the user of these missing tests.
-            walk_item(&mut InnerItemLinter { sess: self.cx.ext_cx.sess }, &item);
+            ast::visit::walk_item(&mut InnerItemLinter { sess: self.cx.ext_cx.sess }, &item);
         }
     }
 }
@@ -227,7 +227,7 @@ fn generate_test_harness(
     panic_strategy: PanicStrategy,
     test_runner: Option<ast::Path>,
 ) {
-    let econfig = ExpansionConfig::default("test".to_string(), features);
+    let econfig = ExpansionConfig::default(sym::test, features);
     let ext_cx = ExtCtxt::new(sess, econfig, resolver, None);
 
     let expn_id = ext_cx.resolver.expansion_for_ast_pass(
diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock
index a906bec8b7e..b893a2be9a2 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/Cargo.lock
@@ -43,42 +43,42 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cranelift-assembler-x64"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ff8e35182c7372df00447cb90a04e584e032c42b9b9b6e8c50ddaaf0d7900d5"
+checksum = "f6f53499803b1607b6ee0ba0de4ba036e6da700c2e489fe8f9d0f683d0b84d31"
 dependencies = [
  "cranelift-assembler-x64-meta",
 ]
 
 [[package]]
 name = "cranelift-assembler-x64-meta"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14220f9c2698015c3b94dc6b84ae045c1c45509ddc406e43c6139252757fdb7a"
+checksum = "1aadaa5bc8430d0e7bb999459369bedd0e5816ad4a82a0e20748341c4e333eda"
 dependencies = [
  "cranelift-srcgen",
 ]
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d372ef2777ceefd75829e1390211ac240e9196bc60699218f7ea2419038288ee"
+checksum = "2005fda2fc52a2dbce58229b4fb4483b70cbc806ba8ecc11b3f050c1a2d26cac"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-bitset"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56323783e423818fa89ce8078e90a3913d2a6e0810399bfce8ebd7ee87baa81f"
+checksum = "56935e02452ca1249d39ad5c45a96304d0b4300a158a391fd113451e0cd4483d"
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74ffb780aab6186c6e9ba26519654b1ac55a09c0a866f6088a4efbbd84da68ed"
+checksum = "62612786bf00e10999f50217d6f455d02b31591155881a45a903d1a95d1a4043"
 dependencies = [
  "bumpalo",
  "cranelift-assembler-x64",
@@ -97,13 +97,14 @@ dependencies = [
  "serde",
  "smallvec",
  "target-lexicon",
+ "wasmtime-math",
 ]
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c23ef13814d3b39c869650d5961128cbbecad83fbdff4e6836a03ecf6862d7ed"
+checksum = "07bae789df91ef236079733af9df11d852256c64af196f0bc6471ea0f5f301be"
 dependencies = [
  "cranelift-assembler-x64-meta",
  "cranelift-codegen-shared",
@@ -112,33 +113,33 @@ dependencies = [
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f623300657679f847803ce80811454bfff89cea4f6bf684be5c468d4a73631"
+checksum = "1be319616d36527782558a8312508757815f64deb19b094c7b8f4337229a9bc6"
 
 [[package]]
 name = "cranelift-control"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31f4168af69989aa6b91fab46799ed4df6096f3209f4a6c8fb4358f49c60188f"
+checksum = "8810ee1ab5e9bd5cff4c0c8d240e2009cb5c2b79888fde1d5256d605712314b7"
 dependencies = [
  "arbitrary",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca6fa9bae1c8de26d71ac2162f069447610fd91e7780cb480ee0d76ac81eabb8"
+checksum = "086452c97cfbe116bf17dbe622dc5fdf2ea97299c7d4ce42460f284387c9928a"
 dependencies = [
  "cranelift-bitset",
 ]
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8219205608aa0b0e6769b580284a7e055c7e0c323c1041cde7ca078add3e412"
+checksum = "4c27947010ab759330f252610c17a8cd64d123358be4f33164233d04fcd77b80"
 dependencies = [
  "cranelift-codegen",
  "log",
@@ -148,15 +149,15 @@ dependencies = [
 
 [[package]]
 name = "cranelift-isle"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "588d0c5964f10860b04043e55aab26d7f7a206b0fd4f10c5260e8aa5773832bd"
+checksum = "ec67bfb8bd55b1e9760eb9f5186dca8d81bd4d86110f8d5af01154a044c91802"
 
 [[package]]
 name = "cranelift-jit"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56bd917ddc524f84f4066f954062875bdfc0dffea068ee94e906d98de5ac7c33"
+checksum = "d67cdfc447f2abdb46bb30a6582cce189539c3c051c1d5330692376e1400edff"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -174,9 +175,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-module"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68a03c057d8a992e06596c871341e446af43ff9224f941e5b8adea39137a5391"
+checksum = "e4597eaa52bca1ed111986c7a7f70cdbe192f83d271d627201365078e37b7e84"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -185,9 +186,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-native"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ed3c94cb97b14f92b6a94a1d45ef8c851f6a2ad9114e5d91d233f7da638fed"
+checksum = "75a9b63edea46e013fce459c46e500462cb03a0490fdd9c18fe42b1dd7b93aa1"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -196,9 +197,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-object"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a64dacef362a69375a604f6636e5e9a174fb96dba3b273646fcd9fa85c1d0997"
+checksum = "ce706f0166d5b7f31693dff521e87cb9858e12adf22ffcde93c4a2826f8f04a9"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -211,9 +212,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-srcgen"
-version = "0.120.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85256fac1519a7d25a040c1d850fba67478f3f021ad5fdf738ba4425ee862dbf"
+checksum = "7d5870e266df8237b56cc98b04f5739c228565c92dd629ec6c66efa87271a158"
 
 [[package]]
 name = "crc32fast"
@@ -289,6 +290,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "libm"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
+
+[[package]]
 name = "log"
 version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -446,9 +453,9 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
 
 [[package]]
 name = "wasmtime-jit-icache-coherence"
-version = "33.0.0"
+version = "34.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "175e924dbc944c185808466d1e90b5a7feb610f3b9abdfe26f8ee25fd1086d1c"
+checksum = "2eedc0324e37cf39b049f4dca0c30997eaab49f09006d5f4c1994e64e7b7dba8"
 dependencies = [
  "anyhow",
  "cfg-if",
@@ -457,6 +464,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "wasmtime-math"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd35fae4cf51d2b4a9bd2ef04b0eb309fa1849cab6a6ab5ac27cbd054ea284d"
+dependencies = [
+ "libm",
+]
+
+[[package]]
 name = "windows-sys"
 version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml
index 94fcbd0a502..9066e4dbbb5 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.toml
+++ b/compiler/rustc_codegen_cranelift/Cargo.toml
@@ -8,12 +8,12 @@ crate-type = ["dylib"]
 
 [dependencies]
 # These have to be in sync with each other
-cranelift-codegen = { version = "0.120.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] }
-cranelift-frontend = { version = "0.120.0" }
-cranelift-module = { version = "0.120.0" }
-cranelift-native = { version = "0.120.0" }
-cranelift-jit = { version = "0.120.0", optional = true }
-cranelift-object = { version = "0.120.0" }
+cranelift-codegen = { version = "0.121.0", default-features = false, features = ["std", "timing", "unwind", "all-native-arch"] }
+cranelift-frontend = { version = "0.121.0" }
+cranelift-module = { version = "0.121.0" }
+cranelift-native = { version = "0.121.0" }
+cranelift-jit = { version = "0.121.0", optional = true }
+cranelift-object = { version = "0.121.0" }
 target-lexicon = "0.13"
 gimli = { version = "0.31", default-features = false, features = ["write"] }
 object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
@@ -24,12 +24,12 @@ smallvec = "1.8.1"
 
 [patch.crates-io]
 # Uncomment to use an unreleased version of cranelift
-#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
-#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
-#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
-#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
-#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
-#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-33.0.0", version = "0.120.0" }
+#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-34.0.0", version = "0.121.0" }
+#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-34.0.0", version = "0.121.0" }
+#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-34.0.0", version = "0.121.0" }
+#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-34.0.0", version = "0.121.0" }
+#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-34.0.0", version = "0.121.0" }
+#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-34.0.0", version = "0.121.0" }
 
 # Uncomment to use local checkout of cranelift
 #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" }
diff --git a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs
index 674acfbd309..43025137bc6 100644
--- a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs
@@ -6,8 +6,8 @@ use crate::{CodegenBackend, SysrootKind, build_sysroot};
 static ABI_CAFE_REPO: GitRepo = GitRepo::github(
     "Gankra",
     "abi-cafe",
-    "f1220cfd13b57f5c0082c26529163865ee25e115",
-    "fe93a9acd461425d",
+    "94d38030419eb00a1ba80e5e2b4d763dcee58db4",
+    "6efb4457893c8670",
     "abi-cafe",
 );
 
@@ -46,6 +46,10 @@ pub(crate) fn run(
     let mut cmd = ABI_CAFE.run(bootstrap_host_compiler, dirs);
     cmd.arg("--");
 
+    cmd.arg("--debug");
+
+    cmd.arg("--rules").arg(dirs.source_dir.join("scripts/abi-cafe-rules.toml"));
+
     // stdcall, vectorcall and such don't work yet
     cmd.arg("--conventions").arg("c").arg("--conventions").arg("rust");
 
diff --git a/compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch b/compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch
deleted file mode 100644
index 01b6a990b72..00000000000
--- a/compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch
+++ /dev/null
@@ -1,69 +0,0 @@
-From 236df390f3bc4ed69c26f4d51d584bea246da886 Mon Sep 17 00:00:00 2001
-From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
-Date: Tue, 9 Jul 2024 11:25:14 +0000
-Subject: [PATCH] Disable broken tests
-
----
- src/report.rs | 36 ++++++++++++++++++++++++++++++++++++
- 1 file changed, 36 insertions(+)
-
-diff --git a/src/toolchains/rust.rs b/src/toolchains/rust.rs
-index 0c50f7a..bfde2b1 100644
---- a/src/toolchains/rust.rs
-+++ b/src/toolchains/rust.rs
-@@ -83,6 +83,7 @@ impl Toolchain for RustcToolchain {
-             .arg(out_dir)
-             .arg("--target")
-             .arg(built_info::TARGET)
-+            .arg("-g")
-             .arg(format!("-Cmetadata={lib_name}"))
-             .arg(src_path);
-         if let Some(codegen_backend) = &self.codegen_backend {
-diff --git a/src/report.rs b/src/report.rs
-index 958ab43..dcf1044 100644
---- a/src/report.rs
-+++ b/src/report.rs
-@@ -48,6 +48,40 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn Toolchain, callee: &dyn Toolc
-     //
-     // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES
- 
-+    if cfg!(all(target_arch = "aarch64", target_os = "linux")) {
-+        if test.test == "F32Array" && test.options.convention == CallingConvention::C {
-+            result.check = Busted(Check);
-+        }
-+    }
-+
-+    if cfg!(all(target_arch = "aarch64", target_os = "macos")) {
-+        if test.test == "SingleVariantUnion" && test.options.convention == CallingConvention::C && test.options.repr == LangRepr::C {
-+            result.check = Busted(Check);
-+        }
-+
-+        if test.test == "OptionU128" && test.caller == "rustc" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::C {
-+            result.check = Busted(Run);
-+        }
-+
-+        if test.test == "OptionU128" && test.caller == "cgclif" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::C {
-+            result.check = Busted(Check);
-+        }
-+    }
-+
-+    if cfg!(all(target_arch = "x86_64", windows)) {
-+        if test.test == "simple" && test.options.convention == CallingConvention::Rust {
-+            result.check = Busted(Check);
-+        }
-+
-+        if test.test == "simple" && test.options.convention == CallingConvention::Rust && test.caller == "rustc" {
-+            result.check = Busted(Run);
-+        }
-+    }
-+
-+    if test.test == "f16" || test.test == "f128" {
-+        result.run = Skip;
-+    }
-+
-     // END OF VENDOR RESERVED AREA
-     //
-     //
--- 
-2.34.1
-
diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain
index af4bd6dc6b8..150bb562f74 100644
--- a/compiler/rustc_codegen_cranelift/rust-toolchain
+++ b/compiler/rustc_codegen_cranelift/rust-toolchain
@@ -1,4 +1,4 @@
 [toolchain]
-channel = "nightly-2025-05-25"
+channel = "nightly-2025-06-24"
 components = ["rust-src", "rustc-dev", "llvm-tools"]
 profile = "minimal"
diff --git a/compiler/rustc_codegen_cranelift/scripts/abi-cafe-rules.toml b/compiler/rustc_codegen_cranelift/scripts/abi-cafe-rules.toml
new file mode 100644
index 00000000000..54f9445c8e5
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/scripts/abi-cafe-rules.toml
@@ -0,0 +1,17 @@
+[target.'cfg(all(target_arch = "aarch64", target_os = "linux"))']
+'F32Array::conv_c'.busted = "check"
+
+[target.'cfg(all(target_arch = "aarch64", target_os = "macos"))']
+'SingleVariantUnion::conv_c::repr_c'.busted = "check"
+'OptionU128::conv_rust::repr_c::rustc_caller'.busted = "run"
+'OptionU128::conv_rust::repr_c::cgclif_caller'.busted = "check"
+
+[target.'cfg(all(target_arch = "x86_64", windows))']
+'simple::conv_rust'.busted = "check"
+'simple::conv_rust::rustc_caller'.busted = "run"
+
+[target.'*'.'f16']
+run = "skip"
+
+[target.'*'.'f128']
+run = "skip"
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index 32c71f433b0..7e356b4b462 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -151,20 +151,6 @@ rm tests/ui/process/process-panic-after-fork.rs # same
 cp ../dist/bin/rustdoc-clif ../dist/bin/rustdoc # some tests expect bin/rustdoc to exist
 
 cat <<EOF | git apply -
-diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs
-index 30387af428c..f7895b12961 100644
---- a/tests/run-make/linker-warning/rmake.rs
-+++ b/tests/run-make/linker-warning/rmake.rs
-@@ -57,7 +57,8 @@ fn main() {
-             .actual_text("(linker error)", out.stderr())
--            .normalize(r#"/rustc[^/]*/"#, "/rustc/")
-+            .normalize(r#"/tmp/rustc[^/]*/"#, "/tmp/rustc/")
-+            .normalize("libpanic_abort", "libpanic_unwind")
-             .normalize(
-                 regex::escape(run_make_support::build_root().to_str().unwrap()),
-                 "/build-root",
-             )
-             .normalize(r#""[^"]*\/symbols.o""#, "\\"/symbols.o\\"")
 diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
 index 073116933bd..c3e4578204d 100644
 --- a/src/tools/compiletest/src/runtest/run_make.rs
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index c8527c3a57d..3a62cd52a9d 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -228,7 +228,7 @@ fn pointer_for_allocation<'tcx>(
     crate::pointer::Pointer::new(global_ptr)
 }
 
-pub(crate) fn data_id_for_alloc_id(
+fn data_id_for_alloc_id(
     cx: &mut ConstantCx,
     module: &mut dyn Module,
     alloc_id: AllocId,
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
index 615f6c47d90..37fbe4be1b0 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
@@ -202,9 +202,10 @@ pub(super) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             };
             let x = codegen_operand(fx, &x.node);
             let y = codegen_operand(fx, &y.node);
-            let kind = match &kind.node {
-                Operand::Constant(const_) => crate::constant::eval_mir_constant(fx, const_).0,
-                Operand::Copy(_) | Operand::Move(_) => unreachable!("{kind:?}"),
+            let kind = if let Some(const_) = kind.node.constant() {
+                crate::constant::eval_mir_constant(fx, const_).0
+            } else {
+                unreachable!("{kind:?}")
             };
 
             let flt_cc = match kind
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index 46a441488fa..68ff0b622c8 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -205,9 +205,10 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
             // Find a way to reuse `immediate_const_vector` from `codegen_ssa` instead.
             let indexes = {
                 use rustc_middle::mir::interpret::*;
-                let idx_const = match &idx.node {
-                    Operand::Constant(const_) => crate::constant::eval_mir_constant(fx, const_).0,
-                    Operand::Copy(_) | Operand::Move(_) => unreachable!("{idx:?}"),
+                let idx_const = if let Some(const_) = idx.node.constant() {
+                    crate::constant::eval_mir_constant(fx, const_).0
+                } else {
+                    unreachable!("{idx:?}")
                 };
 
                 let idx_bytes = match idx_const {
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index 07ea29f3024..8e34436fb5e 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -184,7 +184,7 @@ impl CodegenBackend for CraneliftCodegenBackend {
         // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)]
         let target_features = if sess.target.arch == "x86_64" && sess.target.os != "none" {
             // x86_64 mandates SSE2 support and rustc requires the x87 feature to be enabled
-            vec![sym::fsxr, sym::sse, sym::sse2, Symbol::intern("x87")]
+            vec![sym::fxsr, sym::sse, sym::sse2, Symbol::intern("x87")]
         } else if sess.target.arch == "aarch64" {
             match &*sess.target.os {
                 "none" => vec![],
diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 18a8a5a1e04..55a28bc9493 100644
--- a/compiler/rustc_codegen_gcc/messages.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
@@ -1,7 +1,3 @@
-codegen_gcc_unknown_ctarget_feature_prefix =
-    unknown feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = features must begin with a `+` to enable or `-` to disable it
-
 codegen_gcc_unwinding_inline_asm =
     GCC backend does not support unwinding from inline asm
 
@@ -16,15 +12,3 @@ codegen_gcc_lto_disallowed = lto can only be run for executables, cdylibs and st
 codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdylib-lto`
 
 codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})
-
-codegen_gcc_unknown_ctarget_feature =
-    unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
-    .possible_feature = you might have meant: `{$rust_feature}`
-    .consider_filing_feature_request = consider filing a feature request
-
-codegen_gcc_missing_features =
-    add the missing features in a `target_feature` attribute
-
-codegen_gcc_target_feature_disable_or_enable =
-    the target features {$features} must all be either enabled or disabled together
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index a2e34d1f8fb..7852aebe0c2 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -1591,9 +1591,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
         (value1, value2)
     }
 
-    fn filter_landing_pad(&mut self, pers_fn: RValue<'gcc>) -> (RValue<'gcc>, RValue<'gcc>) {
+    fn filter_landing_pad(&mut self, pers_fn: RValue<'gcc>) {
         // TODO(antoyo): generate the correct landing pad
-        self.cleanup_landing_pad(pers_fn)
+        self.cleanup_landing_pad(pers_fn);
     }
 
     #[cfg(feature = "master")]
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index 7786be9ae5d..b7e7343460f 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -1,31 +1,7 @@
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::Diagnostic;
 use rustc_span::Span;
 
 #[derive(Diagnostic)]
-#[diag(codegen_gcc_unknown_ctarget_feature_prefix)]
-#[note]
-pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
-    pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_unknown_ctarget_feature)]
-#[note]
-pub(crate) struct UnknownCTargetFeature<'a> {
-    pub feature: &'a str,
-    #[subdiagnostic]
-    pub rust_feature: PossibleFeature<'a>,
-}
-
-#[derive(Subdiagnostic)]
-pub(crate) enum PossibleFeature<'a> {
-    #[help(codegen_gcc_possible_feature)]
-    Some { rust_feature: &'a str },
-    #[help(codegen_gcc_consider_filing_feature_request)]
-    None,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_gcc_unwinding_inline_asm)]
 pub(crate) struct UnwindingInlineAsm {
     #[primary_span]
diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs
index 2e00d5fcb61..42ba40692b7 100644
--- a/compiler/rustc_codegen_gcc/src/gcc_util.rs
+++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs
@@ -1,20 +1,12 @@
 #[cfg(feature = "master")]
 use gccjit::Context;
-use rustc_codegen_ssa::codegen_attrs::check_tied_features;
-use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::unord::UnordSet;
+use rustc_codegen_ssa::target_features;
 use rustc_session::Session;
-use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
-use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
 use smallvec::{SmallVec, smallvec};
 
-use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
-
-fn gcc_features_by_flags(sess: &Session) -> Vec<&str> {
-    let mut features: Vec<&str> = Vec::new();
-    retpoline_features_by_flags(sess, &mut features);
-    features
+fn gcc_features_by_flags(sess: &Session, features: &mut Vec<String>) {
+    target_features::retpoline_features_by_flags(sess, features);
+    // FIXME: LLVM also sets +reserve-x18 here under some conditions.
 }
 
 /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
@@ -44,98 +36,29 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
     features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from));
 
     // -Ctarget-features
-    let known_features = sess.target.rust_target_features();
-    let mut featsmap = FxHashMap::default();
-
-    // Compute implied features
-    let mut all_rust_features = vec![];
-    for feature in sess.opts.cg.target_feature.split(',').chain(gcc_features_by_flags(sess)) {
-        if let Some(feature) = feature.strip_prefix('+') {
-            all_rust_features.extend(
-                UnordSet::from(sess.target.implied_target_features(feature))
-                    .to_sorted_stable_ord()
-                    .iter()
-                    .map(|&&s| (true, s)),
-            )
-        } else if let Some(feature) = feature.strip_prefix('-') {
-            // FIXME: Why do we not remove implied features on "-" here?
-            // We do the equivalent above in `target_config`.
-            // See <https://github.com/rust-lang/rust/issues/134792>.
-            all_rust_features.push((false, feature));
-        } else if !feature.is_empty() && diagnostics {
-            sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
-        }
-    }
-    // Remove features that are meant for rustc, not codegen.
-    all_rust_features.retain(|&(_, feature)| {
-        // Retain if it is not a rustc feature
-        !RUSTC_SPECIFIC_FEATURES.contains(&feature)
-    });
-
-    // Check feature validity.
-    if diagnostics {
-        for &(enable, feature) in &all_rust_features {
-            let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
-            match feature_state {
-                None => {
-                    let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| {
-                        let gcc_features = to_gcc_features(sess, rust_feature);
-                        if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature)
-                        {
-                            Some(rust_feature)
-                        } else {
-                            None
-                        }
-                    });
-                    let unknown_feature = if let Some(rust_feature) = rust_feature {
-                        UnknownCTargetFeature {
-                            feature,
-                            rust_feature: PossibleFeature::Some { rust_feature },
-                        }
-                    } else {
-                        UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
-                    };
-                    sess.dcx().emit_warn(unknown_feature);
-                }
-                Some(&(_, stability, _)) => {
-                    stability.verify_feature_enabled_by_flag(sess, enable, feature);
-                }
-            }
-
-            // FIXME(nagisa): figure out how to not allocate a full hashset here.
-            featsmap.insert(feature, enable);
-        }
-    }
-
-    // Translate this into GCC features.
-    let feats =
-        all_rust_features.iter().flat_map(|&(enable, feature)| {
-            let enable_disable = if enable { '+' } else { '-' };
+    target_features::flag_to_backend_features(
+        sess,
+        diagnostics,
+        |feature| to_gcc_features(sess, feature),
+        |feature, enable| {
             // We run through `to_gcc_features` when
             // passing requests down to GCC. This means that all in-language
             // features also work on the command line instead of having two
             // different names when the GCC name and the Rust name differ.
-            to_gcc_features(sess, feature)
-                .iter()
-                .flat_map(|feat| to_gcc_features(sess, feat).into_iter())
-                .map(|feature| {
-                    if enable_disable == '-' {
-                        format!("-{}", feature)
-                    } else {
-                        feature.to_string()
-                    }
-                })
-                .collect::<Vec<_>>()
-        });
-    features.extend(feats);
-
-    if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
-        sess.dcx().emit_err(TargetFeatureDisableOrEnable {
-            features: f,
-            span: None,
-            missing_features: None,
-        });
-    }
+            features.extend(
+                to_gcc_features(sess, feature)
+                    .iter()
+                    .flat_map(|feat| to_gcc_features(sess, feat).into_iter())
+                    .map(
+                        |feature| {
+                            if !enable { format!("-{}", feature) } else { feature.to_string() }
+                        },
+                    ),
+            );
+        },
+    );
+
+    gcc_features_by_flags(sess, &mut features);
 
     features
 }
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index aa57655921d..a912678ef2a 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -102,6 +102,7 @@ use rustc_codegen_ssa::back::write::{
     CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn,
 };
 use rustc_codegen_ssa::base::codegen_crate;
+use rustc_codegen_ssa::target_features::cfg_target_feature;
 use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods};
 use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen, TargetConfig};
 use rustc_data_structures::fx::FxIndexMap;
@@ -476,42 +477,21 @@ fn to_gcc_opt_level(optlevel: Option<OptLevel>) -> OptimizationLevel {
 
 /// Returns the features that should be set in `cfg(target_feature)`.
 fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig {
-    // TODO(antoyo): use global_gcc_features.
-    let f = |allow_unstable| {
-        sess.target
-            .rust_target_features()
-            .iter()
-            .filter_map(|&(feature, gate, _)| {
-                if allow_unstable
-                    || (gate.in_cfg()
-                        && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
-                {
-                    Some(feature)
-                } else {
-                    None
-                }
-            })
-            .filter(|feature| {
-                // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it.
-                if *feature == "neon" {
-                    return false;
-                }
-                target_info.cpu_supports(feature)
-                // cSpell:disable
-                /*
-                  adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
-                  avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
-                  bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
-                  sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
-                */
-                // cSpell:enable
-            })
-            .map(Symbol::intern)
-            .collect()
-    };
-
-    let target_features = f(false);
-    let unstable_target_features = f(true);
+    let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| {
+        // TODO: we disable Neon for now since we don't support the LLVM intrinsics for it.
+        if feature == "neon" {
+            return false;
+        }
+        target_info.cpu_supports(feature)
+        // cSpell:disable
+        /*
+          adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512fp16, avx512ifma,
+          avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
+          bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
+          sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
+        */
+        // cSpell:enable
+    });
 
     let has_reliable_f16 = target_info.supports_target_dependent_type(CType::Float16);
     let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128);
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index 3faeb9b3b22..3885f18271f 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -59,16 +59,6 @@ codegen_llvm_symbol_already_defined =
 codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple}
 codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}
 
-codegen_llvm_unknown_ctarget_feature =
-    unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
-    .possible_feature = you might have meant: `{$rust_feature}`
-    .consider_filing_feature_request = consider filing a feature request
-
-codegen_llvm_unknown_ctarget_feature_prefix =
-    unknown feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = features must begin with a `+` to enable or `-` to disable it
-
 codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
 
 codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 27fd09745ff..adb53e0b66c 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -491,11 +491,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
         let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
         attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
     }
-    // function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
-    // the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
-    if let Some(align) =
-        Ord::max(cx.tcx.sess.opts.unstable_opts.min_function_alignment, codegen_fn_attrs.alignment)
-    {
+    if let Some(align) = codegen_fn_attrs.alignment {
         llvm::set_alignment(llfn, align);
     }
     if let Some(backchain) = backchain_attr(cx) {
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index ee46b49a094..9c62244f3c9 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -587,7 +587,7 @@ fn thin_lto(
 }
 
 fn enable_autodiff_settings(ad: &[config::AutoDiff]) {
-    for &val in ad {
+    for val in ad {
         // We intentionally don't use a wildcard, to not forget handling anything new.
         match val {
             config::AutoDiff::PrintPerf => {
@@ -599,6 +599,10 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff]) {
             config::AutoDiff::PrintTA => {
                 llvm::set_print_type(true);
             }
+            config::AutoDiff::PrintTAFn(fun) => {
+                llvm::set_print_type(true); // Enable general type printing
+                llvm::set_print_type_fun(&fun); // Set specific function to analyze
+            }
             config::AutoDiff::Inline => {
                 llvm::set_inline(true);
             }
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 5e9594dd06b..d0aa7320b4b 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -1166,11 +1166,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         (self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1))
     }
 
-    fn filter_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) {
+    fn filter_landing_pad(&mut self, pers_fn: &'ll Value) {
         let ty = self.type_struct(&[self.type_ptr(), self.type_i32()], false);
         let landing_pad = self.landing_pad(ty, pers_fn, 1);
         self.add_clause(landing_pad, self.const_array(self.type_ptr(), &[]));
-        (self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1))
     }
 
     fn resume(&mut self, exn0: &'ll Value, exn1: &'ll Value) {
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 8bc74fbec7e..d50ad8a1a9c 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -3,36 +3,12 @@ use std::path::Path;
 
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::Diagnostic;
 use rustc_span::Span;
 
 use crate::fluent_generated as fluent;
 
 #[derive(Diagnostic)]
-#[diag(codegen_llvm_unknown_ctarget_feature_prefix)]
-#[note]
-pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
-    pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_unknown_ctarget_feature)]
-#[note]
-pub(crate) struct UnknownCTargetFeature<'a> {
-    pub feature: &'a str,
-    #[subdiagnostic]
-    pub rust_feature: PossibleFeature<'a>,
-}
-
-#[derive(Subdiagnostic)]
-pub(crate) enum PossibleFeature<'a> {
-    #[help(codegen_llvm_possible_feature)]
-    Some { rust_feature: &'a str },
-    #[help(codegen_llvm_consider_filing_feature_request)]
-    None,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_llvm_symbol_already_defined)]
 pub(crate) struct SymbolAlreadyDefined<'a> {
     #[primary_span]
diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs
index 2ad39fc8538..b94716b89d6 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs
@@ -57,14 +57,19 @@ pub(crate) use self::Enzyme_AD::*;
 
 #[cfg(llvm_enzyme)]
 pub(crate) mod Enzyme_AD {
+    use std::ffi::{CString, c_char};
+
     use libc::c_void;
+
     unsafe extern "C" {
         pub(crate) fn EnzymeSetCLBool(arg1: *mut ::std::os::raw::c_void, arg2: u8);
+        pub(crate) fn EnzymeSetCLString(arg1: *mut ::std::os::raw::c_void, arg2: *const c_char);
     }
     unsafe extern "C" {
         static mut EnzymePrintPerf: c_void;
         static mut EnzymePrintActivity: c_void;
         static mut EnzymePrintType: c_void;
+        static mut EnzymeFunctionToAnalyze: c_void;
         static mut EnzymePrint: c_void;
         static mut EnzymeStrictAliasing: c_void;
         static mut looseTypeAnalysis: c_void;
@@ -86,6 +91,15 @@ pub(crate) mod Enzyme_AD {
             EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintType), print as u8);
         }
     }
+    pub(crate) fn set_print_type_fun(fun_name: &str) {
+        let c_fun_name = CString::new(fun_name).unwrap();
+        unsafe {
+            EnzymeSetCLString(
+                std::ptr::addr_of_mut!(EnzymeFunctionToAnalyze),
+                c_fun_name.as_ptr() as *const c_char,
+            );
+        }
+    }
     pub(crate) fn set_print(print: bool) {
         unsafe {
             EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrint), print as u8);
@@ -132,6 +146,9 @@ pub(crate) mod Fallback_AD {
     pub(crate) fn set_print_type(print: bool) {
         unimplemented!()
     }
+    pub(crate) fn set_print_type_fun(fun_name: &str) {
+        unimplemented!()
+    }
     pub(crate) fn set_print(print: bool) {
         unimplemented!()
     }
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 0e77bc43df8..6fd07d562af 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -6,27 +6,20 @@ use std::sync::Once;
 use std::{ptr, slice, str};
 
 use libc::c_int;
-use rustc_codegen_ssa::TargetConfig;
 use rustc_codegen_ssa::base::wants_wasm_eh;
-use rustc_codegen_ssa::codegen_attrs::check_tied_features;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_codegen_ssa::target_features::cfg_target_feature;
+use rustc_codegen_ssa::{TargetConfig, target_features};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_data_structures::unord::UnordSet;
 use rustc_fs_util::path_to_c_string;
 use rustc_middle::bug;
 use rustc_session::Session;
 use rustc_session::config::{PrintKind, PrintRequest};
-use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
-use rustc_span::Symbol;
 use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
-use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
 use smallvec::{SmallVec, smallvec};
 
 use crate::back::write::create_informational_target_machine;
-use crate::errors::{
-    FixedX18InvalidArch, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
-};
-use crate::llvm;
+use crate::{errors, llvm};
 
 static INIT: Once = Once::new();
 
@@ -195,15 +188,6 @@ impl<'a> LLVMFeature<'a> {
     ) -> Self {
         Self { llvm_feature_name, dependencies }
     }
-
-    fn contains(&'a self, feat: &str) -> bool {
-        self.iter().any(|dep| dep == feat)
-    }
-
-    fn iter(&'a self) -> impl Iterator<Item = &'a str> {
-        let dependencies = self.dependencies.iter().map(|feat| feat.as_str());
-        std::iter::once(self.llvm_feature_name).chain(dependencies)
-    }
 }
 
 impl<'a> IntoIterator for LLVMFeature<'a> {
@@ -216,18 +200,22 @@ impl<'a> IntoIterator for LLVMFeature<'a> {
     }
 }
 
-// WARNING: the features after applying `to_llvm_features` must be known
-// to LLVM or the feature detection code will walk past the end of the feature
-// array, leading to crashes.
-//
-// To find a list of LLVM's names, see llvm-project/llvm/lib/Target/{ARCH}/*.td
-// where `{ARCH}` is the architecture name. Look for instances of `SubtargetFeature`.
-//
-// Check the current rustc fork of LLVM in the repo at https://github.com/rust-lang/llvm-project/.
-// The commit in use can be found via the `llvm-project` submodule in
-// https://github.com/rust-lang/rust/tree/master/src Though note that Rust can also be build with
-// an external precompiled version of LLVM which might lead to failures if the oldest tested /
-// supported LLVM version doesn't yet support the relevant intrinsics.
+/// Convert a Rust feature name to an LLVM feature name. Returning `None` means the
+/// feature should be skipped, usually because it is not supported by the current
+/// LLVM version.
+///
+/// WARNING: the features after applying `to_llvm_features` must be known
+/// to LLVM or the feature detection code will walk past the end of the feature
+/// array, leading to crashes.
+///
+/// To find a list of LLVM's names, see llvm-project/llvm/lib/Target/{ARCH}/*.td
+/// where `{ARCH}` is the architecture name. Look for instances of `SubtargetFeature`.
+///
+/// Check the current rustc fork of LLVM in the repo at
+/// <https://github.com/rust-lang/llvm-project/>. The commit in use can be found via the
+/// `llvm-project` submodule in <https://github.com/rust-lang/rust/tree/master/src> Though note that
+/// Rust can also be build with an external precompiled version of LLVM which might lead to failures
+/// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics.
 pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFeature<'a>> {
     let arch = if sess.target.arch == "x86_64" {
         "x86"
@@ -343,98 +331,25 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig {
     // the target CPU, that is still expanded to target features (with all their implied features)
     // by LLVM.
     let target_machine = create_informational_target_machine(sess, true);
-    // Compute which of the known target features are enabled in the 'base' target machine. We only
-    // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
-    let mut features: FxHashSet<Symbol> = sess
-        .target
-        .rust_target_features()
-        .iter()
-        .filter(|(feature, _, _)| {
-            // skip checking special features, as LLVM may not understand them
-            if RUSTC_SPECIAL_FEATURES.contains(feature) {
-                return true;
-            }
-            if let Some(feat) = to_llvm_features(sess, feature) {
-                for llvm_feature in feat {
-                    let cstr = SmallCStr::new(llvm_feature);
-                    // `LLVMRustHasFeature` is moderately expensive. On targets with many
-                    // features (e.g. x86) these calls take a non-trivial fraction of runtime
-                    // when compiling very small programs.
-                    if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } {
-                        return false;
-                    }
+
+    let (unstable_target_features, target_features) = cfg_target_feature(sess, |feature| {
+        if let Some(feat) = to_llvm_features(sess, feature) {
+            // All the LLVM features this expands to must be enabled.
+            for llvm_feature in feat {
+                let cstr = SmallCStr::new(llvm_feature);
+                // `LLVMRustHasFeature` is moderately expensive. On targets with many
+                // features (e.g. x86) these calls take a non-trivial fraction of runtime
+                // when compiling very small programs.
+                if !unsafe { llvm::LLVMRustHasFeature(target_machine.raw(), cstr.as_ptr()) } {
+                    return false;
                 }
-                true
-            } else {
-                false
             }
-        })
-        .map(|(feature, _, _)| Symbol::intern(feature))
-        .collect();
-
-    // Add enabled and remove disabled features.
-    for (enabled, feature) in
-        sess.opts.cg.target_feature.split(',').filter_map(|s| match s.chars().next() {
-            Some('+') => Some((true, Symbol::intern(&s[1..]))),
-            Some('-') => Some((false, Symbol::intern(&s[1..]))),
-            _ => None,
-        })
-    {
-        if enabled {
-            // Also add all transitively implied features.
-
-            // We don't care about the order in `features` since the only thing we use it for is the
-            // `features.contains` below.
-            #[allow(rustc::potential_query_instability)]
-            features.extend(
-                sess.target
-                    .implied_target_features(feature.as_str())
-                    .iter()
-                    .map(|s| Symbol::intern(s)),
-            );
+            true
         } else {
-            // Remove transitively reverse-implied features.
-
-            // We don't care about the order in `features` since the only thing we use it for is the
-            // `features.contains` below.
-            #[allow(rustc::potential_query_instability)]
-            features.retain(|f| {
-                if sess.target.implied_target_features(f.as_str()).contains(&feature.as_str()) {
-                    // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to
-                    // remove `f`. (This is the standard logical contraposition principle.)
-                    false
-                } else {
-                    // We can keep `f`.
-                    true
-                }
-            });
+            false
         }
-    }
-
-    // Filter enabled features based on feature gates.
-    let f = |allow_unstable| {
-        sess.target
-            .rust_target_features()
-            .iter()
-            .filter_map(|(feature, gate, _)| {
-                // The `allow_unstable` set is used by rustc internally to determined which target
-                // features are truly available, so we want to return even perma-unstable
-                // "forbidden" features.
-                if allow_unstable
-                    || (gate.in_cfg()
-                        && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
-                {
-                    Some(Symbol::intern(feature))
-                } else {
-                    None
-                }
-            })
-            .filter(|feature| features.contains(&feature))
-            .collect()
-    };
+    });
 
-    let target_features = f(false);
-    let unstable_target_features = f(true);
     let mut cfg = TargetConfig {
         target_features,
         unstable_target_features,
@@ -707,10 +622,18 @@ pub(crate) fn target_cpu(sess: &Session) -> &str {
     handle_native(cpu_name)
 }
 
-fn llvm_features_by_flags(sess: &Session) -> Vec<&str> {
-    let mut features: Vec<&str> = Vec::new();
-    retpoline_features_by_flags(sess, &mut features);
-    features
+/// The target features for compiler flags other than `-Ctarget-features`.
+fn llvm_features_by_flags(sess: &Session, features: &mut Vec<String>) {
+    target_features::retpoline_features_by_flags(sess, features);
+
+    // -Zfixed-x18
+    if sess.opts.unstable_opts.fixed_x18 {
+        if sess.target.arch != "aarch64" {
+            sess.dcx().emit_fatal(errors::FixedX18InvalidArch { arch: &sess.target.arch });
+        } else {
+            features.push("+reserve-x18".into());
+        }
+    }
 }
 
 /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
@@ -777,6 +700,8 @@ pub(crate) fn global_llvm_features(
             .split(',')
             .filter(|v| !v.is_empty())
             // Drop +v8plus feature introduced in LLVM 20.
+            // (Hard-coded target features do not go through `to_llvm_feature` since they already
+            // are LLVM feature names, hence we need a special case here.)
             .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0))
             .map(String::from),
     );
@@ -787,86 +712,23 @@ pub(crate) fn global_llvm_features(
 
     // -Ctarget-features
     if !only_base_features {
-        let known_features = sess.target.rust_target_features();
-        // Will only be filled when `diagnostics` is set!
-        let mut featsmap = FxHashMap::default();
-
-        // Compute implied features
-        let mut all_rust_features = vec![];
-        for feature in sess.opts.cg.target_feature.split(',').chain(llvm_features_by_flags(sess)) {
-            if let Some(feature) = feature.strip_prefix('+') {
-                all_rust_features.extend(
-                    UnordSet::from(sess.target.implied_target_features(feature))
-                        .to_sorted_stable_ord()
-                        .iter()
-                        .map(|&&s| (true, s)),
-                )
-            } else if let Some(feature) = feature.strip_prefix('-') {
-                // FIXME: Why do we not remove implied features on "-" here?
-                // We do the equivalent above in `target_config`.
-                // See <https://github.com/rust-lang/rust/issues/134792>.
-                all_rust_features.push((false, feature));
-            } else if !feature.is_empty() {
-                if diagnostics {
-                    sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
-                }
-            }
-        }
-        // Remove features that are meant for rustc, not LLVM.
-        all_rust_features.retain(|(_, feature)| {
-            // Retain if it is not a rustc feature
-            !RUSTC_SPECIFIC_FEATURES.contains(feature)
-        });
-
-        // Check feature validity.
-        if diagnostics {
-            for &(enable, feature) in &all_rust_features {
-                let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
-                match feature_state {
-                    None => {
-                        let rust_feature =
-                            known_features.iter().find_map(|&(rust_feature, _, _)| {
-                                let llvm_features = to_llvm_features(sess, rust_feature)?;
-                                if llvm_features.contains(feature)
-                                    && !llvm_features.contains(rust_feature)
-                                {
-                                    Some(rust_feature)
-                                } else {
-                                    None
-                                }
-                            });
-                        let unknown_feature = if let Some(rust_feature) = rust_feature {
-                            UnknownCTargetFeature {
-                                feature,
-                                rust_feature: PossibleFeature::Some { rust_feature },
-                            }
-                        } else {
-                            UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
-                        };
-                        sess.dcx().emit_warn(unknown_feature);
-                    }
-                    Some((_, stability, _)) => {
-                        stability.verify_feature_enabled_by_flag(sess, enable, feature);
-                    }
-                }
-
-                // FIXME(nagisa): figure out how to not allocate a full hashset here.
-                featsmap.insert(feature, enable);
-            }
-        }
-
-        // Translate this into LLVM features.
-        let feats = all_rust_features
-            .iter()
-            .filter_map(|&(enable, feature)| {
+        target_features::flag_to_backend_features(
+            sess,
+            diagnostics,
+            |feature| {
+                to_llvm_features(sess, feature)
+                    .map(|f| SmallVec::<[&str; 2]>::from_iter(f.into_iter()))
+                    .unwrap_or_default()
+            },
+            |feature, enable| {
                 let enable_disable = if enable { '+' } else { '-' };
                 // We run through `to_llvm_features` when
                 // passing requests down to LLVM. This means that all in-language
                 // features also work on the command line instead of having two
                 // different names when the LLVM name and the Rust name differ.
-                let llvm_feature = to_llvm_features(sess, feature)?;
+                let Some(llvm_feature) = to_llvm_features(sess, feature) else { return };
 
-                Some(
+                features.extend(
                     std::iter::once(format!(
                         "{}{}",
                         enable_disable, llvm_feature.llvm_feature_name
@@ -881,27 +743,12 @@ pub(crate) fn global_llvm_features(
                         },
                     )),
                 )
-            })
-            .flatten();
-        features.extend(feats);
-
-        if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
-            sess.dcx().emit_err(rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable {
-                features: f,
-                span: None,
-                missing_features: None,
-            });
-        }
+            },
+        );
     }
 
-    // -Zfixed-x18
-    if sess.opts.unstable_opts.fixed_x18 {
-        if sess.target.arch != "aarch64" {
-            sess.dcx().emit_fatal(FixedX18InvalidArch { arch: &sess.target.arch });
-        } else {
-            features.push("+reserve-x18".into());
-        }
-    }
+    // We add this in the "base target" so that these show up in `sess.unstable_target_features`.
+    llvm_features_by_flags(sess, &mut features);
 
     features
 }
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 5322fe58cf3..b2e86414d90 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -48,8 +48,6 @@ codegen_ssa_error_writing_def_file =
 
 codegen_ssa_expected_name_value_pair = expected name value pair
 
-codegen_ssa_expected_one_argument = expected one argument
-
 codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
 
 codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
@@ -68,6 +66,11 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error}
 
 codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
 
+codegen_ssa_forbidden_ctarget_feature =
+    target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
+    .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+codegen_ssa_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
 codegen_ssa_forbidden_target_feature_attr =
     target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason}
 
@@ -86,9 +89,6 @@ codegen_ssa_incorrect_cgu_reuse_type =
 
 codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient.
 
-codegen_ssa_invalid_argument = invalid argument
-    .help = valid inline arguments are `always` and `never`
-
 codegen_ssa_invalid_instruction_set = invalid instruction set specified
 
 codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]`
@@ -221,6 +221,8 @@ codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple time
 
 codegen_ssa_no_field = no field `{$name}`
 
+codegen_ssa_no_mangle_nameless = `#[no_mangle]` cannot be used on {$definition} as it has no name
+
 codegen_ssa_no_module_named =
     no module named `{$user_path}` (mangled: {$cgu_name}). available modules: {$cgu_names}
 
@@ -368,8 +370,22 @@ codegen_ssa_unexpected_parameter_name = unexpected parameter name
 codegen_ssa_unknown_archive_kind =
     Don't know how to build archive of type: {$kind}
 
+codegen_ssa_unknown_ctarget_feature =
+    unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
+    .possible_feature = you might have meant: `{$rust_feature}`
+    .consider_filing_feature_request = consider filing a feature request
+
+codegen_ssa_unknown_ctarget_feature_prefix =
+    unknown feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = features must begin with a `+` to enable or `-` to disable it
+
 codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified
 
+codegen_ssa_unstable_ctarget_feature =
+    unstable feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = this feature is not stably supported; its behavior can change in the future
+
 codegen_ssa_unsupported_instruction_set = target does not support `#[instruction_set]`
 
 codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 8882ba359b7..4a2425967e4 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -865,7 +865,7 @@ fn link_natively(
                     command: cmd,
                     escaped_output,
                     verbose: sess.opts.verbose,
-                    sysroot_dir: sess.sysroot.clone(),
+                    sysroot_dir: sess.opts.sysroot.path().to_owned(),
                 };
                 sess.dcx().emit_err(err);
                 // If MSVC's `link.exe` was expected but the return code
@@ -1249,10 +1249,10 @@ fn link_sanitizer_runtime(
         if path.exists() {
             sess.target_tlib_path.dir.clone()
         } else {
-            let default_sysroot = filesearch::get_or_default_sysroot();
-            let default_tlib =
-                filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.tuple());
-            default_tlib
+            filesearch::make_target_lib_path(
+                &sess.opts.sysroot.default,
+                sess.opts.target_triple.tuple(),
+            )
         }
     }
 
@@ -1758,7 +1758,7 @@ fn detect_self_contained_mingw(sess: &Session, linker: &Path) -> bool {
     for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
         let full_path = dir.join(&linker_with_extension);
         // If linker comes from sysroot assume self-contained mode
-        if full_path.is_file() && !full_path.starts_with(&sess.sysroot) {
+        if full_path.is_file() && !full_path.starts_with(sess.opts.sysroot.path()) {
             return false;
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 8fc83908efb..cd08c0fc30f 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -337,7 +337,12 @@ pub(crate) trait Linker {
     fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]);
     fn no_crt_objects(&mut self);
     fn no_default_libraries(&mut self);
-    fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
+    fn export_symbols(
+        &mut self,
+        tmpdir: &Path,
+        crate_type: CrateType,
+        symbols: &[(String, SymbolExportKind)],
+    );
     fn subsystem(&mut self, subsystem: &str);
     fn linker_plugin_lto(&mut self);
     fn add_eh_frame_header(&mut self) {}
@@ -770,7 +775,12 @@ impl<'a> Linker for GccLinker<'a> {
         }
     }
 
-    fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) {
+    fn export_symbols(
+        &mut self,
+        tmpdir: &Path,
+        crate_type: CrateType,
+        symbols: &[(String, SymbolExportKind)],
+    ) {
         // Symbol visibility in object files typically takes care of this.
         if crate_type == CrateType::Executable {
             let should_export_executable_symbols =
@@ -799,7 +809,7 @@ impl<'a> Linker for GccLinker<'a> {
             // Write a plain, newline-separated list of symbols
             let res: io::Result<()> = try {
                 let mut f = File::create_buffered(&path)?;
-                for sym in symbols {
+                for (sym, _) in symbols {
                     debug!("  _{sym}");
                     writeln!(f, "_{sym}")?;
                 }
@@ -814,11 +824,12 @@ impl<'a> Linker for GccLinker<'a> {
                 // .def file similar to MSVC one but without LIBRARY section
                 // because LD doesn't like when it's empty
                 writeln!(f, "EXPORTS")?;
-                for symbol in symbols {
+                for (symbol, kind) in symbols {
+                    let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" };
                     debug!("  _{symbol}");
                     // Quote the name in case it's reserved by linker in some way
                     // (this accounts for names with dots in particular).
-                    writeln!(f, "  \"{symbol}\"")?;
+                    writeln!(f, "  \"{symbol}\"{kind_marker}")?;
                 }
             };
             if let Err(error) = res {
@@ -831,7 +842,7 @@ impl<'a> Linker for GccLinker<'a> {
                 writeln!(f, "{{")?;
                 if !symbols.is_empty() {
                     writeln!(f, "  global:")?;
-                    for sym in symbols {
+                    for (sym, _) in symbols {
                         debug!("    {sym};");
                         writeln!(f, "    {sym};")?;
                     }
@@ -1059,7 +1070,7 @@ impl<'a> Linker for MsvcLinker<'a> {
         self.link_arg("/PDBALTPATH:%_PDB%");
 
         // This will cause the Microsoft linker to embed .natvis info into the PDB file
-        let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc");
+        let natvis_dir_path = self.sess.opts.sysroot.path().join("lib\\rustlib\\etc");
         if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) {
             for entry in natvis_dir {
                 match entry {
@@ -1098,7 +1109,12 @@ impl<'a> Linker for MsvcLinker<'a> {
     // crates. Upstream rlibs may be linked statically to this dynamic library,
     // in which case they may continue to transitively be used and hence need
     // their symbols exported.
-    fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) {
+    fn export_symbols(
+        &mut self,
+        tmpdir: &Path,
+        crate_type: CrateType,
+        symbols: &[(String, SymbolExportKind)],
+    ) {
         // Symbol visibility takes care of this typically
         if crate_type == CrateType::Executable {
             let should_export_executable_symbols =
@@ -1116,9 +1132,10 @@ impl<'a> Linker for MsvcLinker<'a> {
             // straight to exports.
             writeln!(f, "LIBRARY")?;
             writeln!(f, "EXPORTS")?;
-            for symbol in symbols {
+            for (symbol, kind) in symbols {
+                let kind_marker = if *kind == SymbolExportKind::Data { " DATA" } else { "" };
                 debug!("  _{symbol}");
-                writeln!(f, "  {symbol}")?;
+                writeln!(f, "  {symbol}{kind_marker}")?;
             }
         };
         if let Err(error) = res {
@@ -1259,14 +1276,19 @@ impl<'a> Linker for EmLinker<'a> {
         self.cc_arg("-nodefaultlibs");
     }
 
-    fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
+    fn export_symbols(
+        &mut self,
+        _tmpdir: &Path,
+        _crate_type: CrateType,
+        symbols: &[(String, SymbolExportKind)],
+    ) {
         debug!("EXPORTED SYMBOLS:");
 
         self.cc_arg("-s");
 
         let mut arg = OsString::from("EXPORTED_FUNCTIONS=");
         let encoded = serde_json::to_string(
-            &symbols.iter().map(|sym| "_".to_owned() + sym).collect::<Vec<_>>(),
+            &symbols.iter().map(|(sym, _)| "_".to_owned() + sym).collect::<Vec<_>>(),
         )
         .unwrap();
         debug!("{encoded}");
@@ -1428,8 +1450,13 @@ impl<'a> Linker for WasmLd<'a> {
 
     fn no_default_libraries(&mut self) {}
 
-    fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
-        for sym in symbols {
+    fn export_symbols(
+        &mut self,
+        _tmpdir: &Path,
+        _crate_type: CrateType,
+        symbols: &[(String, SymbolExportKind)],
+    ) {
+        for (sym, _) in symbols {
             self.link_args(&["--export", sym]);
         }
 
@@ -1563,7 +1590,7 @@ impl<'a> Linker for L4Bender<'a> {
         self.cc_arg("-nostdlib");
     }
 
-    fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) {
+    fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[(String, SymbolExportKind)]) {
         // ToDo, not implemented, copy from GCC
         self.sess.dcx().emit_warn(errors::L4BenderExportingSymbolsUnimplemented);
     }
@@ -1720,12 +1747,17 @@ impl<'a> Linker for AixLinker<'a> {
 
     fn no_default_libraries(&mut self) {}
 
-    fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
+    fn export_symbols(
+        &mut self,
+        tmpdir: &Path,
+        _crate_type: CrateType,
+        symbols: &[(String, SymbolExportKind)],
+    ) {
         let path = tmpdir.join("list.exp");
         let res: io::Result<()> = try {
             let mut f = File::create_buffered(&path)?;
             // FIXME: use llvm-nm to generate export list.
-            for symbol in symbols {
+            for (symbol, _) in symbols {
                 debug!("  _{symbol}");
                 writeln!(f, "  {symbol}")?;
             }
@@ -1769,9 +1801,23 @@ fn for_each_exported_symbols_include_dep<'tcx>(
     }
 }
 
-pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
+pub(crate) fn exported_symbols(
+    tcx: TyCtxt<'_>,
+    crate_type: CrateType,
+) -> Vec<(String, SymbolExportKind)> {
     if let Some(ref exports) = tcx.sess.target.override_export_symbols {
-        return exports.iter().map(ToString::to_string).collect();
+        return exports
+            .iter()
+            .map(|name| {
+                (
+                    name.to_string(),
+                    // FIXME use the correct export kind for this symbol. override_export_symbols
+                    // can't directly specify the SymbolExportKind as it is defined in rustc_middle
+                    // which rustc_target can't depend on.
+                    SymbolExportKind::Text,
+                )
+            })
+            .collect();
     }
 
     if let CrateType::ProcMacro = crate_type {
@@ -1781,7 +1827,10 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
     }
 }
 
-fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
+fn exported_symbols_for_non_proc_macro(
+    tcx: TyCtxt<'_>,
+    crate_type: CrateType,
+) -> Vec<(String, SymbolExportKind)> {
     let mut symbols = Vec::new();
     let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
     for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
@@ -1789,8 +1838,9 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -
         // from any cdylib. The latter doesn't work anyway as we use hidden visibility for
         // compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning.
         if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) {
-            symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate(
-                tcx, symbol, cnum,
+            symbols.push((
+                symbol_export::exporting_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
+                info.kind,
             ));
             symbol_export::extend_exported_symbols(&mut symbols, tcx, symbol, cnum);
         }
@@ -1799,7 +1849,7 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -
     symbols
 }
 
-fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
+fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<(String, SymbolExportKind)> {
     // `exported_symbols` will be empty when !should_codegen.
     if !tcx.sess.opts.output_types.should_codegen() {
         return Vec::new();
@@ -1809,7 +1859,10 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
     let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
     let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx);
 
-    vec![proc_macro_decls_name, metadata_symbol_name]
+    vec![
+        (proc_macro_decls_name, SymbolExportKind::Data),
+        (metadata_symbol_name, SymbolExportKind::Data),
+    ]
 }
 
 pub(crate) fn linked_symbols(
@@ -1831,7 +1884,9 @@ pub(crate) fn linked_symbols(
             || info.used
         {
             symbols.push((
-                symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
+                symbol_export::linking_symbol_name_for_instance_in_crate(
+                    tcx, symbol, info.kind, cnum,
+                ),
                 info.kind,
             ));
         }
@@ -1906,7 +1961,13 @@ impl<'a> Linker for PtxLinker<'a> {
 
     fn ehcont_guard(&mut self) {}
 
-    fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, _symbols: &[String]) {}
+    fn export_symbols(
+        &mut self,
+        _tmpdir: &Path,
+        _crate_type: CrateType,
+        _symbols: &[(String, SymbolExportKind)],
+    ) {
+    }
 
     fn subsystem(&mut self, _subsystem: &str) {}
 
@@ -1975,10 +2036,15 @@ impl<'a> Linker for LlbcLinker<'a> {
 
     fn ehcont_guard(&mut self) {}
 
-    fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
+    fn export_symbols(
+        &mut self,
+        _tmpdir: &Path,
+        _crate_type: CrateType,
+        symbols: &[(String, SymbolExportKind)],
+    ) {
         match _crate_type {
             CrateType::Cdylib => {
-                for sym in symbols {
+                for (sym, _) in symbols {
                     self.link_args(&["--export-symbol", sym]);
                 }
             }
@@ -2052,11 +2118,16 @@ impl<'a> Linker for BpfLinker<'a> {
 
     fn ehcont_guard(&mut self) {}
 
-    fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
+    fn export_symbols(
+        &mut self,
+        tmpdir: &Path,
+        _crate_type: CrateType,
+        symbols: &[(String, SymbolExportKind)],
+    ) {
         let path = tmpdir.join("symbols");
         let res: io::Result<()> = try {
             let mut f = File::create_buffered(&path)?;
-            for sym in symbols {
+            for (sym, _) in symbols {
                 writeln!(f, "{sym}")?;
             }
         };
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index d0b6c7470fb..19c005d418e 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -680,6 +680,7 @@ fn calling_convention_for_symbol<'tcx>(
 pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>(
     tcx: TyCtxt<'tcx>,
     symbol: ExportedSymbol<'tcx>,
+    export_kind: SymbolExportKind,
     instantiating_crate: CrateNum,
 ) -> String {
     let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
@@ -700,8 +701,9 @@ pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>(
     let prefix = match &target.arch[..] {
         "x86" => Some('_'),
         "x86_64" => None,
-        "arm64ec" => Some('#'),
-        // Only x86/64 use symbol decorations.
+        // Only functions are decorated for arm64ec.
+        "arm64ec" if export_kind == SymbolExportKind::Text => Some('#'),
+        // Only x86/64 and arm64ec use symbol decorations.
         _ => return undecorated,
     };
 
@@ -741,7 +743,7 @@ pub(crate) fn exporting_symbol_name_for_instance_in_crate<'tcx>(
 /// Add it to the symbols list for all kernel functions, so that it is exported in the linked
 /// object.
 pub(crate) fn extend_exported_symbols<'tcx>(
-    symbols: &mut Vec<String>,
+    symbols: &mut Vec<(String, SymbolExportKind)>,
     tcx: TyCtxt<'tcx>,
     symbol: ExportedSymbol<'tcx>,
     instantiating_crate: CrateNum,
@@ -755,7 +757,9 @@ pub(crate) fn extend_exported_symbols<'tcx>(
     let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
 
     // Add the symbol for the kernel descriptor (with .kd suffix)
-    symbols.push(format!("{undecorated}.kd"));
+    // Per https://llvm.org/docs/AMDGPUUsage.html#symbols these will always be `STT_OBJECT` so
+    // export as data.
+    symbols.push((format!("{undecorated}.kd"), SymbolExportKind::Data));
 }
 
 fn maybe_emutls_symbol_name<'tcx>(
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index bbf9cceef2a..c3bfe4c13cd 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -14,10 +14,10 @@ use rustc_data_structures::jobserver::{self, Acquired};
 use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
 use rustc_errors::emitter::Emitter;
-use rustc_errors::translation::Translate;
+use rustc_errors::translation::Translator;
 use rustc_errors::{
-    Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FluentBundle, Level, MultiSpan,
-    Style, Suggestions,
+    Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, Level, MultiSpan, Style,
+    Suggestions,
 };
 use rustc_fs_util::link_or_copy;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
@@ -1889,16 +1889,6 @@ impl SharedEmitter {
     }
 }
 
-impl Translate for SharedEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        None
-    }
-
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        panic!("shared emitter attempted to translate a diagnostic");
-    }
-}
-
 impl Emitter for SharedEmitter {
     fn emit_diagnostic(
         &mut self,
@@ -1932,6 +1922,10 @@ impl Emitter for SharedEmitter {
     fn source_map(&self) -> Option<&SourceMap> {
         None
     }
+
+    fn translator(&self) -> &Translator {
+        panic!("shared emitter attempted to translate a diagnostic");
+    }
 }
 
 impl SharedEmitterMain {
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index a3d6c73ba85..cc90271cd0c 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -12,9 +12,9 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
 use rustc_data_structures::sync::{IntoDynSyncSend, par_map};
 use rustc_data_structures::unord::UnordMap;
-use rustc_hir::ItemId;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::lang_items::LangItem;
+use rustc_hir::{ItemId, Target};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType};
 use rustc_middle::middle::exported_symbols::{self, SymbolExportKind};
@@ -1003,21 +1003,35 @@ impl CrateInfo {
         // by the compiler, but that's ok because all this stuff is unstable anyway.
         let target = &tcx.sess.target;
         if !are_upstream_rust_objects_already_included(tcx.sess) {
-            let missing_weak_lang_items: FxIndexSet<Symbol> = info
+            let add_prefix = match (target.is_like_windows, target.arch.as_ref()) {
+                (true, "x86") => |name: String, _: SymbolExportKind| format!("_{name}"),
+                (true, "arm64ec") => {
+                    // Only functions are decorated for arm64ec.
+                    |name: String, export_kind: SymbolExportKind| match export_kind {
+                        SymbolExportKind::Text => format!("#{name}"),
+                        _ => name,
+                    }
+                }
+                _ => |name: String, _: SymbolExportKind| name,
+            };
+            let missing_weak_lang_items: FxIndexSet<(Symbol, SymbolExportKind)> = info
                 .used_crates
                 .iter()
                 .flat_map(|&cnum| tcx.missing_lang_items(cnum))
                 .filter(|l| l.is_weak())
                 .filter_map(|&l| {
                     let name = l.link_name()?;
-                    lang_items::required(tcx, l).then_some(name)
+                    let export_kind = match l.target() {
+                        Target::Fn => SymbolExportKind::Text,
+                        Target::Static => SymbolExportKind::Data,
+                        _ => bug!(
+                            "Don't know what the export kind is for lang item of kind {:?}",
+                            l.target()
+                        ),
+                    };
+                    lang_items::required(tcx, l).then_some((name, export_kind))
                 })
                 .collect();
-            let prefix = match (target.is_like_windows, target.arch.as_ref()) {
-                (true, "x86") => "_",
-                (true, "arm64ec") => "#",
-                _ => "",
-            };
 
             // This loop only adds new items to values of the hash map, so the order in which we
             // iterate over the values is not important.
@@ -1030,10 +1044,13 @@ impl CrateInfo {
                 .for_each(|(_, linked_symbols)| {
                     let mut symbols = missing_weak_lang_items
                         .iter()
-                        .map(|item| {
+                        .map(|(item, export_kind)| {
                             (
-                                format!("{prefix}{}", mangle_internal_symbol(tcx, item.as_str())),
-                                SymbolExportKind::Text,
+                                add_prefix(
+                                    mangle_internal_symbol(tcx, item.as_str()),
+                                    *export_kind,
+                                ),
+                                *export_kind,
                             )
                         })
                         .collect::<Vec<_>>();
@@ -1048,12 +1065,12 @@ impl CrateInfo {
                         // errors.
                         linked_symbols.extend(ALLOCATOR_METHODS.iter().map(|method| {
                             (
-                                format!(
-                                    "{prefix}{}",
+                                add_prefix(
                                     mangle_internal_symbol(
                                         tcx,
-                                        global_fn_name(method.name).as_str()
-                                    )
+                                        global_fn_name(method.name).as_str(),
+                                    ),
+                                    SymbolExportKind::Text,
                                 ),
                                 SymbolExportKind::Text,
                             )
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 188a9a98ce7..7bd27eb3ef1 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -3,11 +3,9 @@ use std::str::FromStr;
 use rustc_abi::ExternAbi;
 use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
 use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
-use rustc_attr_data_structures::ReprAttr::ReprAlign;
 use rustc_attr_data_structures::{
-    AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, find_attr,
+    AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, ReprAttr, find_attr,
 };
-use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
@@ -19,13 +17,16 @@ use rustc_middle::mir::mono::Linkage;
 use rustc_middle::query::Providers;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self as ty, TyCtxt};
+use rustc_session::lint;
 use rustc_session::parse::feature_err;
-use rustc_session::{Session, lint};
 use rustc_span::{Ident, Span, sym};
 use rustc_target::spec::SanitizerSet;
 
 use crate::errors;
-use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
+use crate::errors::NoMangleNameless;
+use crate::target_features::{
+    check_target_feature_trait_unsafe, check_tied_features, from_target_feature_attr,
+};
 
 fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
     use rustc_middle::mir::mono::Linkage::*;
@@ -87,7 +88,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     let mut link_ordinal_span = None;
     let mut no_sanitize_span = None;
     let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
-    let mut no_mangle_span = None;
 
     for attr in attrs.iter() {
         // In some cases, attribute are only valid on functions, but it's the `check_attr`
@@ -95,17 +95,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         // In these cases, we bail from performing further checks that are only meaningful for
         // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
         // report a delayed bug, just in case `check_attr` isn't doing its job.
-        let fn_sig = || {
+        let fn_sig = |attr_span| {
             use DefKind::*;
 
             let def_kind = tcx.def_kind(did);
             if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
                 Some(tcx.fn_sig(did))
             } else {
-                tcx.dcx().span_delayed_bug(
-                    attr.span(),
-                    "this attribute can only be applied to functions",
-                );
+                tcx.dcx()
+                    .span_delayed_bug(attr_span, "this attribute can only be applied to functions");
                 None
             }
         };
@@ -115,10 +113,56 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                 AttributeKind::Repr(reprs) => {
                     codegen_fn_attrs.alignment = reprs
                         .iter()
-                        .filter_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None })
+                        .filter_map(
+                            |(r, _)| if let ReprAttr::ReprAlign(x) = r { Some(*x) } else { None },
+                        )
                         .max();
                 }
-
+                AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
+                AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
+                AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
+                AttributeKind::NoMangle(attr_span) => {
+                    if tcx.opt_item_name(did.to_def_id()).is_some() {
+                        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
+                        mixed_export_name_no_mangle_lint_state.track_no_mangle(
+                            *attr_span,
+                            tcx.local_def_id_to_hir_id(did),
+                            attr,
+                        );
+                    } else {
+                        tcx.dcx().emit_err(NoMangleNameless {
+                            span: *attr_span,
+                            definition: format!(
+                                "{} {}",
+                                tcx.def_descr_article(did.to_def_id()),
+                                tcx.def_descr(did.to_def_id())
+                            ),
+                        });
+                    }
+                }
+                AttributeKind::TrackCaller(attr_span) => {
+                    let is_closure = tcx.is_closure_like(did.to_def_id());
+
+                    if !is_closure
+                        && let Some(fn_sig) = fn_sig(*attr_span)
+                        && fn_sig.skip_binder().abi() != ExternAbi::Rust
+                    {
+                        tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
+                    }
+                    if is_closure
+                        && !tcx.features().closure_track_caller()
+                        && !attr_span.allows_unstable(sym::closure_track_caller)
+                    {
+                        feature_err(
+                            &tcx.sess,
+                            sym::closure_track_caller,
+                            *attr_span,
+                            "`#[track_caller]` on closures is currently unstable",
+                        )
+                        .emit();
+                    }
+                    codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
+                }
                 _ => {}
             }
         }
@@ -128,7 +172,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         };
 
         match name {
-            sym::cold => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
             sym::rustc_allocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR,
             sym::ffi_pure => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
             sym::ffi_const => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
@@ -138,29 +181,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             sym::rustc_allocator_zeroed => {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
             }
-            sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
-            sym::no_mangle => {
-                no_mangle_span = Some(attr.span());
-                if tcx.opt_item_name(did.to_def_id()).is_some() {
-                    codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
-                    mixed_export_name_no_mangle_lint_state.track_no_mangle(
-                        attr.span(),
-                        tcx.local_def_id_to_hir_id(did),
-                        attr,
-                    );
-                } else {
-                    tcx.dcx()
-                        .struct_span_err(
-                            attr.span(),
-                            format!(
-                                "`#[no_mangle]` cannot be used on {} {} as it has no name",
-                                tcx.def_descr_article(did.to_def_id()),
-                                tcx.def_descr(did.to_def_id()),
-                            ),
-                        )
-                        .emit();
-                }
-            }
             sym::rustc_std_internal_symbol => {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
             }
@@ -203,29 +223,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                 }
             }
             sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
-            sym::track_caller => {
-                let is_closure = tcx.is_closure_like(did.to_def_id());
-
-                if !is_closure
-                    && let Some(fn_sig) = fn_sig()
-                    && fn_sig.skip_binder().abi() != ExternAbi::Rust
-                {
-                    tcx.dcx().emit_err(errors::RequiresRustAbi { span: attr.span() });
-                }
-                if is_closure
-                    && !tcx.features().closure_track_caller()
-                    && !attr.span().allows_unstable(sym::closure_track_caller)
-                {
-                    feature_err(
-                        &tcx.sess,
-                        sym::closure_track_caller,
-                        attr.span(),
-                        "`#[track_caller]` on closures is currently unstable",
-                    )
-                    .emit();
-                }
-                codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
-            }
             sym::export_name => {
                 if let Some(s) = attr.value_str() {
                     if s.as_str().contains('\0') {
@@ -449,6 +446,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
 
     mixed_export_name_no_mangle_lint_state.lint_if_mixed(tcx);
 
+    // Apply the minimum function alignment here, so that individual backends don't have to.
+    codegen_fn_attrs.alignment =
+        Ord::max(codegen_fn_attrs.alignment, tcx.sess.opts.unstable_opts.min_function_alignment);
+
     let inline_span;
     (codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) =
         find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span))
@@ -465,33 +466,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         codegen_fn_attrs.inline = InlineAttr::Never;
     }
 
-    codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| {
-        if !attr.has_name(sym::optimize) {
-            return ia;
-        }
-        if attr.is_word() {
-            tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
-            return ia;
-        }
-        let Some(ref items) = attr.meta_item_list() else {
-            return OptimizeAttr::Default;
-        };
-
-        let [item] = &items[..] else {
-            tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() });
-            return OptimizeAttr::Default;
-        };
-        if item.has_name(sym::size) {
-            OptimizeAttr::Size
-        } else if item.has_name(sym::speed) {
-            OptimizeAttr::Speed
-        } else if item.has_name(sym::none) {
-            OptimizeAttr::DoNotOptimize
-        } else {
-            tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() });
-            OptimizeAttr::Default
-        }
-    });
+    codegen_fn_attrs.optimize =
+        find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default);
 
     // #73631: closures inherit `#[target_feature]` annotations
     //
@@ -567,12 +543,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
         && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
     {
+        let no_mangle_span =
+            find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span)
+                .unwrap_or_default();
         let lang_item =
             lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
         let mut err = tcx
             .dcx()
             .struct_span_err(
-                no_mangle_span.unwrap_or_default(),
+                no_mangle_span,
                 "`#[no_mangle]` cannot be used on internal language items",
             )
             .with_note("Rustc requires this item to have a specific mangled name.")
@@ -625,25 +604,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     codegen_fn_attrs
 }
 
-/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
-/// combinations are allowed.
-pub fn check_tied_features(
-    sess: &Session,
-    features: &FxHashMap<&str, bool>,
-) -> Option<&'static [&'static str]> {
-    if !features.is_empty() {
-        for tied in sess.target.tied_target_features() {
-            // Tied features must be set to the same value, or not set at all
-            let mut tied_iter = tied.iter();
-            let enabled = features.get(tied_iter.next().unwrap());
-            if tied_iter.any(|f| enabled != features.get(f)) {
-                return Some(tied);
-            }
-        }
-    }
-    None
-}
-
 /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
 /// applied to the method prototype.
 fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 5387b2a7f81..caac0f83f9d 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -209,20 +209,6 @@ pub(crate) struct OutOfRangeInteger {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_ssa_expected_one_argument, code = E0722)]
-pub(crate) struct ExpectedOneArgumentOptimize {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_ssa_invalid_argument, code = E0722)]
-pub(crate) struct InvalidArgumentOptimize {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_ssa_copy_path_buf)]
 pub(crate) struct CopyPathBuf {
     pub source_file: PathBuf,
@@ -1217,30 +1203,6 @@ pub(crate) struct ErrorCreatingImportLibrary<'a> {
     pub error: String,
 }
 
-pub struct TargetFeatureDisableOrEnable<'a> {
-    pub features: &'a [&'a str],
-    pub span: Option<Span>,
-    pub missing_features: Option<MissingFeatures>,
-}
-
-#[derive(Subdiagnostic)]
-#[help(codegen_ssa_missing_features)]
-pub struct MissingFeatures;
-
-impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
-        let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable);
-        if let Some(span) = self.span {
-            diag.span(span);
-        };
-        if let Some(missing_features) = self.missing_features {
-            diag.subdiagnostic(missing_features);
-        }
-        diag.arg("features", self.features.join(", "));
-        diag
-    }
-}
-
 #[derive(Diagnostic)]
 #[diag(codegen_ssa_aix_strip_not_used)]
 pub(crate) struct AixStripNotUsed;
@@ -1283,3 +1245,76 @@ pub(crate) struct XcrunSdkPathWarning {
 #[derive(LintDiagnostic)]
 #[diag(codegen_ssa_aarch64_softfloat_neon)]
 pub(crate) struct Aarch64SoftfloatNeon;
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unknown_ctarget_feature_prefix)]
+#[note]
+pub(crate) struct UnknownCTargetFeaturePrefix<'a> {
+    pub feature: &'a str,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum PossibleFeature<'a> {
+    #[help(codegen_ssa_possible_feature)]
+    Some { rust_feature: &'a str },
+    #[help(codegen_ssa_consider_filing_feature_request)]
+    None,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unknown_ctarget_feature)]
+#[note]
+pub(crate) struct UnknownCTargetFeature<'a> {
+    pub feature: &'a str,
+    #[subdiagnostic]
+    pub rust_feature: PossibleFeature<'a>,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unstable_ctarget_feature)]
+#[note]
+pub(crate) struct UnstableCTargetFeature<'a> {
+    pub feature: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_forbidden_ctarget_feature)]
+#[note]
+#[note(codegen_ssa_forbidden_ctarget_feature_issue)]
+pub(crate) struct ForbiddenCTargetFeature<'a> {
+    pub feature: &'a str,
+    pub enabled: &'a str,
+    pub reason: &'a str,
+}
+
+pub struct TargetFeatureDisableOrEnable<'a> {
+    pub features: &'a [&'a str],
+    pub span: Option<Span>,
+    pub missing_features: Option<MissingFeatures>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(codegen_ssa_missing_features)]
+pub struct MissingFeatures;
+
+impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
+        let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_target_feature_disable_or_enable);
+        if let Some(span) = self.span {
+            diag.span(span);
+        };
+        if let Some(missing_features) = self.missing_features {
+            diag.subdiagnostic(missing_features);
+        }
+        diag.arg("features", self.features.join(", "));
+        diag
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_no_mangle_nameless)]
+pub(crate) struct NoMangleNameless {
+    #[primary_span]
+    pub span: Span,
+    pub definition: String,
+}
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 523c9f2ad1c..23ed387a3ff 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -218,7 +218,7 @@ pub struct CrateInfo {
     pub target_cpu: String,
     pub target_features: Vec<String>,
     pub crate_types: Vec<CrateType>,
-    pub exported_symbols: UnordMap<CrateType, Vec<String>>,
+    pub exported_symbols: UnordMap<CrateType, Vec<(String, SymbolExportKind)>>,
     pub linked_symbols: FxIndexMap<CrateType, Vec<(String, SymbolExportKind)>>,
     pub local_crate_name: Symbol,
     pub compiler_builtins: Option<CrateNum>,
diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
index 9f66457a740..9da4b8cc8fd 100644
--- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs
@@ -131,12 +131,8 @@ fn prefix_and_suffix<'tcx>(
     let attrs = tcx.codegen_fn_attrs(instance.def_id());
     let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
 
-    // function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
-    // the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
-    // if no alignment is specified, an alignment of 4 bytes is used.
-    let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment;
-    let align_bytes =
-        Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4);
+    // If no alignment is specified, an alignment of 4 bytes is used.
+    let align_bytes = attrs.alignment.map(|a| a.bytes()).unwrap_or(4);
 
     // In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`.
     let (arch_prefix, arch_suffix) = if is_arm {
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index e1d8b7546cf..db5ac6a514f 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -1123,7 +1123,7 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     // While optimizations will remove no-op transmutes, they might still be
     // there in debug or things that aren't no-op in MIR because they change
     // the Rust type but not the underlying layout/niche.
-    if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
+    if from_scalar == to_scalar {
         return imm;
     }
 
@@ -1142,7 +1142,13 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
 
     imm = match (from_scalar.primitive(), to_scalar.primitive()) {
-        (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
+        (Int(..) | Float(_), Int(..) | Float(_)) => {
+            if from_backend_ty == to_backend_ty {
+                imm
+            } else {
+                bx.bitcast(imm, to_backend_ty)
+            }
+        }
         (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
         (Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
         (Pointer(..), Int(..)) => {
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 640d197c219..67ac619091b 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -1,5 +1,5 @@
 use rustc_attr_data_structures::InstructionSetAttr;
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -8,11 +8,12 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::features::StabilityExt;
+use rustc_session::Session;
 use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
 use rustc_session::parse::feature_err;
 use rustc_span::{Span, Symbol, sym};
-use rustc_target::target_features::{self, Stability};
+use rustc_target::target_features::{self, RUSTC_SPECIFIC_FEATURES, Stability};
+use smallvec::SmallVec;
 
 use crate::errors;
 
@@ -67,7 +68,7 @@ pub(crate) fn from_target_feature_attr(
 
             // Only allow target features whose feature gates have been enabled
             // and which are permitted to be toggled.
-            if let Err(reason) = stability.is_toggle_permitted(tcx.sess) {
+            if let Err(reason) = stability.toggle_allowed() {
                 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
                     span: item.span(),
                     feature,
@@ -88,7 +89,7 @@ pub(crate) fn from_target_feature_attr(
                 let feature_sym = Symbol::intern(feature);
                 for &name in tcx.implied_target_features(feature_sym) {
                     // But ensure the ABI does not forbid enabling this.
-                    // Here we do assume that LLVM doesn't add even more implied features
+                    // Here we do assume that the backend doesn't add even more implied features
                     // we don't know about, at least no features that would have ABI effects!
                     // We skip this logic in rustdoc, where we want to allow all target features of
                     // all targets, so we can't check their ABI compatibility and anyway we are not
@@ -156,6 +157,276 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId,
     }
 }
 
+/// Parse the value of `-Ctarget-feature`, also expanding implied features,
+/// and call the closure for each (expanded) Rust feature. If the list contains
+/// a syntactically invalid item (not starting with `+`/`-`), the error callback is invoked.
+fn parse_rust_feature_flag<'a>(
+    sess: &'a Session,
+    err_callback: impl Fn(&'a str),
+    mut callback: impl FnMut(
+        /* base_feature */ &'a str,
+        /* with_implied */ FxHashSet<&'a str>,
+        /* enable */ bool,
+    ),
+) {
+    // A cache for the backwards implication map.
+    let mut inverse_implied_features: Option<FxHashMap<&str, FxHashSet<&str>>> = None;
+
+    for feature in sess.opts.cg.target_feature.split(',') {
+        if let Some(base_feature) = feature.strip_prefix('+') {
+            // Skip features that are not target features, but rustc features.
+            if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
+                return;
+            }
+
+            callback(base_feature, sess.target.implied_target_features(base_feature), true)
+        } else if let Some(base_feature) = feature.strip_prefix('-') {
+            // Skip features that are not target features, but rustc features.
+            if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
+                return;
+            }
+
+            // If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical
+            // contraposition. So we have to find all the reverse implications of `base_feature` and
+            // disable them, too.
+
+            let inverse_implied_features = inverse_implied_features.get_or_insert_with(|| {
+                let mut set: FxHashMap<&str, FxHashSet<&str>> = FxHashMap::default();
+                for (f, _, is) in sess.target.rust_target_features() {
+                    for i in is.iter() {
+                        set.entry(i).or_default().insert(f);
+                    }
+                }
+                set
+            });
+
+            // Inverse implied target features have their own inverse implied target features, so we
+            // traverse the map until there are no more features to add.
+            let mut features = FxHashSet::default();
+            let mut new_features = vec![base_feature];
+            while let Some(new_feature) = new_features.pop() {
+                if features.insert(new_feature) {
+                    if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
+                        new_features.extend(implied_features)
+                    }
+                }
+            }
+
+            callback(base_feature, features, false)
+        } else if !feature.is_empty() {
+            err_callback(feature)
+        }
+    }
+}
+
+/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
+/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
+/// 2nd component of the return value, respectively).
+///
+/// `target_base_has_feature` should check whether the given feature (a Rust feature name!) is
+/// enabled in the "base" target machine, i.e., without applying `-Ctarget-feature`.
+///
+/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere.
+pub fn cfg_target_feature(
+    sess: &Session,
+    mut target_base_has_feature: impl FnMut(&str) -> bool,
+) -> (Vec<Symbol>, Vec<Symbol>) {
+    // Compute which of the known target features are enabled in the 'base' target machine. We only
+    // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
+    let mut features: UnordSet<Symbol> = sess
+        .target
+        .rust_target_features()
+        .iter()
+        .filter(|(feature, _, _)| target_base_has_feature(feature))
+        .map(|(feature, _, _)| Symbol::intern(feature))
+        .collect();
+
+    // Add enabled and remove disabled features.
+    parse_rust_feature_flag(
+        sess,
+        /* err_callback */
+        |_| {
+            // Errors are already emitted in `flag_to_backend_features`; avoid duplicates.
+        },
+        |_base_feature, new_features, enabled| {
+            // Iteration order is irrelevant since this only influences an `UnordSet`.
+            #[allow(rustc::potential_query_instability)]
+            if enabled {
+                features.extend(new_features.into_iter().map(|f| Symbol::intern(f)));
+            } else {
+                // Remove `new_features` from `features`.
+                for new in new_features {
+                    features.remove(&Symbol::intern(new));
+                }
+            }
+        },
+    );
+
+    // Filter enabled features based on feature gates.
+    let f = |allow_unstable| {
+        sess.target
+            .rust_target_features()
+            .iter()
+            .filter_map(|(feature, gate, _)| {
+                // The `allow_unstable` set is used by rustc internally to determine which target
+                // features are truly available, so we want to return even perma-unstable
+                // "forbidden" features.
+                if allow_unstable
+                    || (gate.in_cfg()
+                        && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
+                {
+                    Some(Symbol::intern(feature))
+                } else {
+                    None
+                }
+            })
+            .filter(|feature| features.contains(&feature))
+            .collect()
+    };
+
+    (f(true), f(false))
+}
+
+/// Given a map from target_features to whether they are enabled or disabled, ensure only valid
+/// combinations are allowed.
+pub fn check_tied_features(
+    sess: &Session,
+    features: &FxHashMap<&str, bool>,
+) -> Option<&'static [&'static str]> {
+    if !features.is_empty() {
+        for tied in sess.target.tied_target_features() {
+            // Tied features must be set to the same value, or not set at all
+            let mut tied_iter = tied.iter();
+            let enabled = features.get(tied_iter.next().unwrap());
+            if tied_iter.any(|f| enabled != features.get(f)) {
+                return Some(tied);
+            }
+        }
+    }
+    None
+}
+
+/// Translates the `-Ctarget-feature` flag into a backend target feature list.
+///
+/// `to_backend_features` converts a Rust feature name into a list of backend feature names; this is
+/// used for diagnostic purposes only.
+///
+/// `extend_backend_features` extends the set of backend features (assumed to be in mutable state
+/// accessible by that closure) to enable/disable the given Rust feature name.
+pub fn flag_to_backend_features<'a, const N: usize>(
+    sess: &'a Session,
+    diagnostics: bool,
+    to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>,
+    mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool),
+) {
+    let known_features = sess.target.rust_target_features();
+
+    // Compute implied features
+    let mut rust_features = vec![];
+    parse_rust_feature_flag(
+        sess,
+        /* err_callback */
+        |feature| {
+            if diagnostics {
+                sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature });
+            }
+        },
+        |base_feature, new_features, enable| {
+            rust_features.extend(
+                UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)),
+            );
+            // Check feature validity.
+            if diagnostics {
+                let feature_state = known_features.iter().find(|&&(v, _, _)| v == base_feature);
+                match feature_state {
+                    None => {
+                        // This is definitely not a valid Rust feature name. Maybe it is a backend
+                        // feature name? If so, give a better error message.
+                        let rust_feature =
+                            known_features.iter().find_map(|&(rust_feature, _, _)| {
+                                let backend_features = to_backend_features(rust_feature);
+                                if backend_features.contains(&base_feature)
+                                    && !backend_features.contains(&rust_feature)
+                                {
+                                    Some(rust_feature)
+                                } else {
+                                    None
+                                }
+                            });
+                        let unknown_feature = if let Some(rust_feature) = rust_feature {
+                            errors::UnknownCTargetFeature {
+                                feature: base_feature,
+                                rust_feature: errors::PossibleFeature::Some { rust_feature },
+                            }
+                        } else {
+                            errors::UnknownCTargetFeature {
+                                feature: base_feature,
+                                rust_feature: errors::PossibleFeature::None,
+                            }
+                        };
+                        sess.dcx().emit_warn(unknown_feature);
+                    }
+                    Some((_, stability, _)) => {
+                        if let Err(reason) = stability.toggle_allowed() {
+                            sess.dcx().emit_warn(errors::ForbiddenCTargetFeature {
+                                feature: base_feature,
+                                enabled: if enable { "enabled" } else { "disabled" },
+                                reason,
+                            });
+                        } else if stability.requires_nightly().is_some() {
+                            // An unstable feature. Warn about using it. It makes little sense
+                            // to hard-error here since we just warn about fully unknown
+                            // features above.
+                            sess.dcx().emit_warn(errors::UnstableCTargetFeature {
+                                feature: base_feature,
+                            });
+                        }
+                    }
+                }
+            }
+        },
+    );
+
+    if diagnostics {
+        // FIXME(nagisa): figure out how to not allocate a full hashmap here.
+        if let Some(f) = check_tied_features(
+            sess,
+            &FxHashMap::from_iter(rust_features.iter().map(|&(enable, feature)| (feature, enable))),
+        ) {
+            sess.dcx().emit_err(errors::TargetFeatureDisableOrEnable {
+                features: f,
+                span: None,
+                missing_features: None,
+            });
+        }
+    }
+
+    // Add this to the backend features.
+    for (enable, feature) in rust_features {
+        extend_backend_features(feature, enable);
+    }
+}
+
+/// Computes the backend target features to be added to account for retpoline flags.
+/// Used by both LLVM and GCC since their target features are, conveniently, the same.
+pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<String>) {
+    // -Zretpoline without -Zretpoline-external-thunk enables
+    // retpoline-indirect-branches and retpoline-indirect-calls target features
+    let unstable_opts = &sess.opts.unstable_opts;
+    if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
+        features.push("+retpoline-indirect-branches".into());
+        features.push("+retpoline-indirect-calls".into());
+    }
+    // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables
+    // retpoline-external-thunk, retpoline-indirect-branches and
+    // retpoline-indirect-calls target features
+    if unstable_opts.retpoline_external_thunk {
+        features.push("+retpoline-external-thunk".into());
+        features.push("+retpoline-indirect-branches".into());
+        features.push("+retpoline-indirect-calls".into());
+    }
+}
+
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         rust_target_features: |tcx, cnum| {
@@ -182,7 +453,8 @@ pub(crate) fn provide(providers: &mut Providers) {
                                     Stability::Unstable { .. } | Stability::Forbidden { .. },
                                 )
                                 | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
-                                    // The stability in the entry is at least as good as the new one, just keep it.
+                                    // The stability in the entry is at least as good as the new
+                                    // one, just keep it.
                                 }
                                 _ => {
                                     // Overwrite stabilite.
diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs
index f35f551d590..d19de6f5d26 100644
--- a/compiler/rustc_codegen_ssa/src/traits/builder.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs
@@ -516,7 +516,7 @@ pub trait BuilderMethods<'a, 'tcx>:
 
     // These are used by everyone except msvc
     fn cleanup_landing_pad(&mut self, pers_fn: Self::Function) -> (Self::Value, Self::Value);
-    fn filter_landing_pad(&mut self, pers_fn: Self::Function) -> (Self::Value, Self::Value);
+    fn filter_landing_pad(&mut self, pers_fn: Self::Function);
     fn resume(&mut self, exn0: Self::Value, exn1: Self::Value);
 
     // These are used only by msvc
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 4f252f3ccd4..576b174369d 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -463,12 +463,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
         );
     }
 
-    fn crate_inject_span(&self) -> Option<Span> {
-        self.tcx.hir_crate_items(()).definitions().next().and_then(|id| {
-            self.tcx.crate_level_attribute_injection_span(self.tcx.local_def_id_to_hir_id(id))
-        })
-    }
-
     /// Check the const stability of the given item (fn or trait).
     fn check_callee_stability(&mut self, def_id: DefId) {
         match self.tcx.lookup_const_stability(def_id) {
@@ -543,7 +537,6 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
                         feature,
                         feature_enabled,
                         safe_to_expose_on_stable: callee_safe_to_expose_on_stable,
-                        suggestion_span: self.crate_inject_span(),
                         is_function_call: self.tcx.def_kind(def_id) != DefKind::Trait,
                     });
                 }
@@ -919,7 +912,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                                 name: intrinsic.name,
                                 feature,
                                 const_stable_indirect: is_const_stable,
-                                suggestion: self.crate_inject_span(),
                             });
                         }
                         Some(attrs::ConstStability {
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index 9c30dbff99e..f5b7a6066c8 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -1,8 +1,8 @@
 //! Concrete error types for all operations which may be invalid in a certain const context.
 
 use hir::{ConstContext, LangItem};
+use rustc_errors::Diag;
 use rustc_errors::codes::*;
-use rustc_errors::{Applicability, Diag};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::TyCtxtInferExt;
@@ -384,7 +384,6 @@ pub(crate) struct CallUnstable {
     /// expose on stable.
     pub feature_enabled: bool,
     pub safe_to_expose_on_stable: bool,
-    pub suggestion_span: Option<Span>,
     /// true if `def_id` is the function we are calling, false if `def_id` is an unstable trait.
     pub is_function_call: bool,
 }
@@ -412,20 +411,7 @@ impl<'tcx> NonConstOp<'tcx> for CallUnstable {
                 def_path: ccx.tcx.def_path_str(self.def_id),
             })
         };
-        // FIXME: make this translatable
-        let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature);
-        #[allow(rustc::untranslatable_diagnostic)]
-        if let Some(span) = self.suggestion_span {
-            err.span_suggestion_verbose(
-                span,
-                msg,
-                format!("#![feature({})]\n", self.feature),
-                Applicability::MachineApplicable,
-            );
-        } else {
-            err.help(msg);
-        }
-
+        ccx.tcx.disabled_nightly_features(&mut err, [(String::new(), self.feature)]);
         err
     }
 }
@@ -452,7 +438,6 @@ pub(crate) struct IntrinsicUnstable {
     pub name: Symbol,
     pub feature: Symbol,
     pub const_stable_indirect: bool,
-    pub suggestion: Option<Span>,
 }
 
 impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
@@ -472,8 +457,7 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
             span,
             name: self.name,
             feature: self.feature,
-            suggestion: self.suggestion,
-            help: self.suggestion.is_none(),
+            suggestion: ccx.tcx.crate_level_attribute_injection_span(),
         })
     }
 }
@@ -616,11 +600,13 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
                 kind: ccx.const_kind(),
                 teach: ccx.tcx.sess.teach(E0764),
             }),
-            hir::BorrowKind::Ref => ccx.dcx().create_err(errors::MutableRefEscaping {
-                span,
-                kind: ccx.const_kind(),
-                teach: ccx.tcx.sess.teach(E0764),
-            }),
+            hir::BorrowKind::Ref | hir::BorrowKind::Pin => {
+                ccx.dcx().create_err(errors::MutableRefEscaping {
+                    span,
+                    kind: ccx.const_kind(),
+                    teach: ccx.tcx.sess.teach(E0764),
+                })
+            }
         }
     }
 }
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 037cbf777e7..609749554ce 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -136,9 +136,7 @@ pub(crate) struct UnstableIntrinsic {
         code = "#![feature({feature})]\n",
         applicability = "machine-applicable"
     )]
-    pub suggestion: Option<Span>,
-    #[help(const_eval_unstable_intrinsic_suggestion)]
-    pub help: bool,
+    pub suggestion: Span,
 }
 
 #[derive(Diagnostic)]
@@ -293,6 +291,9 @@ impl Subdiagnostic for FrameNote {
             span.push_span_label(self.span, fluent::const_eval_frame_note_last);
         }
         let msg = diag.eagerly_translate(fluent::const_eval_frame_note);
+        diag.remove_arg("times");
+        diag.remove_arg("where_");
+        diag.remove_arg("instance");
         diag.span_note(span, msg);
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 99a4bc1b7d6..57bf867e389 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -877,12 +877,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         if let Some(fn_val) = self.get_fn_alloc(id) {
             let align = match fn_val {
                 FnVal::Instance(instance) => {
-                    // Function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
-                    // the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
-                    let fn_align = self.tcx.codegen_fn_attrs(instance.def_id()).alignment;
-                    let global_align = self.tcx.sess.opts.unstable_opts.min_function_alignment;
-
-                    Ord::max(global_align, fn_align).unwrap_or(Align::ONE)
+                    self.tcx.codegen_fn_attrs(instance.def_id()).alignment.unwrap_or(Align::ONE)
                 }
                 // Machine-specific extra functions currently do not support alignment restrictions.
                 FnVal::Other(_) => Align::ONE,
@@ -1412,8 +1407,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         let src_alloc = self.get_alloc_raw(src_alloc_id)?;
         let src_range = alloc_range(src_offset, size);
         assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation");
-        // For the overlapping case, it is crucial that we trigger the read hook
+
+        // Trigger read hooks.
+        // For the overlapping case, it is crucial that we trigger the read hooks
         // before the write hook -- the aliasing model cares about the order.
+        if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(src, size.bytes() as i64) {
+            M::before_alloc_read(self, alloc_id)?;
+        }
         M::before_memory_read(
             tcx,
             &self.machine,
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index f6a02011618..17204883fb0 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -14,7 +14,6 @@ indexmap = "2.4.0"
 jobserver_crate = { version = "0.1.28", package = "jobserver" }
 measureme = "12.0.1"
 rustc-hash = "2.0.0"
-rustc-rayon-core = { version = "0.5.0" }
 rustc-stable-hash = { version = "0.1.0", features = ["nightly"] }
 rustc_arena = { path = "../rustc_arena" }
 rustc_graphviz = { path = "../rustc_graphviz" }
@@ -22,6 +21,7 @@ rustc_hashes = { path = "../rustc_hashes" }
 rustc_index = { path = "../rustc_index", package = "rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
+rustc_thread_pool = { path = "../rustc_thread_pool" }
 smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] }
 stacker = "0.1.17"
 tempfile = "3.2"
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index b28c333d860..3881f3c2aa8 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -22,8 +22,6 @@
 //! |                         |                     | `parking_lot::Mutex<T>`         |
 //! | `RwLock<T>`             | `RefCell<T>`        | `parking_lot::RwLock<T>`        |
 //! | `MTLock<T>`        [^1] | `T`                 | `Lock<T>`                       |
-//! |                         |                     |                                 |
-//! | `ParallelIterator`      | `Iterator`          | `rayon::iter::ParallelIterator` |
 //!
 //! [^1]: `MTLock` is similar to `Lock`, but the serial version avoids the cost
 //! of a `RefCell`. This is appropriate when interior mutability is not
diff --git a/compiler/rustc_data_structures/src/sync/lock.rs b/compiler/rustc_data_structures/src/sync/lock.rs
index 2ccf06ccd4f..a8161c51511 100644
--- a/compiler/rustc_data_structures/src/sync/lock.rs
+++ b/compiler/rustc_data_structures/src/sync/lock.rs
@@ -1,8 +1,6 @@
 //! This module implements a lock which only uses synchronization if `might_be_dyn_thread_safe` is true.
 //! It implements `DynSend` and `DynSync` instead of the typical `Send` and `Sync` traits.
 
-#![allow(dead_code)]
-
 use std::fmt;
 
 #[derive(Clone, Copy, PartialEq)]
diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs
index ab65c7f3a6b..b515c0bee8a 100644
--- a/compiler/rustc_data_structures/src/sync/parallel.rs
+++ b/compiler/rustc_data_structures/src/sync/parallel.rs
@@ -1,8 +1,6 @@
 //! This module defines parallel operations that are implemented in
 //! one way for the serial compiler, and another way the parallel compiler.
 
-#![allow(dead_code)]
-
 use std::any::Any;
 use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
 
@@ -96,7 +94,7 @@ macro_rules! parallel {
 pub fn spawn(func: impl FnOnce() + DynSend + 'static) {
     if mode::is_dyn_thread_safe() {
         let func = FromDyn::from(func);
-        rayon_core::spawn(|| {
+        rustc_thread_pool::spawn(|| {
             (func.into_inner())();
         });
     } else {
@@ -107,11 +105,11 @@ pub fn spawn(func: impl FnOnce() + DynSend + 'static) {
 // This function only works when `mode::is_dyn_thread_safe()`.
 pub fn scope<'scope, OP, R>(op: OP) -> R
 where
-    OP: FnOnce(&rayon_core::Scope<'scope>) -> R + DynSend,
+    OP: FnOnce(&rustc_thread_pool::Scope<'scope>) -> R + DynSend,
     R: DynSend,
 {
     let op = FromDyn::from(op);
-    rayon_core::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner()
+    rustc_thread_pool::scope(|s| FromDyn::from(op.into_inner()(s))).into_inner()
 }
 
 #[inline]
@@ -124,7 +122,7 @@ where
         let oper_a = FromDyn::from(oper_a);
         let oper_b = FromDyn::from(oper_b);
         let (a, b) = parallel_guard(|guard| {
-            rayon_core::join(
+            rustc_thread_pool::join(
                 move || guard.run(move || FromDyn::from(oper_a.into_inner()())),
                 move || guard.run(move || FromDyn::from(oper_b.into_inner()())),
             )
@@ -158,7 +156,7 @@ fn par_slice<I: DynSend>(
             let (left, right) = items.split_at_mut(items.len() / 2);
             let mut left = state.for_each.derive(left);
             let mut right = state.for_each.derive(right);
-            rayon_core::join(move || par_rec(*left, state), move || par_rec(*right, state));
+            rustc_thread_pool::join(move || par_rec(*left, state), move || par_rec(*right, state));
         }
     }
 
@@ -241,7 +239,7 @@ pub fn par_map<I: DynSend, T: IntoIterator<Item = I>, R: DynSend, C: FromIterato
 pub fn broadcast<R: DynSend>(op: impl Fn(usize) -> R + DynSync) -> Vec<R> {
     if mode::is_dyn_thread_safe() {
         let op = FromDyn::from(op);
-        let results = rayon_core::broadcast(|context| op.derive(op(context.index())));
+        let results = rustc_thread_pool::broadcast(|context| op.derive(op(context.index())));
         results.into_iter().map(|r| r.into_inner()).collect()
     } else {
         vec![op(0)]
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index d53126d0414..4855fc58d03 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -38,6 +38,7 @@ use rustc_data_structures::profiling::{
 };
 use rustc_errors::emitter::stderr_destination;
 use rustc_errors::registry::Registry;
+use rustc_errors::translation::Translator;
 use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown};
 use rustc_feature::find_gated_cfg;
 // This avoids a false positive with `-Wunused_crate_dependencies`.
@@ -52,13 +53,13 @@ use rustc_metadata::locator;
 use rustc_middle::ty::TyCtxt;
 use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
 use rustc_session::config::{
-    CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType,
+    CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, Sysroot,
     UnstableOptions, Z_OPTIONS, nightly_options, parse_target_triple,
 };
 use rustc_session::getopts::{self, Matches};
 use rustc_session::lint::{Lint, LintId};
 use rustc_session::output::{CRATE_TYPES, collect_crate_types, invalid_output_for_target};
-use rustc_session::{EarlyDiagCtxt, Session, config, filesearch};
+use rustc_session::{EarlyDiagCtxt, Session, config};
 use rustc_span::FileName;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustc_target::json::ToJson;
@@ -109,6 +110,10 @@ use crate::session_diagnostics::{
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
+pub fn default_translator() -> Translator {
+    Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false)
+}
+
 pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
     // tidy-alphabetical-start
     crate::DEFAULT_LOCALE_RESOURCE,
@@ -657,7 +662,7 @@ fn print_crate_info(
                 println_info!("{}", targets.join("\n"));
             }
             HostTuple => println_info!("{}", rustc_session::config::host_tuple()),
-            Sysroot => println_info!("{}", sess.sysroot.display()),
+            Sysroot => println_info!("{}", sess.opts.sysroot.path().display()),
             TargetLibdir => println_info!("{}", sess.target_tlib_path.dir.display()),
             TargetSpecJson => {
                 println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
@@ -1109,8 +1114,8 @@ fn get_backend_from_raw_matches(
     let debug_flags = matches.opt_strs("Z");
     let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
     let target = parse_target_triple(early_dcx, matches);
-    let sysroot = filesearch::materialize_sysroot(matches.opt_str("sysroot").map(PathBuf::from));
-    let target = config::build_target_config(early_dcx, &target, &sysroot);
+    let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from));
+    let target = config::build_target_config(early_dcx, &target, sysroot.path());
 
     get_codegen_backend(early_dcx, &sysroot, backend_name, &target)
 }
@@ -1413,11 +1418,10 @@ fn report_ice(
     extra_info: fn(&DiagCtxt),
     using_internal_features: &AtomicBool,
 ) {
-    let fallback_bundle =
-        rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
+    let translator = default_translator();
     let emitter = Box::new(rustc_errors::emitter::HumanEmitter::new(
         stderr_destination(rustc_errors::ColorConfig::Auto),
-        fallback_bundle,
+        translator,
     ));
     let dcx = rustc_errors::DiagCtxt::new(emitter);
     let dcx = dcx.handle();
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index ec77043cd12..688307a941f 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -292,7 +292,11 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
         }
         HirTree => {
             debug!("pretty printing HIR tree");
-            format!("{:#?}", ex.tcx().hir_crate(()))
+            ex.tcx()
+                .hir_crate_items(())
+                .owners()
+                .map(|owner| format!("{:#?} => {:#?}\n", owner, ex.tcx().hir_owner_nodes(owner)))
+                .collect()
         }
         Mir => {
             let mut out = Vec::new();
diff --git a/compiler/rustc_error_codes/src/error_codes/E0722.md b/compiler/rustc_error_codes/src/error_codes/E0722.md
index 570717a92bd..1799458d46c 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0722.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0722.md
@@ -1,8 +1,14 @@
+#### Note: this error code is no longer emitted by the compiler
+
+This is because it was too specific to the `optimize` attribute.
+Similar diagnostics occur for other attributes too.
+The example here will now emit `E0539`
+
 The `optimize` attribute was malformed.
 
 Erroneous code example:
 
-```compile_fail,E0722
+```compile_fail,E0539
 #![feature(optimize_attribute)]
 
 #[optimize(something)] // error: invalid argument
diff --git a/compiler/rustc_error_codes/src/error_codes/E0775.md b/compiler/rustc_error_codes/src/error_codes/E0775.md
index efbd51e89ea..9fcd3a6eef7 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0775.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0775.md
@@ -8,7 +8,7 @@ Erroneous code example:
 ```ignore (no longer emitted)
 #![feature(cmse_nonsecure_entry)]
 
-pub extern "C-cmse-nonsecure-entry" fn entry_function() {}
+pub extern "cmse-nonsecure-entry" fn entry_function() {}
 ```
 
 To fix this error, compile your code for a Rust target that supports the
diff --git a/compiler/rustc_error_codes/src/error_codes/E0781.md b/compiler/rustc_error_codes/src/error_codes/E0781.md
index 7641acfb524..22abe0e3cb1 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0781.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0781.md
@@ -1,12 +1,12 @@
-The `C-cmse-nonsecure-call` ABI can only be used with function pointers.
+The `cmse-nonsecure-call` ABI can only be used with function pointers.
 
 Erroneous code example:
 
 ```compile_fail,E0781
-#![feature(abi_c_cmse_nonsecure_call)]
+#![feature(abi_cmse_nonsecure_call)]
 
-pub extern "C-cmse-nonsecure-call" fn test() {}
+pub extern "cmse-nonsecure-call" fn test() {}
 ```
 
-The `C-cmse-nonsecure-call` ABI should be used by casting function pointers to
+The `cmse-nonsecure-call` ABI should be used by casting function pointers to
 specific addresses.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0798.md b/compiler/rustc_error_codes/src/error_codes/E0798.md
index da08cde3010..e5f356ef4d5 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0798.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0798.md
@@ -1,4 +1,4 @@
-Functions marked as `C-cmse-nonsecure-call` place restrictions on their
+Functions marked as `cmse-nonsecure-call` place restrictions on their
 inputs and outputs.
 
 - inputs must fit in the 4 available 32-bit argument registers. Alignment
@@ -12,12 +12,12 @@ see [arm's aapcs32](https://github.com/ARM-software/abi-aa/releases).
 
 Erroneous code example:
 
-```ignore (only fails on supported targets)
-#![feature(abi_c_cmse_nonsecure_call)]
+```ignore (host errors will not match for target)
+#![feature(abi_cmse_nonsecure_call)]
 
 #[no_mangle]
 pub fn test(
-    f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32,
+    f: extern "cmse-nonsecure-call" fn(u32, u32, u32, u32, u32) -> u32,
 ) -> u32 {
     f(1, 2, 3, 4, 5)
 }
@@ -27,12 +27,12 @@ Arguments' alignment is respected. In the example below, padding is inserted
 so that the `u64` argument is passed in registers r2 and r3. There is then no
 room left for the final `f32` argument
 
-```ignore (only fails on supported targets)
-#![feature(abi_c_cmse_nonsecure_call)]
+```ignore (host errors will not match for target)
+#![feature(abi_cmse_nonsecure_call)]
 
 #[no_mangle]
 pub fn test(
-    f: extern "C-cmse-nonsecure-call" fn(u32, u64, f32) -> u32,
+    f: extern "cmse-nonsecure-call" fn(u32, u64, f32) -> u32,
 ) -> u32 {
     f(1, 2, 3.0)
 }
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index 6f5e4829802..0aff1c06e0a 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -686,8 +686,9 @@ E0805: 0805,
 //  E0707, // multiple elided lifetimes used in arguments of `async fn`
 //  E0709, // multiple different lifetimes used in arguments of `async fn`
 //  E0721, // `await` keyword
+//  E0722, // replaced with a generic attribute input check
 //  E0723, // unstable feature in `const` context
 //  E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
 //  E0744, // merged into E0728
-//  E0776, // Removed; cmse_nonsecure_entry is now `C-cmse-nonsecure-entry`
+//  E0776, // Removed; `#[cmse_nonsecure_entry]` is now `extern "cmse-nonsecure-entry"`
 //  E0796, // unused error code. We use `static_mut_refs` lint instead.
diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml
index 5dc582b9c3a..0951859fa53 100644
--- a/compiler/rustc_error_messages/Cargo.toml
+++ b/compiler/rustc_error_messages/Cargo.toml
@@ -16,7 +16,6 @@ rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 unic-langid = { version = "0.9.0", features = ["macros"] }
 # tidy-alphabetical-end
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 1d3b5b20751..4e4345cfe0f 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -8,7 +8,7 @@
 
 use std::borrow::Cow;
 use std::error::Error;
-use std::path::{Path, PathBuf};
+use std::path::Path;
 use std::sync::{Arc, LazyLock};
 use std::{fmt, fs, io};
 
@@ -18,10 +18,9 @@ pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue};
 use fluent_syntax::parser::ParserError;
 use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker};
 use intl_memoizer::concurrent::IntlLangMemoizer;
-use rustc_data_structures::sync::IntoDynSyncSend;
+use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
 use rustc_macros::{Decodable, Encodable};
 use rustc_span::Span;
-use smallvec::SmallVec;
 use tracing::{instrument, trace};
 pub use unic_langid::{LanguageIdentifier, langid};
 
@@ -107,7 +106,7 @@ impl From<Vec<FluentError>> for TranslationBundleError {
 /// (overriding any conflicting messages).
 #[instrument(level = "trace")]
 pub fn fluent_bundle(
-    sysroot_candidates: SmallVec<[PathBuf; 2]>,
+    sysroot_candidates: &[&Path],
     requested_locale: Option<LanguageIdentifier>,
     additional_ftl_path: Option<&Path>,
     with_directionality_markers: bool,
@@ -141,7 +140,8 @@ pub fn fluent_bundle(
     // If the user requests the default locale then don't try to load anything.
     if let Some(requested_locale) = requested_locale {
         let mut found_resources = false;
-        for mut sysroot in sysroot_candidates {
+        for sysroot in sysroot_candidates {
+            let mut sysroot = sysroot.to_path_buf();
             sysroot.push("share");
             sysroot.push("locale");
             sysroot.push(requested_locale.to_string());
@@ -204,16 +204,16 @@ fn register_functions(bundle: &mut FluentBundle) {
 
 /// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily
 /// evaluated fluent bundle.
-pub type LazyFallbackBundle = Arc<LazyLock<FluentBundle, impl FnOnce() -> FluentBundle>>;
+pub type LazyFallbackBundle =
+    Arc<LazyLock<FluentBundle, Box<dyn FnOnce() -> FluentBundle + DynSend>>>;
 
 /// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
 #[instrument(level = "trace", skip(resources))]
-#[define_opaque(LazyFallbackBundle)]
 pub fn fallback_fluent_bundle(
     resources: Vec<&'static str>,
     with_directionality_markers: bool,
 ) -> LazyFallbackBundle {
-    Arc::new(LazyLock::new(move || {
+    Arc::new(LazyLock::new(Box::new(move || {
         let mut fallback_bundle = new_bundle(vec![langid!("en-US")]);
 
         register_functions(&mut fallback_bundle);
@@ -228,7 +228,7 @@ pub fn fallback_fluent_bundle(
         }
 
         fallback_bundle
-    }))
+    })))
 }
 
 /// Identifier for the Fluent message/attribute corresponding to a diagnostic message.
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index f3aeb8d224b..2eb3c23259f 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -15,17 +15,15 @@ use rustc_span::source_map::SourceMap;
 use crate::emitter::FileWithAnnotatedLines;
 use crate::registry::Registry;
 use crate::snippet::Line;
-use crate::translation::{Translate, to_fluent_args};
+use crate::translation::{Translator, to_fluent_args};
 use crate::{
-    CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, FluentBundle, LazyFallbackBundle,
-    Level, MultiSpan, Style, Subdiag,
+    CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag,
 };
 
 /// Generates diagnostics using annotate-snippet
 pub struct AnnotateSnippetEmitter {
     source_map: Option<Arc<SourceMap>>,
-    fluent_bundle: Option<Arc<FluentBundle>>,
-    fallback_bundle: LazyFallbackBundle,
+    translator: Translator,
 
     /// If true, hides the longer explanation text
     short_message: bool,
@@ -35,16 +33,6 @@ pub struct AnnotateSnippetEmitter {
     macro_backtrace: bool,
 }
 
-impl Translate for AnnotateSnippetEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        self.fluent_bundle.as_deref()
-    }
-
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        &self.fallback_bundle
-    }
-}
-
 impl Emitter for AnnotateSnippetEmitter {
     /// The entry point for the diagnostics generation
     fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
@@ -78,6 +66,10 @@ impl Emitter for AnnotateSnippetEmitter {
     fn should_show_explain(&self) -> bool {
         !self.short_message
     }
+
+    fn translator(&self) -> &Translator {
+        &self.translator
+    }
 }
 
 /// Provides the source string for the given `line` of `file`
@@ -104,19 +96,11 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
 impl AnnotateSnippetEmitter {
     pub fn new(
         source_map: Option<Arc<SourceMap>>,
-        fluent_bundle: Option<Arc<FluentBundle>>,
-        fallback_bundle: LazyFallbackBundle,
+        translator: Translator,
         short_message: bool,
         macro_backtrace: bool,
     ) -> Self {
-        Self {
-            source_map,
-            fluent_bundle,
-            fallback_bundle,
-            short_message,
-            ui_testing: false,
-            macro_backtrace,
-        }
+        Self { source_map, translator, short_message, ui_testing: false, macro_backtrace }
     }
 
     /// Allows to modify `Self` to enable or disable the `ui_testing` flag.
@@ -137,7 +121,7 @@ impl AnnotateSnippetEmitter {
         _children: &[Subdiag],
         _suggestions: &[CodeSuggestion],
     ) {
-        let message = self.translate_messages(messages, args);
+        let message = self.translator.translate_messages(messages, args);
         if let Some(source_map) = &self.source_map {
             // Make sure our primary file comes first
             let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index a11f81b55bb..8da7cdd9358 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -289,6 +289,9 @@ pub struct DiagInner {
     pub suggestions: Suggestions,
     pub args: DiagArgMap,
 
+    // This is used to store args and restore them after a subdiagnostic is rendered.
+    pub reserved_args: DiagArgMap,
+
     /// This is not used for highlighting or rendering any error message. Rather, it can be used
     /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
     /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
@@ -319,6 +322,7 @@ impl DiagInner {
             children: vec![],
             suggestions: Suggestions::Enabled(vec![]),
             args: Default::default(),
+            reserved_args: Default::default(),
             sort_span: DUMMY_SP,
             is_lint: None,
             long_ty_path: None,
@@ -390,7 +394,27 @@ impl DiagInner {
     }
 
     pub(crate) fn arg(&mut self, name: impl Into<DiagArgName>, arg: impl IntoDiagArg) {
-        self.args.insert(name.into(), arg.into_diag_arg(&mut self.long_ty_path));
+        let name = name.into();
+        let value = arg.into_diag_arg(&mut self.long_ty_path);
+        // This assertion is to avoid subdiagnostics overwriting an existing diagnostic arg.
+        debug_assert!(
+            !self.args.contains_key(&name) || self.args.get(&name) == Some(&value),
+            "arg {} already exists",
+            name
+        );
+        self.args.insert(name, value);
+    }
+
+    pub fn remove_arg(&mut self, name: &str) {
+        self.args.swap_remove(name);
+    }
+
+    pub fn store_args(&mut self) {
+        self.reserved_args = self.args.clone();
+    }
+
+    pub fn restore_args(&mut self) {
+        self.args = std::mem::take(&mut self.reserved_args);
     }
 
     /// Fields used for Hash, and PartialEq trait.
@@ -1423,6 +1447,12 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
         self.downgrade_to_delayed_bug();
         self.emit()
     }
+
+    pub fn remove_arg(&mut self, name: &str) {
+        if let Some(diag) = self.diag.as_mut() {
+            diag.remove_arg(name);
+        }
+    }
 }
 
 /// Destructor bomb: every `Diag` must be consumed (emitted, cancelled, etc.)
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 6ab6f96079e..e333de4b660 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -35,10 +35,10 @@ use crate::snippet::{
 };
 use crate::styled_buffer::StyledBuffer;
 use crate::timings::TimingRecord;
-use crate::translation::{Translate, to_fluent_args};
+use crate::translation::{Translator, to_fluent_args};
 use crate::{
-    CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level,
-    MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl,
+    CodeSuggestion, DiagInner, DiagMessage, ErrCode, Level, MultiSpan, Subdiag,
+    SubstitutionHighlight, SuggestionStyle, TerminalUrl,
 };
 
 /// Default column width, used in tests and when terminal dimensions cannot be determined.
@@ -175,7 +175,7 @@ const ANONYMIZED_LINE_NUM: &str = "LL";
 pub type DynEmitter = dyn Emitter + DynSend;
 
 /// Emitter trait for emitting errors and other structured information.
-pub trait Emitter: Translate {
+pub trait Emitter {
     /// Emit a structured diagnostic.
     fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry);
 
@@ -212,6 +212,8 @@ pub trait Emitter: Translate {
 
     fn source_map(&self) -> Option<&SourceMap>;
 
+    fn translator(&self) -> &Translator;
+
     /// Formats the substitutions of the primary_span
     ///
     /// There are a lot of conditions to this method, but in short:
@@ -224,13 +226,17 @@ pub trait Emitter: Translate {
     /// * If the current `DiagInner` has multiple suggestions,
     ///   we leave `primary_span` and the suggestions untouched.
     fn primary_span_formatted(
-        &mut self,
+        &self,
         primary_span: &mut MultiSpan,
         suggestions: &mut Vec<CodeSuggestion>,
         fluent_args: &FluentArgs<'_>,
     ) {
         if let Some((sugg, rest)) = suggestions.split_first() {
-            let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap();
+            let msg = self
+                .translator()
+                .translate_message(&sugg.msg, fluent_args)
+                .map_err(Report::new)
+                .unwrap();
             if rest.is_empty()
                // ^ if there is only one suggestion
                // don't display multi-suggestions as labels
@@ -491,16 +497,6 @@ pub trait Emitter: Translate {
     }
 }
 
-impl Translate for HumanEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        self.fluent_bundle.as_deref()
-    }
-
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        &self.fallback_bundle
-    }
-}
-
 impl Emitter for HumanEmitter {
     fn source_map(&self) -> Option<&SourceMap> {
         self.sm.as_deref()
@@ -538,39 +534,52 @@ impl Emitter for HumanEmitter {
     fn supports_color(&self) -> bool {
         self.dst.supports_color()
     }
+
+    fn translator(&self) -> &Translator {
+        &self.translator
+    }
 }
 
 /// An emitter that does nothing when emitting a non-fatal diagnostic.
 /// Fatal diagnostics are forwarded to `fatal_emitter` to avoid silent
 /// failures of rustc, as witnessed e.g. in issue #89358.
-pub struct SilentEmitter {
+pub struct FatalOnlyEmitter {
     pub fatal_emitter: Box<dyn Emitter + DynSend>,
     pub fatal_note: Option<String>,
-    pub emit_fatal_diagnostic: bool,
 }
 
-impl Translate for SilentEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
+impl Emitter for FatalOnlyEmitter {
+    fn source_map(&self) -> Option<&SourceMap> {
         None
     }
 
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        self.fatal_emitter.fallback_fluent_bundle()
+    fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) {
+        if diag.level == Level::Fatal {
+            if let Some(fatal_note) = &self.fatal_note {
+                diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
+            }
+            self.fatal_emitter.emit_diagnostic(diag, registry);
+        }
+    }
+
+    fn translator(&self) -> &Translator {
+        self.fatal_emitter.translator()
     }
 }
 
+pub struct SilentEmitter {
+    pub translator: Translator,
+}
+
 impl Emitter for SilentEmitter {
     fn source_map(&self) -> Option<&SourceMap> {
         None
     }
 
-    fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) {
-        if self.emit_fatal_diagnostic && diag.level == Level::Fatal {
-            if let Some(fatal_note) = &self.fatal_note {
-                diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
-            }
-            self.fatal_emitter.emit_diagnostic(diag, registry);
-        }
+    fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {}
+
+    fn translator(&self) -> &Translator {
+        &self.translator
     }
 }
 
@@ -615,9 +624,8 @@ pub struct HumanEmitter {
     #[setters(skip)]
     dst: IntoDynSyncSend<Destination>,
     sm: Option<Arc<SourceMap>>,
-    fluent_bundle: Option<Arc<FluentBundle>>,
     #[setters(skip)]
-    fallback_bundle: LazyFallbackBundle,
+    translator: Translator,
     short_message: bool,
     ui_testing: bool,
     ignored_directories_in_source_blocks: Vec<String>,
@@ -637,12 +645,11 @@ pub(crate) struct FileWithAnnotatedLines {
 }
 
 impl HumanEmitter {
-    pub fn new(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter {
+    pub fn new(dst: Destination, translator: Translator) -> HumanEmitter {
         HumanEmitter {
             dst: IntoDynSyncSend(dst),
             sm: None,
-            fluent_bundle: None,
-            fallback_bundle,
+            translator,
             short_message: false,
             ui_testing: false,
             ignored_directories_in_source_blocks: Vec::new(),
@@ -1433,7 +1440,7 @@ impl HumanEmitter {
         //                very *weird* formats
         //                see?
         for (text, style) in msgs.iter() {
-            let text = self.translate_message(text, args).map_err(Report::new).unwrap();
+            let text = self.translator.translate_message(text, args).map_err(Report::new).unwrap();
             let text = &normalize_whitespace(&text);
             let lines = text.split('\n').collect::<Vec<_>>();
             if lines.len() > 1 {
@@ -1528,7 +1535,8 @@ impl HumanEmitter {
             }
             let mut line = 0;
             for (text, style) in msgs.iter() {
-                let text = self.translate_message(text, args).map_err(Report::new).unwrap();
+                let text =
+                    self.translator.translate_message(text, args).map_err(Report::new).unwrap();
                 // Account for newlines to align output to its label.
                 for text in normalize_whitespace(&text).lines() {
                     buffer.append(
@@ -1560,7 +1568,7 @@ impl HumanEmitter {
                     .into_iter()
                     .filter_map(|label| match label.label {
                         Some(msg) if label.is_primary => {
-                            let text = self.translate_message(&msg, args).ok()?;
+                            let text = self.translator.translate_message(&msg, args).ok()?;
                             if !text.trim().is_empty() { Some(text.to_string()) } else { None }
                         }
                         _ => None,
@@ -3104,7 +3112,11 @@ impl FileWithAnnotatedLines {
 
                 let label = label.as_ref().map(|m| {
                     normalize_whitespace(
-                        &emitter.translate_message(m, args).map_err(Report::new).unwrap(),
+                        &emitter
+                            .translator()
+                            .translate_message(m, args)
+                            .map_err(Report::new)
+                            .unwrap(),
                     )
                 });
 
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index d67e2ba2d60..4348610be0a 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -32,11 +32,8 @@ use crate::emitter::{
 };
 use crate::registry::Registry;
 use crate::timings::{TimingRecord, TimingSection};
-use crate::translation::{Translate, to_fluent_args};
-use crate::{
-    CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions,
-    TerminalUrl,
-};
+use crate::translation::{Translator, to_fluent_args};
+use crate::{CodeSuggestion, MultiSpan, SpanLabel, Subdiag, Suggestions, TerminalUrl};
 
 #[cfg(test)]
 mod tests;
@@ -47,9 +44,8 @@ pub struct JsonEmitter {
     dst: IntoDynSyncSend<Box<dyn Write + Send>>,
     #[setters(skip)]
     sm: Option<Arc<SourceMap>>,
-    fluent_bundle: Option<Arc<FluentBundle>>,
     #[setters(skip)]
-    fallback_bundle: LazyFallbackBundle,
+    translator: Translator,
     #[setters(skip)]
     pretty: bool,
     ui_testing: bool,
@@ -67,7 +63,7 @@ impl JsonEmitter {
     pub fn new(
         dst: Box<dyn Write + Send>,
         sm: Option<Arc<SourceMap>>,
-        fallback_bundle: LazyFallbackBundle,
+        translator: Translator,
         pretty: bool,
         json_rendered: HumanReadableErrorType,
         color_config: ColorConfig,
@@ -75,8 +71,7 @@ impl JsonEmitter {
         JsonEmitter {
             dst: IntoDynSyncSend(dst),
             sm,
-            fluent_bundle: None,
-            fallback_bundle,
+            translator,
             pretty,
             ui_testing: false,
             ignored_directories_in_source_blocks: Vec::new(),
@@ -110,16 +105,6 @@ enum EmitTyped<'a> {
     UnusedExtern(UnusedExterns<'a>),
 }
 
-impl Translate for JsonEmitter {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        self.fluent_bundle.as_deref()
-    }
-
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        &self.fallback_bundle
-    }
-}
-
 impl Emitter for JsonEmitter {
     fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) {
         let data = Diagnostic::from_errors_diagnostic(diag, self, registry);
@@ -144,6 +129,7 @@ impl Emitter for JsonEmitter {
         };
         let name = match record.section {
             TimingSection::Linking => "link",
+            TimingSection::Codegen => "codegen",
         };
         let data = SectionTimestamp { name, event, timestamp: record.timestamp };
         let result = self.emit(EmitTyped::SectionTiming(data));
@@ -194,6 +180,10 @@ impl Emitter for JsonEmitter {
     fn should_show_explain(&self) -> bool {
         !self.json_rendered.short()
     }
+
+    fn translator(&self) -> &Translator {
+        &self.translator
+    }
 }
 
 // The following data types are provided just for serialisation.
@@ -324,7 +314,7 @@ impl Diagnostic {
         let args = to_fluent_args(diag.args.iter());
         let sugg_to_diag = |sugg: &CodeSuggestion| {
             let translated_message =
-                je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
+                je.translator.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
             Diagnostic {
                 message: translated_message.to_string(),
                 code: None,
@@ -368,7 +358,7 @@ impl Diagnostic {
             }
         }
 
-        let translated_message = je.translate_messages(&diag.messages, &args);
+        let translated_message = je.translator.translate_messages(&diag.messages, &args);
 
         let code = if let Some(code) = diag.code {
             Some(DiagnosticCode {
@@ -396,10 +386,9 @@ impl Diagnostic {
             ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)),
             ColorConfig::Never => {}
         }
-        HumanEmitter::new(dst, Arc::clone(&je.fallback_bundle))
+        HumanEmitter::new(dst, je.translator.clone())
             .short_message(short)
             .sm(je.sm.clone())
-            .fluent_bundle(je.fluent_bundle.clone())
             .diagnostic_width(je.diagnostic_width)
             .macro_backtrace(je.macro_backtrace)
             .track_diagnostics(je.track_diagnostics)
@@ -430,7 +419,7 @@ impl Diagnostic {
         args: &FluentArgs<'_>,
         je: &JsonEmitter,
     ) -> Diagnostic {
-        let translated_message = je.translate_messages(&subdiag.messages, args);
+        let translated_message = je.translator.translate_messages(&subdiag.messages, args);
         Diagnostic {
             message: translated_message.to_string(),
             code: None,
@@ -454,7 +443,7 @@ impl DiagnosticSpan {
             span.is_primary,
             span.label
                 .as_ref()
-                .map(|m| je.translate_message(m, args).unwrap())
+                .map(|m| je.translator.translate_message(m, args).unwrap())
                 .map(|m| m.to_string()),
             suggestion,
             je,
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index 40973e8e5d8..8cf81f467d8 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -41,14 +41,14 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
     rustc_span::create_default_session_globals_then(|| {
         let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
         sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned());
-        let fallback_bundle =
-            crate::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
+        let translator =
+            Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
 
         let output = Arc::new(Mutex::new(Vec::new()));
         let je = JsonEmitter::new(
             Box::new(Shared { data: output.clone() }),
             Some(sm),
-            fallback_bundle,
+            translator,
             true, // pretty
             HumanReadableErrorType::Short,
             ColorConfig::Never,
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 0bd259366de..207aed8c755 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -748,40 +748,10 @@ impl DiagCtxt {
         Self { inner: Lock::new(DiagCtxtInner::new(emitter)) }
     }
 
-    pub fn make_silent(&self, fatal_note: Option<String>, emit_fatal_diagnostic: bool) {
-        // An empty type that implements `Emitter` to temporarily swap in place of the real one,
-        // which will be used in constructing its replacement.
-        struct FalseEmitter;
-
-        impl Emitter for FalseEmitter {
-            fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) {
-                unimplemented!("false emitter must only used during `make_silent`")
-            }
-
-            fn source_map(&self) -> Option<&SourceMap> {
-                unimplemented!("false emitter must only used during `make_silent`")
-            }
-        }
-
-        impl translation::Translate for FalseEmitter {
-            fn fluent_bundle(&self) -> Option<&FluentBundle> {
-                unimplemented!("false emitter must only used during `make_silent`")
-            }
-
-            fn fallback_fluent_bundle(&self) -> &FluentBundle {
-                unimplemented!("false emitter must only used during `make_silent`")
-            }
-        }
-
+    pub fn make_silent(&self) {
         let mut inner = self.inner.borrow_mut();
-        let mut prev_emitter = Box::new(FalseEmitter) as Box<dyn Emitter + DynSend>;
-        std::mem::swap(&mut inner.emitter, &mut prev_emitter);
-        let new_emitter = Box::new(emitter::SilentEmitter {
-            fatal_emitter: prev_emitter,
-            fatal_note,
-            emit_fatal_diagnostic,
-        });
-        inner.emitter = new_emitter;
+        let translator = inner.emitter.translator().clone();
+        inner.emitter = Box::new(emitter::SilentEmitter { translator });
     }
 
     pub fn set_emitter(&self, emitter: Box<dyn Emitter + DynSend>) {
@@ -1771,7 +1741,12 @@ impl DiagCtxtInner {
         args: impl Iterator<Item = DiagArg<'a>>,
     ) -> String {
         let args = crate::translation::to_fluent_args(args);
-        self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
+        self.emitter
+            .translator()
+            .translate_message(&message, &args)
+            .map_err(Report::new)
+            .unwrap()
+            .to_string()
     }
 
     fn eagerly_translate_for_subdiag(
diff --git a/compiler/rustc_errors/src/tests.rs b/compiler/rustc_errors/src/tests.rs
index 376fd24d57b..34ebac0fde1 100644
--- a/compiler/rustc_errors/src/tests.rs
+++ b/compiler/rustc_errors/src/tests.rs
@@ -1,3 +1,5 @@
+use std::sync::{Arc, LazyLock};
+
 use rustc_data_structures::sync::IntoDynSyncSend;
 use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError};
 use rustc_error_messages::{DiagMessage, langid};
@@ -5,23 +7,9 @@ use rustc_error_messages::{DiagMessage, langid};
 use crate::FluentBundle;
 use crate::error::{TranslateError, TranslateErrorKind};
 use crate::fluent_bundle::*;
-use crate::translation::Translate;
-
-struct Dummy {
-    bundle: FluentBundle,
-}
-
-impl Translate for Dummy {
-    fn fluent_bundle(&self) -> Option<&FluentBundle> {
-        None
-    }
+use crate::translation::Translator;
 
-    fn fallback_fluent_bundle(&self) -> &FluentBundle {
-        &self.bundle
-    }
-}
-
-fn make_dummy(ftl: &'static str) -> Dummy {
+fn make_translator(ftl: &'static str) -> Translator {
     let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string.");
 
     let langid_en = langid!("en-US");
@@ -33,12 +21,15 @@ fn make_dummy(ftl: &'static str) -> Dummy {
 
     bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle.");
 
-    Dummy { bundle }
+    Translator {
+        fluent_bundle: None,
+        fallback_fluent_bundle: Arc::new(LazyLock::new(Box::new(|| bundle))),
+    }
 }
 
 #[test]
 fn wellformed_fluent() {
-    let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
+    let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
     .label = value moved into `{$name}` here
     .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait
     .value_borrowed_label = value borrowed here after move
@@ -54,7 +45,7 @@ fn wellformed_fluent() {
         );
 
         assert_eq!(
-            dummy.translate_message(&message, &args).unwrap(),
+            translator.translate_message(&message, &args).unwrap(),
             "borrow this binding in the pattern to avoid moving the value"
         );
     }
@@ -66,7 +57,7 @@ fn wellformed_fluent() {
         );
 
         assert_eq!(
-            dummy.translate_message(&message, &args).unwrap(),
+            translator.translate_message(&message, &args).unwrap(),
             "value borrowed here after move"
         );
     }
@@ -78,7 +69,7 @@ fn wellformed_fluent() {
         );
 
         assert_eq!(
-            dummy.translate_message(&message, &args).unwrap(),
+            translator.translate_message(&message, &args).unwrap(),
             "move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait"
         );
 
@@ -89,7 +80,7 @@ fn wellformed_fluent() {
             );
 
             assert_eq!(
-                dummy.translate_message(&message, &args).unwrap(),
+                translator.translate_message(&message, &args).unwrap(),
                 "value moved into `\u{2068}Foo\u{2069}` here"
             );
         }
@@ -98,7 +89,7 @@ fn wellformed_fluent() {
 
 #[test]
 fn misformed_fluent() {
-    let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
+    let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
     .label = value moved into `{name}` here
     .occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait
     .suggestion = borrow this binding in the pattern to avoid moving the value");
@@ -112,7 +103,7 @@ fn misformed_fluent() {
             Some("value_borrowed_label".into()),
         );
 
-        let err = dummy.translate_message(&message, &args).unwrap_err();
+        let err = translator.translate_message(&message, &args).unwrap_err();
         assert!(
             matches!(
                 &err,
@@ -141,7 +132,7 @@ fn misformed_fluent() {
             Some("label".into()),
         );
 
-        let err = dummy.translate_message(&message, &args).unwrap_err();
+        let err = translator.translate_message(&message, &args).unwrap_err();
         if let TranslateError::Two {
             primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
             fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
@@ -168,7 +159,7 @@ fn misformed_fluent() {
             Some("occurs_because_label".into()),
         );
 
-        let err = dummy.translate_message(&message, &args).unwrap_err();
+        let err = translator.translate_message(&message, &args).unwrap_err();
         if let TranslateError::Two {
             primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
             fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
diff --git a/compiler/rustc_errors/src/timings.rs b/compiler/rustc_errors/src/timings.rs
index 27fc9df8d79..0d82f3e8db8 100644
--- a/compiler/rustc_errors/src/timings.rs
+++ b/compiler/rustc_errors/src/timings.rs
@@ -1,10 +1,15 @@
 use std::time::Instant;
 
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::sync::Lock;
+
 use crate::DiagCtxtHandle;
 
 /// A high-level section of the compilation process.
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub enum TimingSection {
+    /// Time spent doing codegen.
+    Codegen,
     /// Time spent linking.
     Linking,
 }
@@ -36,23 +41,59 @@ pub struct TimingSectionHandler {
     /// Time when the compilation session started.
     /// If `None`, timing is disabled.
     origin: Option<Instant>,
+    /// Sanity check to ensure that we open and close sections correctly.
+    opened_sections: Lock<FxHashSet<TimingSection>>,
 }
 
 impl TimingSectionHandler {
     pub fn new(enabled: bool) -> Self {
         let origin = if enabled { Some(Instant::now()) } else { None };
-        Self { origin }
+        Self { origin, opened_sections: Lock::new(FxHashSet::default()) }
     }
 
     /// Returns a RAII guard that will immediately emit a start the provided section, and then emit
     /// its end when it is dropped.
-    pub fn start_section<'a>(
+    pub fn section_guard<'a>(
         &self,
         diag_ctxt: DiagCtxtHandle<'a>,
         section: TimingSection,
     ) -> TimingSectionGuard<'a> {
+        if self.is_enabled() && self.opened_sections.borrow().contains(&section) {
+            diag_ctxt
+                .bug(format!("Section `{section:?}` was started again before it was finished"));
+        }
+
         TimingSectionGuard::create(diag_ctxt, section, self.origin)
     }
+
+    /// Start the provided section.
+    pub fn start_section(&self, diag_ctxt: DiagCtxtHandle<'_>, section: TimingSection) {
+        if let Some(origin) = self.origin {
+            let mut opened = self.opened_sections.borrow_mut();
+            if !opened.insert(section) {
+                diag_ctxt
+                    .bug(format!("Section `{section:?}` was started again before it was finished"));
+            }
+
+            diag_ctxt.emit_timing_section_start(TimingRecord::from_origin(origin, section));
+        }
+    }
+
+    /// End the provided section.
+    pub fn end_section(&self, diag_ctxt: DiagCtxtHandle<'_>, section: TimingSection) {
+        if let Some(origin) = self.origin {
+            let mut opened = self.opened_sections.borrow_mut();
+            if !opened.remove(&section) {
+                diag_ctxt.bug(format!("Section `{section:?}` was ended before being started"));
+            }
+
+            diag_ctxt.emit_timing_section_end(TimingRecord::from_origin(origin, section));
+        }
+    }
+
+    fn is_enabled(&self) -> bool {
+        self.origin.is_some()
+    }
 }
 
 /// RAII wrapper for starting and ending section timings.
diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs
index 156f5e5d26e..c0bcec093c7 100644
--- a/compiler/rustc_errors/src/translation.rs
+++ b/compiler/rustc_errors/src/translation.rs
@@ -1,8 +1,9 @@
 use std::borrow::Cow;
 use std::env;
 use std::error::Report;
+use std::sync::Arc;
 
-pub use rustc_error_messages::FluentArgs;
+pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle};
 use tracing::{debug, trace};
 
 use crate::error::{TranslateError, TranslateErrorKind};
@@ -28,19 +29,33 @@ pub fn to_fluent_args<'iter>(iter: impl Iterator<Item = DiagArg<'iter>>) -> Flue
     args
 }
 
-pub trait Translate {
-    /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
-    /// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
-    /// should be used.
-    fn fluent_bundle(&self) -> Option<&FluentBundle>;
-
+#[derive(Clone)]
+pub struct Translator {
+    /// Localized diagnostics for the locale requested by the user. If no language was requested by
+    /// the user then this will be `None` and `fallback_fluent_bundle` should be used.
+    pub fluent_bundle: Option<Arc<FluentBundle>>,
     /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
     /// Used when the user has not requested a specific language or when a localized diagnostic is
     /// unavailable for the requested locale.
-    fn fallback_fluent_bundle(&self) -> &FluentBundle;
+    pub fallback_fluent_bundle: LazyFallbackBundle,
+}
+
+impl Translator {
+    pub fn with_fallback_bundle(
+        resources: Vec<&'static str>,
+        with_directionality_markers: bool,
+    ) -> Translator {
+        Translator {
+            fluent_bundle: None,
+            fallback_fluent_bundle: crate::fallback_fluent_bundle(
+                resources,
+                with_directionality_markers,
+            ),
+        }
+    }
 
     /// Convert `DiagMessage`s to a string, performing translation if necessary.
-    fn translate_messages(
+    pub fn translate_messages(
         &self,
         messages: &[(DiagMessage, Style)],
         args: &FluentArgs<'_>,
@@ -54,7 +69,7 @@ pub trait Translate {
     }
 
     /// Convert a `DiagMessage` to a string, performing translation if necessary.
-    fn translate_message<'a>(
+    pub fn translate_message<'a>(
         &'a self,
         message: &'a DiagMessage,
         args: &'a FluentArgs<'_>,
@@ -91,7 +106,7 @@ pub trait Translate {
             };
 
         try {
-            match self.fluent_bundle().map(|b| translate_with_bundle(b)) {
+            match self.fluent_bundle.as_ref().map(|b| translate_with_bundle(b)) {
                 // The primary bundle was present and translation succeeded
                 Some(Ok(t)) => t,
 
@@ -102,7 +117,7 @@ pub trait Translate {
                     primary @ TranslateError::One {
                         kind: TranslateErrorKind::MessageMissing, ..
                     },
-                )) => translate_with_bundle(self.fallback_fluent_bundle())
+                )) => translate_with_bundle(&self.fallback_fluent_bundle)
                     .map_err(|fallback| primary.and(fallback))?,
 
                 // Always yeet out for errors on debug (unless
@@ -118,11 +133,11 @@ pub trait Translate {
 
                 // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
                 // just hide it and try with the fallback bundle.
-                Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle())
+                Some(Err(primary)) => translate_with_bundle(&self.fallback_fluent_bundle)
                     .map_err(|fallback| primary.and(fallback))?,
 
                 // The primary bundle is missing, proceed to the fallback bundle
-                None => translate_with_bundle(self.fallback_fluent_bundle())
+                None => translate_with_bundle(&self.fallback_fluent_bundle)
                     .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?,
             }
         }
diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl
index 8b7c47dad99..b7555bba28e 100644
--- a/compiler/rustc_expand/messages.ftl
+++ b/compiler/rustc_expand/messages.ftl
@@ -62,7 +62,7 @@ expand_feature_not_allowed =
 expand_feature_removed =
     feature has been removed
     .label = feature has been removed
-    .note = removed in {$removed_rustc_version} (you are using {$current_rustc_version}){$pull_note}
+    .note = removed in {$removed_rustc_version}{$pull_note}
     .reason = {$reason}
 
 expand_glob_delegation_outside_impls =
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 9a359e9b031..170ac39d1ec 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -92,7 +92,6 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
                     span: mi.span(),
                     reason: f.reason.map(|reason| FeatureRemovedReason { reason }),
                     removed_rustc_version: f.feature.since,
-                    current_rustc_version: sess.cfg_version,
                     pull_note,
                 });
                 continue;
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs
index ec0af67c046..fe4d2af97a0 100644
--- a/compiler/rustc_expand/src/errors.rs
+++ b/compiler/rustc_expand/src/errors.rs
@@ -162,7 +162,6 @@ pub(crate) struct FeatureRemoved<'a> {
     #[subdiagnostic]
     pub reason: Option<FeatureRemovedReason<'a>>,
     pub removed_rustc_version: &'a str,
-    pub current_rustc_version: &'a str,
     pub pull_note: String,
 }
 
@@ -183,12 +182,12 @@ pub(crate) struct FeatureNotAllowed {
 #[derive(Diagnostic)]
 #[diag(expand_recursion_limit_reached)]
 #[help]
-pub(crate) struct RecursionLimitReached<'a> {
+pub(crate) struct RecursionLimitReached {
     #[primary_span]
     pub span: Span,
     pub descr: String,
     pub suggested_limit: Limit,
-    pub crate_name: &'a str,
+    pub crate_name: Symbol,
 }
 
 #[derive(Diagnostic)]
@@ -444,7 +443,7 @@ pub(crate) struct InvalidFragmentSpecifier {
     #[primary_span]
     pub span: Span,
     pub fragment: Ident,
-    pub help: String,
+    pub help: &'static str,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 9fd524ef45c..0474413e762 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -26,7 +26,7 @@ use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
 use rustc_session::parse::feature_err;
 use rustc_session::{Limit, Session};
 use rustc_span::hygiene::SyntaxContext;
-use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, sym};
+use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, Symbol, sym};
 use smallvec::SmallVec;
 
 use crate::base::*;
@@ -86,7 +86,7 @@ macro_rules! ast_fragments {
                 }
             }
 
-            fn make_from<'a>(self, result: Box<dyn MacResult + 'a>) -> Option<AstFragment> {
+            fn make_from(self, result: Box<dyn MacResult + '_>) -> Option<AstFragment> {
                 match self {
                     AstFragmentKind::OptExpr =>
                         result.make_expr().map(Some).map(AstFragment::OptExpr),
@@ -136,7 +136,7 @@ macro_rules! ast_fragments {
                 T::fragment_to_output(self)
             }
 
-            pub(crate) fn mut_visit_with<F: MutVisitor>(&mut self, vis: &mut F) {
+            pub(crate) fn mut_visit_with(&mut self, vis: &mut impl MutVisitor) {
                 match self {
                     AstFragment::OptExpr(opt_expr) => {
                         if let Some(expr) = opt_expr.take() {
@@ -316,9 +316,9 @@ impl AstFragmentKind {
         }
     }
 
-    pub(crate) fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>(
+    pub(crate) fn expect_from_annotatables(
         self,
-        items: I,
+        items: impl IntoIterator<Item = Annotatable>,
     ) -> AstFragment {
         let mut items = items.into_iter();
         match self {
@@ -473,7 +473,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
         self.cx.root_path = dir_path.clone();
         self.cx.current_expansion.module = Rc::new(ModuleData {
-            mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)],
+            mod_path: vec![Ident::with_dummy_span(self.cx.ecfg.crate_name)],
             file_path_stack: vec![file_path],
             dir_path,
         });
@@ -689,7 +689,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             span: expn_data.call_site,
             descr: expn_data.kind.descr(),
             suggested_limit,
-            crate_name: &self.cx.ecfg.crate_name,
+            crate_name: self.cx.ecfg.crate_name,
         });
 
         self.cx.trace_macros_diag();
@@ -1218,10 +1218,10 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
     fn descr() -> &'static str {
         unreachable!()
     }
-    fn walk_flat_map<V: MutVisitor>(self, _visitor: &mut V) -> Self::OutputTy {
+    fn walk_flat_map(self, _collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
         unreachable!()
     }
-    fn walk<V: MutVisitor>(&mut self, _visitor: &mut V) {
+    fn walk(&mut self, _collector: &mut InvocationCollector<'_, '_>) {
         unreachable!()
     }
     fn is_mac_call(&self) -> bool {
@@ -1276,8 +1276,8 @@ impl InvocationCollectorNode for P<ast::Item> {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_item(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_item(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, ItemKind::MacCall(..))
@@ -1431,8 +1431,8 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag>
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_trait_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Trait)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_assoc_item(collector, self.wrapped, AssocCtxt::Trait)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
@@ -1472,8 +1472,8 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_impl_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Impl { of_trait: false })
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_assoc_item(collector, self.wrapped, AssocCtxt::Impl { of_trait: false })
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
@@ -1513,8 +1513,8 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitImplItem
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_trait_impl_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Impl { of_trait: true })
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_assoc_item(collector, self.wrapped, AssocCtxt::Impl { of_trait: true })
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
@@ -1551,8 +1551,8 @@ impl InvocationCollectorNode for P<ast::ForeignItem> {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_foreign_items()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_foreign_item(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_foreign_item(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, ForeignItemKind::MacCall(..))
@@ -1573,8 +1573,8 @@ impl InvocationCollectorNode for ast::Variant {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_variants()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_variant(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_variant(collector, self)
     }
 }
 
@@ -1586,8 +1586,8 @@ impl InvocationCollectorNode for ast::WherePredicate {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_where_predicates()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_where_predicate(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_where_predicate(collector, self)
     }
 }
 
@@ -1599,8 +1599,8 @@ impl InvocationCollectorNode for ast::FieldDef {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_field_defs()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_field_def(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_field_def(collector, self)
     }
 }
 
@@ -1612,8 +1612,8 @@ impl InvocationCollectorNode for ast::PatField {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_pat_fields()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_pat_field(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_pat_field(collector, self)
     }
 }
 
@@ -1625,8 +1625,8 @@ impl InvocationCollectorNode for ast::ExprField {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_expr_fields()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_expr_field(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_expr_field(collector, self)
     }
 }
 
@@ -1638,8 +1638,8 @@ impl InvocationCollectorNode for ast::Param {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_params()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_param(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_param(collector, self)
     }
 }
 
@@ -1651,8 +1651,8 @@ impl InvocationCollectorNode for ast::GenericParam {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_generic_params()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_generic_param(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_generic_param(collector, self)
     }
 }
 
@@ -1664,8 +1664,8 @@ impl InvocationCollectorNode for ast::Arm {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_arms()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_arm(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_arm(collector, self)
     }
 }
 
@@ -1677,8 +1677,8 @@ impl InvocationCollectorNode for ast::Stmt {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_stmts()
     }
-    fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
-        walk_flat_map_stmt(visitor, self)
+    fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_flat_map_stmt(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         match &self.kind {
@@ -1751,8 +1751,8 @@ impl InvocationCollectorNode for ast::Crate {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_crate()
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_crate(visitor, self)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_crate(collector, self)
     }
     fn expand_cfg_false(
         &mut self,
@@ -1777,8 +1777,8 @@ impl InvocationCollectorNode for ast::Ty {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_ty()
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_ty(visitor, self)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_ty(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, ast::TyKind::MacCall(..))
@@ -1800,8 +1800,8 @@ impl InvocationCollectorNode for ast::Pat {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_pat()
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_pat(visitor, self)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_pat(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, PatKind::MacCall(..))
@@ -1826,8 +1826,8 @@ impl InvocationCollectorNode for ast::Expr {
     fn descr() -> &'static str {
         "an expression"
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_expr(visitor, self)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_expr(collector, self)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.kind, ExprKind::MacCall(..))
@@ -1850,8 +1850,8 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, OptExprTag> {
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         fragment.make_opt_expr()
     }
-    fn walk_flat_map<V: MutVisitor>(mut self, visitor: &mut V) -> Self::OutputTy {
-        walk_expr(visitor, &mut self.wrapped);
+    fn walk_flat_map(mut self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy {
+        walk_expr(collector, &mut self.wrapped);
         Some(self.wrapped)
     }
     fn is_mac_call(&self) -> bool {
@@ -1873,20 +1873,20 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, OptExprTag> {
 /// It can be removed once that feature is stabilized.
 struct MethodReceiverTag;
 
-impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, MethodReceiverTag> {
-    type OutputTy = Self;
+impl InvocationCollectorNode for AstNodeWrapper<ast::Expr, MethodReceiverTag> {
+    type OutputTy = AstNodeWrapper<P<ast::Expr>, MethodReceiverTag>;
     const KIND: AstFragmentKind = AstFragmentKind::MethodReceiverExpr;
     fn descr() -> &'static str {
         "an expression"
     }
     fn to_annotatable(self) -> Annotatable {
-        Annotatable::Expr(self.wrapped)
+        Annotatable::Expr(P(self.wrapped))
     }
     fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
         AstNodeWrapper::new(fragment.make_method_receiver_expr(), MethodReceiverTag)
     }
-    fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
-        walk_expr(visitor, &mut self.wrapped)
+    fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) {
+        walk_expr(collector, &mut self.wrapped)
     }
     fn is_mac_call(&self) -> bool {
         matches!(self.wrapped.kind, ast::ExprKind::MacCall(..))
@@ -1983,9 +1983,9 @@ impl DummyAstNode for ast::Expr {
     }
 }
 
-impl DummyAstNode for AstNodeWrapper<P<ast::Expr>, MethodReceiverTag> {
+impl DummyAstNode for AstNodeWrapper<ast::Expr, MethodReceiverTag> {
     fn dummy() -> Self {
-        AstNodeWrapper::new(P(ast::Expr::dummy()), MethodReceiverTag)
+        AstNodeWrapper::new(ast::Expr::dummy(), MethodReceiverTag)
     }
 }
 
@@ -2431,7 +2431,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
         self.visit_node(node)
     }
 
-    fn visit_method_receiver_expr(&mut self, node: &mut P<ast::Expr>) {
+    fn visit_method_receiver_expr(&mut self, node: &mut ast::Expr) {
         self.visit_node(AstNodeWrapper::from_mut(node, MethodReceiverTag))
     }
 
@@ -2458,7 +2458,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
 }
 
 pub struct ExpansionConfig<'feat> {
-    pub crate_name: String,
+    pub crate_name: Symbol,
     pub features: &'feat Features,
     pub recursion_limit: Limit,
     pub trace_mac: bool,
@@ -2471,7 +2471,7 @@ pub struct ExpansionConfig<'feat> {
 }
 
 impl ExpansionConfig<'_> {
-    pub fn default(crate_name: String, features: &Features) -> ExpansionConfig<'_> {
+    pub fn default(crate_name: Symbol, features: &Features) -> ExpansionConfig<'_> {
         ExpansionConfig {
             crate_name,
             features,
diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs
index 4ff8c02bcdb..3082c881a7a 100644
--- a/compiler/rustc_expand/src/mbe.rs
+++ b/compiler/rustc_expand/src/mbe.rs
@@ -78,7 +78,13 @@ enum TokenTree {
     /// only covers the ident, e.g. `var`.)
     MetaVar(Span, Ident),
     /// e.g., `$var:expr`. Only appears on the LHS.
-    MetaVarDecl(Span, Ident /* name to bind */, Option<NonterminalKind>),
+    MetaVarDecl {
+        span: Span,
+        /// Name to bind.
+        name: Ident,
+        /// The fragment specifier.
+        kind: NonterminalKind,
+    },
     /// A meta-variable expression inside `${...}`.
     MetaVarExpr(DelimSpan, MetaVarExpr),
 }
@@ -102,7 +108,7 @@ impl TokenTree {
         match *self {
             TokenTree::Token(Token { span, .. })
             | TokenTree::MetaVar(span, _)
-            | TokenTree::MetaVarDecl(span, _, _) => span,
+            | TokenTree::MetaVarDecl { span, .. } => span,
             TokenTree::Delimited(span, ..)
             | TokenTree::MetaVarExpr(span, _)
             | TokenTree::Sequence(span, _) => span.entire(),
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index 698492f42e2..99aa376626d 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -24,6 +24,7 @@ pub(super) fn failed_to_match_macro(
     arg: TokenStream,
     lhses: &[Vec<MatcherLoc>],
 ) -> (Span, ErrorGuaranteed) {
+    debug!("failed to match macro");
     // An error occurred, try the expansion again, tracking the expansion closely for better
     // diagnostics.
     let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index 3cd803c3e84..dc2d46c4a14 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -117,7 +117,6 @@ use rustc_session::parse::ParseSess;
 use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw};
 use smallvec::SmallVec;
 
-use super::quoted::VALID_FRAGMENT_NAMES_MSG;
 use crate::errors;
 use crate::mbe::{KleeneToken, TokenTree};
 
@@ -263,14 +262,7 @@ fn check_binders(
             }
         }
         // Similarly, this can only happen when checking a toplevel macro.
-        TokenTree::MetaVarDecl(span, name, kind) => {
-            if kind.is_none() && node_id != DUMMY_NODE_ID {
-                psess.dcx().emit_err(errors::MissingFragmentSpecifier {
-                    span,
-                    add_span: span.shrink_to_hi(),
-                    valid: VALID_FRAGMENT_NAMES_MSG,
-                });
-            }
+        TokenTree::MetaVarDecl { span, name, .. } => {
             if !macros.is_empty() {
                 psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs");
             }
@@ -339,7 +331,7 @@ fn check_occurrences(
 ) {
     match *rhs {
         TokenTree::Token(..) => {}
-        TokenTree::MetaVarDecl(span, _name, _kind) => {
+        TokenTree::MetaVarDecl { span, .. } => {
             psess.dcx().span_bug(span, "unexpected MetaVarDecl in rhs")
         }
         TokenTree::MetaVar(span, name) => {
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index c78beb40688..802e43209a5 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -122,7 +122,7 @@ pub(crate) enum MatcherLoc {
     MetaVarDecl {
         span: Span,
         bind: Ident,
-        kind: Option<NonterminalKind>,
+        kind: NonterminalKind,
         next_metavar: usize,
         seq_depth: usize,
     },
@@ -151,12 +151,7 @@ impl Display for MatcherLoc {
                 write!(f, "{}", token_descr(token))
             }
             MatcherLoc::MetaVarDecl { bind, kind, .. } => {
-                write!(f, "meta-variable `${bind}")?;
-                if let Some(kind) = kind {
-                    write!(f, ":{kind}")?;
-                }
-                write!(f, "`")?;
-                Ok(())
+                write!(f, "meta-variable `${bind}:{kind}`")
             }
             MatcherLoc::Eof => f.write_str("end of macro"),
 
@@ -220,7 +215,7 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
                         seq_depth,
                     };
                 }
-                &TokenTree::MetaVarDecl(span, bind, kind) => {
+                &TokenTree::MetaVarDecl { span, name: bind, kind } => {
                     locs.push(MatcherLoc::MetaVarDecl {
                         span,
                         bind,
@@ -330,7 +325,7 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize {
     matcher
         .iter()
         .map(|tt| match tt {
-            TokenTree::MetaVarDecl(..) => 1,
+            TokenTree::MetaVarDecl { .. } => 1,
             TokenTree::Sequence(_, seq) => seq.num_captures,
             TokenTree::Delimited(.., delim) => count_metavar_decls(&delim.tts),
             TokenTree::Token(..) => 0,
@@ -551,18 +546,12 @@ impl TtParser {
                     mp.idx = idx_first;
                     self.cur_mps.push(mp);
                 }
-                &MatcherLoc::MetaVarDecl { span, kind, .. } => {
+                &MatcherLoc::MetaVarDecl { kind, .. } => {
                     // Built-in nonterminals never start with these tokens, so we can eliminate
                     // them from consideration. We use the span of the metavariable declaration
                     // to determine any edition-specific matching behavior for non-terminals.
-                    if let Some(kind) = kind {
-                        if Parser::nonterminal_may_begin_with(kind, token) {
-                            self.bb_mps.push(mp);
-                        }
-                    } else {
-                        // E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used.
-                        // Both this check and the one in `nameize` are necessary, surprisingly.
-                        return Some(Error(span, "missing fragment specifier".to_string()));
+                    if Parser::nonterminal_may_begin_with(kind, token) {
+                        self.bb_mps.push(mp);
                     }
                 }
                 MatcherLoc::Eof => {
@@ -666,11 +655,7 @@ impl TtParser {
                     let mut mp = self.bb_mps.pop().unwrap();
                     let loc = &matcher[mp.idx];
                     if let &MatcherLoc::MetaVarDecl {
-                        span,
-                        kind: Some(kind),
-                        next_metavar,
-                        seq_depth,
-                        ..
+                        span, kind, next_metavar, seq_depth, ..
                     } = loc
                     {
                         // We use the span of the metavariable declaration to determine any
@@ -715,7 +700,7 @@ impl TtParser {
             .bb_mps
             .iter()
             .map(|mp| match &matcher[mp.idx] {
-                MatcherLoc::MetaVarDecl { bind, kind: Some(kind), .. } => {
+                MatcherLoc::MetaVarDecl { bind, kind, .. } => {
                     format!("{kind} ('{bind}')")
                 }
                 _ => unreachable!(),
@@ -745,19 +730,13 @@ impl TtParser {
         // `NamedParseResult`. Otherwise, it's an error.
         let mut ret_val = FxHashMap::default();
         for loc in matcher {
-            if let &MatcherLoc::MetaVarDecl { span, bind, kind, .. } = loc {
-                if kind.is_some() {
-                    match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) {
-                        Vacant(spot) => spot.insert(res.next().unwrap()),
-                        Occupied(..) => {
-                            return Error(span, format!("duplicated bind name: {bind}"));
-                        }
-                    };
-                } else {
-                    // E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used.
-                    // Both this check and the one in `parse_tt_inner` are necessary, surprisingly.
-                    return Error(span, "missing fragment specifier".to_string());
-                }
+            if let &MatcherLoc::MetaVarDecl { span, bind, .. } = loc {
+                match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) {
+                    Vacant(spot) => spot.insert(res.next().unwrap()),
+                    Occupied(..) => {
+                        return Error(span, format!("duplicated bind name: {bind}"));
+                    }
+                };
             }
         }
         Success(ret_val)
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 783f061ec6c..432ab324740 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -392,7 +392,7 @@ pub fn compile_declarative_macro(
 
     let lhs_nm = Ident::new(sym::lhs, span);
     let rhs_nm = Ident::new(sym::rhs, span);
-    let tt_spec = Some(NonterminalKind::TT);
+    let tt_spec = NonterminalKind::TT;
     let macro_rules = macro_def.macro_rules;
 
     // Parse the macro_rules! invocation
@@ -407,9 +407,9 @@ pub fn compile_declarative_macro(
             DelimSpan::dummy(),
             mbe::SequenceRepetition {
                 tts: vec![
-                    mbe::TokenTree::MetaVarDecl(span, lhs_nm, tt_spec),
+                    mbe::TokenTree::MetaVarDecl { span, name: lhs_nm, kind: tt_spec },
                     mbe::TokenTree::token(token::FatArrow, span),
-                    mbe::TokenTree::MetaVarDecl(span, rhs_nm, tt_spec),
+                    mbe::TokenTree::MetaVarDecl { span, name: rhs_nm, kind: tt_spec },
                 ],
                 separator: Some(Token::new(
                     if macro_rules { token::Semi } else { token::Comma },
@@ -448,6 +448,7 @@ pub fn compile_declarative_macro(
         match tt_parser.parse_tt(&mut Cow::Owned(parser), &argument_gram, &mut NoopTracker) {
             Success(m) => m,
             Failure(()) => {
+                debug!("failed to parse macro tt");
                 // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it
                 // with another one that gives us the information we need.
                 // For this we need to reclone the macro body as the previous parser consumed it.
@@ -616,7 +617,7 @@ fn is_empty_token_tree(sess: &Session, seq: &mbe::SequenceRepetition) -> bool {
         let mut iter = seq.tts.iter().peekable();
         while let Some(tt) = iter.next() {
             match tt {
-                mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => {}
+                mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. } => {}
                 mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => {
                     let mut now = t;
                     while let Some(&mbe::TokenTree::Token(
@@ -651,7 +652,7 @@ fn check_redundant_vis_repetition(
 ) {
     let is_zero_or_one: bool = seq.kleene.op == KleeneOp::ZeroOrOne;
     let is_vis = seq.tts.first().map_or(false, |tt| {
-        matches!(tt, mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)))
+        matches!(tt, mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. })
     });
 
     if is_vis && is_zero_or_one {
@@ -678,7 +679,7 @@ fn check_lhs_no_empty_seq(sess: &Session, tts: &[mbe::TokenTree]) -> Result<(),
         match tt {
             TokenTree::Token(..)
             | TokenTree::MetaVar(..)
-            | TokenTree::MetaVarDecl(..)
+            | TokenTree::MetaVarDecl { .. }
             | TokenTree::MetaVarExpr(..) => (),
             TokenTree::Delimited(.., del) => check_lhs_no_empty_seq(sess, &del.tts)?,
             TokenTree::Sequence(span, seq) => {
@@ -777,7 +778,7 @@ impl<'tt> FirstSets<'tt> {
                 match tt {
                     TokenTree::Token(..)
                     | TokenTree::MetaVar(..)
-                    | TokenTree::MetaVarDecl(..)
+                    | TokenTree::MetaVarDecl { .. }
                     | TokenTree::MetaVarExpr(..) => {
                         first.replace_with(TtHandle::TtRef(tt));
                     }
@@ -845,7 +846,7 @@ impl<'tt> FirstSets<'tt> {
             match tt {
                 TokenTree::Token(..)
                 | TokenTree::MetaVar(..)
-                | TokenTree::MetaVarDecl(..)
+                | TokenTree::MetaVarDecl { .. }
                 | TokenTree::MetaVarExpr(..) => {
                     first.add_one(TtHandle::TtRef(tt));
                     return first;
@@ -1084,7 +1085,7 @@ fn check_matcher_core<'tt>(
         match token {
             TokenTree::Token(..)
             | TokenTree::MetaVar(..)
-            | TokenTree::MetaVarDecl(..)
+            | TokenTree::MetaVarDecl { .. }
             | TokenTree::MetaVarExpr(..) => {
                 if token_can_be_followed_by_any(token) {
                     // don't need to track tokens that work with any,
@@ -1152,7 +1153,7 @@ fn check_matcher_core<'tt>(
         // Now `last` holds the complete set of NT tokens that could
         // end the sequence before SUFFIX. Check that every one works with `suffix`.
         for tt in &last.tokens {
-            if let &TokenTree::MetaVarDecl(span, name, Some(kind)) = tt.get() {
+            if let &TokenTree::MetaVarDecl { span, name, kind } = tt.get() {
                 for next_token in &suffix_first.tokens {
                     let next_token = next_token.get();
 
@@ -1172,11 +1173,11 @@ fn check_matcher_core<'tt>(
                         )
                     {
                         // It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param.
-                        let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
+                        let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl {
                             span,
                             name,
-                            Some(NonterminalKind::Pat(PatParam { inferred: false })),
-                        ));
+                            kind: NonterminalKind::Pat(PatParam { inferred: false }),
+                        });
                         sess.psess.buffer_lint(
                             RUST_2021_INCOMPATIBLE_OR_PATTERNS,
                             span,
@@ -1212,11 +1213,11 @@ fn check_matcher_core<'tt>(
                                 && sess.psess.edition.at_least_rust_2021()
                                 && next_token.is_token(&token::Or)
                             {
-                                let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
+                                let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl {
                                     span,
                                     name,
-                                    Some(NonterminalKind::Pat(PatParam { inferred: false })),
-                                ));
+                                    kind: NonterminalKind::Pat(PatParam { inferred: false }),
+                                });
                                 err.span_suggestion(
                                     span,
                                     "try a `pat_param` fragment specifier instead",
@@ -1254,7 +1255,7 @@ fn check_matcher_core<'tt>(
 }
 
 fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool {
-    if let mbe::TokenTree::MetaVarDecl(_, _, Some(kind)) = *tok {
+    if let mbe::TokenTree::MetaVarDecl { kind, .. } = *tok {
         frag_can_be_followed_by_any(kind)
     } else {
         // (Non NT's can always be followed by anything in matchers.)
@@ -1367,7 +1368,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
                         }
                         _ => IsInFollow::No(TOKENS),
                     },
-                    TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Block)) => IsInFollow::Yes,
+                    TokenTree::MetaVarDecl { kind: NonterminalKind::Block, .. } => IsInFollow::Yes,
                     _ => IsInFollow::No(TOKENS),
                 }
             }
@@ -1400,11 +1401,10 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
                             }
                         }
                     },
-                    TokenTree::MetaVarDecl(
-                        _,
-                        _,
-                        Some(NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path),
-                    ) => IsInFollow::Yes,
+                    TokenTree::MetaVarDecl {
+                        kind: NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path,
+                        ..
+                    } => IsInFollow::Yes,
                     _ => IsInFollow::No(TOKENS),
                 }
             }
@@ -1416,8 +1416,7 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
     match tt {
         mbe::TokenTree::Token(token) => pprust::token_to_string(token).into(),
         mbe::TokenTree::MetaVar(_, name) => format!("${name}"),
-        mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${name}:{kind}"),
-        mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${name}:"),
+        mbe::TokenTree::MetaVarDecl { name, kind, .. } => format!("${name}:{kind}"),
         _ => panic!(
             "{}",
             "unexpected mbe::TokenTree::{Sequence or Delimited} \
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index 0c2362f23bc..2daa4e71558 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -54,66 +54,78 @@ pub(super) fn parse(
         // Given the parsed tree, if there is a metavar and we are expecting matchers, actually
         // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
         let tree = parse_tree(tree, &mut iter, parsing_patterns, sess, node_id, features, edition);
-        match tree {
-            TokenTree::MetaVar(start_sp, ident) if parsing_patterns => {
-                // Not consuming the next token immediately, as it may not be a colon
-                let span = match iter.peek() {
-                    Some(&tokenstream::TokenTree::Token(
-                        Token { kind: token::Colon, span: colon_span },
-                        _,
-                    )) => {
-                        // Consume the colon first
-                        iter.next();
-
-                        // It's ok to consume the next tree no matter how,
-                        // since if it's not a token then it will be an invalid declaration.
-                        match iter.next() {
-                            Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() {
-                                Some((fragment, _)) => {
-                                    let span = token.span.with_lo(start_sp.lo());
-                                    let edition = || {
-                                        // FIXME(#85708) - once we properly decode a foreign
-                                        // crate's `SyntaxContext::root`, then we can replace
-                                        // this with just `span.edition()`. A
-                                        // `SyntaxContext::root()` from the current crate will
-                                        // have the edition of the current crate, and a
-                                        // `SyntaxContext::root()` from a foreign crate will
-                                        // have the edition of that crate (which we manually
-                                        // retrieve via the `edition` parameter).
-                                        if !span.from_expansion() {
-                                            edition
-                                        } else {
-                                            span.edition()
-                                        }
-                                    };
-                                    let kind = NonterminalKind::from_symbol(fragment.name, edition)
-                                        .unwrap_or_else(|| {
-                                            sess.dcx().emit_err(errors::InvalidFragmentSpecifier {
-                                                span,
-                                                fragment,
-                                                help: VALID_FRAGMENT_NAMES_MSG.into(),
-                                            });
-                                            NonterminalKind::Ident
-                                        });
-                                    result.push(TokenTree::MetaVarDecl(span, ident, Some(kind)));
-                                    continue;
-                                }
-                                _ => token.span,
-                            },
-                            // Invalid, return a nice source location
-                            _ => colon_span.with_lo(start_sp.lo()),
-                        }
-                    }
-                    // Whether it's none or some other tree, it doesn't belong to
-                    // the current meta variable, returning the original span.
-                    _ => start_sp,
-                };
 
-                result.push(TokenTree::MetaVarDecl(span, ident, None));
-            }
+        if !parsing_patterns {
+            // No matchers allowed, nothing to process here
+            result.push(tree);
+            continue;
+        }
+
+        let TokenTree::MetaVar(start_sp, ident) = tree else {
+            // Not a metavariable, just return the tree
+            result.push(tree);
+            continue;
+        };
 
-            // Not a metavar or no matchers allowed, so just return the tree
-            _ => result.push(tree),
+        // Push a metavariable with no fragment specifier at the given span
+        let mut missing_fragment_specifier = |span| {
+            sess.dcx().emit_err(errors::MissingFragmentSpecifier {
+                span,
+                add_span: span.shrink_to_hi(),
+                valid: VALID_FRAGMENT_NAMES_MSG,
+            });
+
+            // Fall back to a `TokenTree` since that will match anything if we continue expanding.
+            result.push(TokenTree::MetaVarDecl { span, name: ident, kind: NonterminalKind::TT });
+        };
+
+        // Not consuming the next token immediately, as it may not be a colon
+        if let Some(peek) = iter.peek()
+            && let tokenstream::TokenTree::Token(token, _spacing) = peek
+            && let Token { kind: token::Colon, span: colon_span } = token
+        {
+            // Next token is a colon; consume it
+            iter.next();
+
+            // It's ok to consume the next tree no matter how,
+            // since if it's not a token then it will be an invalid declaration.
+            let Some(tokenstream::TokenTree::Token(token, _)) = iter.next() else {
+                // Invalid, return a nice source location as `var:`
+                missing_fragment_specifier(colon_span.with_lo(start_sp.lo()));
+                continue;
+            };
+
+            let Some((fragment, _)) = token.ident() else {
+                // No identifier for the fragment specifier;
+                missing_fragment_specifier(token.span);
+                continue;
+            };
+
+            let span = token.span.with_lo(start_sp.lo());
+            let edition = || {
+                // FIXME(#85708) - once we properly decode a foreign
+                // crate's `SyntaxContext::root`, then we can replace
+                // this with just `span.edition()`. A
+                // `SyntaxContext::root()` from the current crate will
+                // have the edition of the current crate, and a
+                // `SyntaxContext::root()` from a foreign crate will
+                // have the edition of that crate (which we manually
+                // retrieve via the `edition` parameter).
+                if !span.from_expansion() { edition } else { span.edition() }
+            };
+            let kind = NonterminalKind::from_symbol(fragment.name, edition).unwrap_or_else(|| {
+                sess.dcx().emit_err(errors::InvalidFragmentSpecifier {
+                    span,
+                    fragment,
+                    help: VALID_FRAGMENT_NAMES_MSG,
+                });
+                NonterminalKind::TT
+            });
+            result.push(TokenTree::MetaVarDecl { span, name: ident, kind });
+        } else {
+            // Whether it's none or some other tree, it doesn't belong to
+            // the current meta variable, returning the original span.
+            missing_fragment_specifier(start_sp);
         }
     }
     result
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 2d3fd7702da..a8c4a9e4b1b 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize};
 use rustc_parse::lexer::nfc_normalize;
 use rustc_parse::parser::ParseNtResult;
-use rustc_session::parse::{ParseSess, SymbolGallery};
+use rustc_session::parse::ParseSess;
 use rustc_span::hygiene::{LocalExpnId, Transparency};
 use rustc_span::{
     Ident, MacroRulesNormalizedIdent, Span, Symbol, SyntaxContext, sym, with_metavar_spans,
@@ -25,20 +25,77 @@ use crate::mbe::macro_parser::NamedMatch::*;
 use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR};
 use crate::mbe::{self, KleeneOp, MetaVarExpr};
 
-// A Marker adds the given mark to the syntax context.
-struct Marker(LocalExpnId, Transparency, FxHashMap<SyntaxContext, SyntaxContext>);
+/// Context needed to perform transcription of metavariable expressions.
+struct TranscrCtx<'psess, 'itp> {
+    psess: &'psess ParseSess,
+
+    /// Map from metavars to matched tokens
+    interp: &'itp FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
+
+    /// Allow marking spans.
+    marker: Marker,
+
+    /// The stack of things yet to be completely expanded.
+    ///
+    /// We descend into the RHS (`src`), expanding things as we go. This stack contains the things
+    /// we have yet to expand/are still expanding. We start the stack off with the whole RHS. The
+    /// choice of spacing values doesn't matter.
+    stack: SmallVec<[Frame<'itp>; 1]>,
+
+    /// A stack of where we are in the repeat expansion.
+    ///
+    /// As we descend in the RHS, we will need to be able to match nested sequences of matchers.
+    /// `repeats` keeps track of where we are in matching at each level, with the last element
+    /// being the most deeply nested sequence. This is used as a stack.
+    repeats: Vec<(usize, usize)>,
+
+    /// The resulting token stream from the `TokenTree` we just finished processing.
+    ///
+    /// At the end, this will contain the full result of transcription, but at arbitrary points
+    /// during `transcribe`, `result` will contain subsets of the final result.
+    ///
+    /// Specifically, as we descend into each TokenTree, we will push the existing results onto the
+    /// `result_stack` and clear `results`. We will then produce the results of transcribing the
+    /// TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
+    /// `result_stack` and append `results` too it to produce the new `results` up to that point.
+    ///
+    /// Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
+    /// again, and we are done transcribing.
+    result: Vec<TokenTree>,
+
+    /// The in-progress `result` lives at the top of this stack. Each entered `TokenTree` adds a
+    /// new entry.
+    result_stack: Vec<Vec<TokenTree>>,
+}
+
+impl<'psess> TranscrCtx<'psess, '_> {
+    /// Span marked with the correct expansion and transparency.
+    fn visited_dspan(&mut self, dspan: DelimSpan) -> Span {
+        let mut span = dspan.entire();
+        self.marker.mark_span(&mut span);
+        span
+    }
+}
+
+/// A Marker adds the given mark to the syntax context.
+struct Marker {
+    expand_id: LocalExpnId,
+    transparency: Transparency,
+    cache: FxHashMap<SyntaxContext, SyntaxContext>,
+}
 
 impl Marker {
+    /// Mark a span with the stored expansion ID and transparency.
     fn mark_span(&mut self, span: &mut Span) {
         // `apply_mark` is a relatively expensive operation, both due to taking hygiene lock, and
         // by itself. All tokens in a macro body typically have the same syntactic context, unless
         // it's some advanced case with macro-generated macros. So if we cache the marked version
         // of that context once, we'll typically have a 100% cache hit rate after that.
-        let Marker(expn_id, transparency, ref mut cache) = *self;
         *span = span.map_ctxt(|ctxt| {
-            *cache
+            *self
+                .cache
                 .entry(ctxt)
-                .or_insert_with(|| ctxt.apply_mark(expn_id.to_expn_id(), transparency))
+                .or_insert_with(|| ctxt.apply_mark(self.expand_id.to_expn_id(), self.transparency))
         });
     }
 }
@@ -116,52 +173,36 @@ pub(super) fn transcribe<'a>(
         return Ok(TokenStream::default());
     }
 
-    // We descend into the RHS (`src`), expanding things as we go. This stack contains the things
-    // we have yet to expand/are still expanding. We start the stack off with the whole RHS. The
-    // choice of spacing values doesn't matter.
-    let mut stack: SmallVec<[Frame<'_>; 1]> = smallvec![Frame::new_delimited(
-        src,
-        src_span,
-        DelimSpacing::new(Spacing::Alone, Spacing::Alone)
-    )];
-
-    // As we descend in the RHS, we will need to be able to match nested sequences of matchers.
-    // `repeats` keeps track of where we are in matching at each level, with the last element being
-    // the most deeply nested sequence. This is used as a stack.
-    let mut repeats: Vec<(usize, usize)> = Vec::new();
-
-    // `result` contains resulting token stream from the TokenTree we just finished processing. At
-    // the end, this will contain the full result of transcription, but at arbitrary points during
-    // `transcribe`, `result` will contain subsets of the final result.
-    //
-    // Specifically, as we descend into each TokenTree, we will push the existing results onto the
-    // `result_stack` and clear `results`. We will then produce the results of transcribing the
-    // TokenTree into `results`. Then, as we unwind back out of the `TokenTree`, we will pop the
-    // `result_stack` and append `results` too it to produce the new `results` up to that point.
-    //
-    // Thus, if we try to pop the `result_stack` and it is empty, we have reached the top-level
-    // again, and we are done transcribing.
-    let mut result: Vec<TokenTree> = Vec::new();
-    let mut result_stack = Vec::new();
-    let mut marker = Marker(expand_id, transparency, Default::default());
-
-    let dcx = psess.dcx();
+    let mut tscx = TranscrCtx {
+        psess,
+        interp,
+        marker: Marker { expand_id, transparency, cache: Default::default() },
+        repeats: Vec::new(),
+        stack: smallvec![Frame::new_delimited(
+            src,
+            src_span,
+            DelimSpacing::new(Spacing::Alone, Spacing::Alone)
+        )],
+        result: Vec::new(),
+        result_stack: Vec::new(),
+    };
+
     loop {
         // Look at the last frame on the stack.
         // If it still has a TokenTree we have not looked at yet, use that tree.
-        let Some(tree) = stack.last_mut().unwrap().next() else {
+        let Some(tree) = tscx.stack.last_mut().unwrap().next() else {
             // This else-case never produces a value for `tree` (it `continue`s or `return`s).
 
             // Otherwise, if we have just reached the end of a sequence and we can keep repeating,
             // go back to the beginning of the sequence.
-            let frame = stack.last_mut().unwrap();
+            let frame = tscx.stack.last_mut().unwrap();
             if let FrameKind::Sequence { sep, .. } = &frame.kind {
-                let (repeat_idx, repeat_len) = repeats.last_mut().unwrap();
+                let (repeat_idx, repeat_len) = tscx.repeats.last_mut().unwrap();
                 *repeat_idx += 1;
                 if repeat_idx < repeat_len {
                     frame.idx = 0;
                     if let Some(sep) = sep {
-                        result.push(TokenTree::Token(*sep, Spacing::Alone));
+                        tscx.result.push(TokenTree::Token(*sep, Spacing::Alone));
                     }
                     continue;
                 }
@@ -170,10 +211,10 @@ pub(super) fn transcribe<'a>(
             // We are done with the top of the stack. Pop it. Depending on what it was, we do
             // different things. Note that the outermost item must be the delimited, wrapped RHS
             // that was passed in originally to `transcribe`.
-            match stack.pop().unwrap().kind {
+            match tscx.stack.pop().unwrap().kind {
                 // Done with a sequence. Pop from repeats.
                 FrameKind::Sequence { .. } => {
-                    repeats.pop();
+                    tscx.repeats.pop();
                 }
 
                 // We are done processing a Delimited. If this is the top-level delimited, we are
@@ -185,15 +226,16 @@ pub(super) fn transcribe<'a>(
                     if delim == Delimiter::Bracket {
                         spacing.close = Spacing::Alone;
                     }
-                    if result_stack.is_empty() {
+                    if tscx.result_stack.is_empty() {
                         // No results left to compute! We are back at the top-level.
-                        return Ok(TokenStream::new(result));
+                        return Ok(TokenStream::new(tscx.result));
                     }
 
                     // Step back into the parent Delimited.
-                    let tree = TokenTree::Delimited(span, spacing, delim, TokenStream::new(result));
-                    result = result_stack.pop().unwrap();
-                    result.push(tree);
+                    let tree =
+                        TokenTree::Delimited(span, spacing, delim, TokenStream::new(tscx.result));
+                    tscx.result = tscx.result_stack.pop().unwrap();
+                    tscx.result.push(tree);
                 }
             }
             continue;
@@ -202,223 +244,19 @@ pub(super) fn transcribe<'a>(
         // At this point, we know we are in the middle of a TokenTree (the last one on `stack`).
         // `tree` contains the next `TokenTree` to be processed.
         match tree {
-            // We are descending into a sequence. We first make sure that the matchers in the RHS
-            // and the matches in `interp` have the same shape. Otherwise, either the caller or the
-            // macro writer has made a mistake.
+            // Replace the sequence with its expansion.
             seq @ mbe::TokenTree::Sequence(_, seq_rep) => {
-                match lockstep_iter_size(seq, interp, &repeats) {
-                    LockstepIterSize::Unconstrained => {
-                        return Err(dcx.create_err(NoSyntaxVarsExprRepeat { span: seq.span() }));
-                    }
-
-                    LockstepIterSize::Contradiction(msg) => {
-                        // FIXME: this really ought to be caught at macro definition time... It
-                        // happens when two meta-variables are used in the same repetition in a
-                        // sequence, but they come from different sequence matchers and repeat
-                        // different amounts.
-                        return Err(
-                            dcx.create_err(MetaVarsDifSeqMatchers { span: seq.span(), msg })
-                        );
-                    }
-
-                    LockstepIterSize::Constraint(len, _) => {
-                        // We do this to avoid an extra clone above. We know that this is a
-                        // sequence already.
-                        let mbe::TokenTree::Sequence(sp, seq) = seq else { unreachable!() };
-
-                        // Is the repetition empty?
-                        if len == 0 {
-                            if seq.kleene.op == KleeneOp::OneOrMore {
-                                // FIXME: this really ought to be caught at macro definition
-                                // time... It happens when the Kleene operator in the matcher and
-                                // the body for the same meta-variable do not match.
-                                return Err(dcx.create_err(MustRepeatOnce { span: sp.entire() }));
-                            }
-                        } else {
-                            // 0 is the initial counter (we have done 0 repetitions so far). `len`
-                            // is the total number of repetitions we should generate.
-                            repeats.push((0, len));
-
-                            // The first time we encounter the sequence we push it to the stack. It
-                            // then gets reused (see the beginning of the loop) until we are done
-                            // repeating.
-                            stack.push(Frame::new_sequence(
-                                seq_rep,
-                                seq.separator.clone(),
-                                seq.kleene.op,
-                            ));
-                        }
-                    }
-                }
+                transcribe_sequence(&mut tscx, seq, seq_rep)?;
             }
 
             // Replace the meta-var with the matched token tree from the invocation.
-            &mbe::TokenTree::MetaVar(mut sp, mut original_ident) => {
-                // Find the matched nonterminal from the macro invocation, and use it to replace
-                // the meta-var.
-                //
-                // We use `Spacing::Alone` everywhere here, because that's the conservative choice
-                // and spacing of declarative macros is tricky. E.g. in this macro:
-                // ```
-                // macro_rules! idents {
-                //     ($($a:ident,)*) => { stringify!($($a)*) }
-                // }
-                // ```
-                // `$a` has no whitespace after it and will be marked `JointHidden`. If you then
-                // call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So
-                // if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up
-                // producing "xyz", which is bad because it effectively merges tokens.
-                // `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid
-                // some of the unnecessary whitespace.
-                let ident = MacroRulesNormalizedIdent::new(original_ident);
-                if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
-                    // We wrap the tokens in invisible delimiters, unless they are already wrapped
-                    // in invisible delimiters with the same `MetaVarKind`. Because some proc
-                    // macros can't handle multiple layers of invisible delimiters of the same
-                    // `MetaVarKind`. This loses some span info, though it hopefully won't matter.
-                    let mut mk_delimited = |mk_span, mv_kind, mut stream: TokenStream| {
-                        if stream.len() == 1 {
-                            let tree = stream.iter().next().unwrap();
-                            if let TokenTree::Delimited(_, _, delim, inner) = tree
-                                && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mvk)) = delim
-                                && mv_kind == *mvk
-                            {
-                                stream = inner.clone();
-                            }
-                        }
-
-                        // Emit as a token stream within `Delimiter::Invisible` to maintain
-                        // parsing priorities.
-                        marker.mark_span(&mut sp);
-                        with_metavar_spans(|mspans| mspans.insert(mk_span, sp));
-                        // Both the open delim and close delim get the same span, which covers the
-                        // `$foo` in the decl macro RHS.
-                        TokenTree::Delimited(
-                            DelimSpan::from_single(sp),
-                            DelimSpacing::new(Spacing::Alone, Spacing::Alone),
-                            Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)),
-                            stream,
-                        )
-                    };
-                    let tt = match cur_matched {
-                        MatchedSingle(ParseNtResult::Tt(tt)) => {
-                            // `tt`s are emitted into the output stream directly as "raw tokens",
-                            // without wrapping them into groups. Other variables are emitted into
-                            // the output stream as groups with `Delimiter::Invisible` to maintain
-                            // parsing priorities.
-                            maybe_use_metavar_location(psess, &stack, sp, tt, &mut marker)
-                        }
-                        MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => {
-                            marker.mark_span(&mut sp);
-                            with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
-                            let kind = token::NtIdent(*ident, *is_raw);
-                            TokenTree::token_alone(kind, sp)
-                        }
-                        MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
-                            marker.mark_span(&mut sp);
-                            with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
-                            let kind = token::NtLifetime(*ident, *is_raw);
-                            TokenTree::token_alone(kind, sp)
-                        }
-                        MatchedSingle(ParseNtResult::Item(item)) => {
-                            mk_delimited(item.span, MetaVarKind::Item, TokenStream::from_ast(item))
-                        }
-                        MatchedSingle(ParseNtResult::Block(block)) => mk_delimited(
-                            block.span,
-                            MetaVarKind::Block,
-                            TokenStream::from_ast(block),
-                        ),
-                        MatchedSingle(ParseNtResult::Stmt(stmt)) => {
-                            let stream = if let StmtKind::Empty = stmt.kind {
-                                // FIXME: Properly collect tokens for empty statements.
-                                TokenStream::token_alone(token::Semi, stmt.span)
-                            } else {
-                                TokenStream::from_ast(stmt)
-                            };
-                            mk_delimited(stmt.span, MetaVarKind::Stmt, stream)
-                        }
-                        MatchedSingle(ParseNtResult::Pat(pat, pat_kind)) => mk_delimited(
-                            pat.span,
-                            MetaVarKind::Pat(*pat_kind),
-                            TokenStream::from_ast(pat),
-                        ),
-                        MatchedSingle(ParseNtResult::Expr(expr, kind)) => {
-                            let (can_begin_literal_maybe_minus, can_begin_string_literal) =
-                                match &expr.kind {
-                                    ExprKind::Lit(_) => (true, true),
-                                    ExprKind::Unary(UnOp::Neg, e)
-                                        if matches!(&e.kind, ExprKind::Lit(_)) =>
-                                    {
-                                        (true, false)
-                                    }
-                                    _ => (false, false),
-                                };
-                            mk_delimited(
-                                expr.span,
-                                MetaVarKind::Expr {
-                                    kind: *kind,
-                                    can_begin_literal_maybe_minus,
-                                    can_begin_string_literal,
-                                },
-                                TokenStream::from_ast(expr),
-                            )
-                        }
-                        MatchedSingle(ParseNtResult::Literal(lit)) => {
-                            mk_delimited(lit.span, MetaVarKind::Literal, TokenStream::from_ast(lit))
-                        }
-                        MatchedSingle(ParseNtResult::Ty(ty)) => {
-                            let is_path = matches!(&ty.kind, TyKind::Path(None, _path));
-                            mk_delimited(
-                                ty.span,
-                                MetaVarKind::Ty { is_path },
-                                TokenStream::from_ast(ty),
-                            )
-                        }
-                        MatchedSingle(ParseNtResult::Meta(attr_item)) => {
-                            let has_meta_form = attr_item.meta_kind().is_some();
-                            mk_delimited(
-                                attr_item.span(),
-                                MetaVarKind::Meta { has_meta_form },
-                                TokenStream::from_ast(attr_item),
-                            )
-                        }
-                        MatchedSingle(ParseNtResult::Path(path)) => {
-                            mk_delimited(path.span, MetaVarKind::Path, TokenStream::from_ast(path))
-                        }
-                        MatchedSingle(ParseNtResult::Vis(vis)) => {
-                            mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis))
-                        }
-                        MatchedSeq(..) => {
-                            // We were unable to descend far enough. This is an error.
-                            return Err(dcx.create_err(VarStillRepeating { span: sp, ident }));
-                        }
-                    };
-                    result.push(tt)
-                } else {
-                    // If we aren't able to match the meta-var, we push it back into the result but
-                    // with modified syntax context. (I believe this supports nested macros).
-                    marker.mark_span(&mut sp);
-                    marker.mark_span(&mut original_ident.span);
-                    result.push(TokenTree::token_joint_hidden(token::Dollar, sp));
-                    result.push(TokenTree::Token(
-                        Token::from_ast_ident(original_ident),
-                        Spacing::Alone,
-                    ));
-                }
+            &mbe::TokenTree::MetaVar(sp, original_ident) => {
+                transcribe_metavar(&mut tscx, sp, original_ident)?;
             }
 
             // Replace meta-variable expressions with the result of their expansion.
-            mbe::TokenTree::MetaVarExpr(sp, expr) => {
-                transcribe_metavar_expr(
-                    dcx,
-                    expr,
-                    interp,
-                    &mut marker,
-                    &repeats,
-                    &mut result,
-                    sp,
-                    &psess.symbol_gallery,
-                )?;
+            mbe::TokenTree::MetaVarExpr(dspan, expr) => {
+                transcribe_metavar_expr(&mut tscx, *dspan, expr)?;
             }
 
             // If we are entering a new delimiter, we push its contents to the `stack` to be
@@ -427,27 +265,326 @@ pub(super) fn transcribe<'a>(
             // jump back out of the Delimited, pop the result_stack and add the new results back to
             // the previous results (from outside the Delimited).
             &mbe::TokenTree::Delimited(mut span, ref spacing, ref delimited) => {
-                marker.mark_span(&mut span.open);
-                marker.mark_span(&mut span.close);
-                stack.push(Frame::new_delimited(delimited, span, *spacing));
-                result_stack.push(mem::take(&mut result));
+                tscx.marker.mark_span(&mut span.open);
+                tscx.marker.mark_span(&mut span.close);
+                tscx.stack.push(Frame::new_delimited(delimited, span, *spacing));
+                tscx.result_stack.push(mem::take(&mut tscx.result));
             }
 
             // Nothing much to do here. Just push the token to the result, being careful to
             // preserve syntax context.
             &mbe::TokenTree::Token(mut token) => {
-                marker.mark_span(&mut token.span);
+                tscx.marker.mark_span(&mut token.span);
                 if let token::NtIdent(ident, _) | token::NtLifetime(ident, _) = &mut token.kind {
-                    marker.mark_span(&mut ident.span);
+                    tscx.marker.mark_span(&mut ident.span);
                 }
                 let tt = TokenTree::Token(token, Spacing::Alone);
-                result.push(tt);
+                tscx.result.push(tt);
             }
 
             // There should be no meta-var declarations in the invocation of a macro.
-            mbe::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl`"),
+            mbe::TokenTree::MetaVarDecl { .. } => panic!("unexpected `TokenTree::MetaVarDecl`"),
+        }
+    }
+}
+
+/// Turn `$(...)*` sequences into tokens.
+fn transcribe_sequence<'tx, 'itp>(
+    tscx: &mut TranscrCtx<'tx, 'itp>,
+    seq: &mbe::TokenTree,
+    seq_rep: &'itp mbe::SequenceRepetition,
+) -> PResult<'tx, ()> {
+    let dcx = tscx.psess.dcx();
+
+    // We are descending into a sequence. We first make sure that the matchers in the RHS
+    // and the matches in `interp` have the same shape. Otherwise, either the caller or the
+    // macro writer has made a mistake.
+    match lockstep_iter_size(seq, tscx.interp, &tscx.repeats) {
+        LockstepIterSize::Unconstrained => {
+            return Err(dcx.create_err(NoSyntaxVarsExprRepeat { span: seq.span() }));
+        }
+
+        LockstepIterSize::Contradiction(msg) => {
+            // FIXME: this really ought to be caught at macro definition time... It
+            // happens when two meta-variables are used in the same repetition in a
+            // sequence, but they come from different sequence matchers and repeat
+            // different amounts.
+            return Err(dcx.create_err(MetaVarsDifSeqMatchers { span: seq.span(), msg }));
+        }
+
+        LockstepIterSize::Constraint(len, _) => {
+            // We do this to avoid an extra clone above. We know that this is a
+            // sequence already.
+            let mbe::TokenTree::Sequence(sp, seq) = seq else { unreachable!() };
+
+            // Is the repetition empty?
+            if len == 0 {
+                if seq.kleene.op == KleeneOp::OneOrMore {
+                    // FIXME: this really ought to be caught at macro definition
+                    // time... It happens when the Kleene operator in the matcher and
+                    // the body for the same meta-variable do not match.
+                    return Err(dcx.create_err(MustRepeatOnce { span: sp.entire() }));
+                }
+            } else {
+                // 0 is the initial counter (we have done 0 repetitions so far). `len`
+                // is the total number of repetitions we should generate.
+                tscx.repeats.push((0, len));
+
+                // The first time we encounter the sequence we push it to the stack. It
+                // then gets reused (see the beginning of the loop) until we are done
+                // repeating.
+                tscx.stack.push(Frame::new_sequence(seq_rep, seq.separator.clone(), seq.kleene.op));
+            }
+        }
+    }
+
+    Ok(())
+}
+
+/// Find the matched nonterminal from the macro invocation, and use it to replace
+/// the meta-var.
+///
+/// We use `Spacing::Alone` everywhere here, because that's the conservative choice
+/// and spacing of declarative macros is tricky. E.g. in this macro:
+/// ```
+/// macro_rules! idents {
+///     ($($a:ident,)*) => { stringify!($($a)*) }
+/// }
+/// ```
+/// `$a` has no whitespace after it and will be marked `JointHidden`. If you then
+/// call `idents!(x,y,z,)`, each of `x`, `y`, and `z` will be marked as `Joint`. So
+/// if you choose to use `$x`'s spacing or the identifier's spacing, you'll end up
+/// producing "xyz", which is bad because it effectively merges tokens.
+/// `Spacing::Alone` is the safer option. Fortunately, `space_between` will avoid
+/// some of the unnecessary whitespace.
+fn transcribe_metavar<'tx>(
+    tscx: &mut TranscrCtx<'tx, '_>,
+    mut sp: Span,
+    mut original_ident: Ident,
+) -> PResult<'tx, ()> {
+    let dcx = tscx.psess.dcx();
+
+    let ident = MacroRulesNormalizedIdent::new(original_ident);
+    let Some(cur_matched) = lookup_cur_matched(ident, tscx.interp, &tscx.repeats) else {
+        // If we aren't able to match the meta-var, we push it back into the result but
+        // with modified syntax context. (I believe this supports nested macros).
+        tscx.marker.mark_span(&mut sp);
+        tscx.marker.mark_span(&mut original_ident.span);
+        tscx.result.push(TokenTree::token_joint_hidden(token::Dollar, sp));
+        tscx.result.push(TokenTree::Token(Token::from_ast_ident(original_ident), Spacing::Alone));
+        return Ok(());
+    };
+
+    // We wrap the tokens in invisible delimiters, unless they are already wrapped
+    // in invisible delimiters with the same `MetaVarKind`. Because some proc
+    // macros can't handle multiple layers of invisible delimiters of the same
+    // `MetaVarKind`. This loses some span info, though it hopefully won't matter.
+    let mut mk_delimited = |mk_span, mv_kind, mut stream: TokenStream| {
+        if stream.len() == 1 {
+            let tree = stream.iter().next().unwrap();
+            if let TokenTree::Delimited(_, _, delim, inner) = tree
+                && let Delimiter::Invisible(InvisibleOrigin::MetaVar(mvk)) = delim
+                && mv_kind == *mvk
+            {
+                stream = inner.clone();
+            }
+        }
+
+        // Emit as a token stream within `Delimiter::Invisible` to maintain
+        // parsing priorities.
+        tscx.marker.mark_span(&mut sp);
+        with_metavar_spans(|mspans| mspans.insert(mk_span, sp));
+        // Both the open delim and close delim get the same span, which covers the
+        // `$foo` in the decl macro RHS.
+        TokenTree::Delimited(
+            DelimSpan::from_single(sp),
+            DelimSpacing::new(Spacing::Alone, Spacing::Alone),
+            Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)),
+            stream,
+        )
+    };
+
+    let tt = match cur_matched {
+        MatchedSingle(ParseNtResult::Tt(tt)) => {
+            // `tt`s are emitted into the output stream directly as "raw tokens",
+            // without wrapping them into groups. Other variables are emitted into
+            // the output stream as groups with `Delimiter::Invisible` to maintain
+            // parsing priorities.
+            maybe_use_metavar_location(tscx.psess, &tscx.stack, sp, tt, &mut tscx.marker)
+        }
+        MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => {
+            tscx.marker.mark_span(&mut sp);
+            with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
+            let kind = token::NtIdent(*ident, *is_raw);
+            TokenTree::token_alone(kind, sp)
+        }
+        MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => {
+            tscx.marker.mark_span(&mut sp);
+            with_metavar_spans(|mspans| mspans.insert(ident.span, sp));
+            let kind = token::NtLifetime(*ident, *is_raw);
+            TokenTree::token_alone(kind, sp)
+        }
+        MatchedSingle(ParseNtResult::Item(item)) => {
+            mk_delimited(item.span, MetaVarKind::Item, TokenStream::from_ast(item))
+        }
+        MatchedSingle(ParseNtResult::Block(block)) => {
+            mk_delimited(block.span, MetaVarKind::Block, TokenStream::from_ast(block))
+        }
+        MatchedSingle(ParseNtResult::Stmt(stmt)) => {
+            let stream = if let StmtKind::Empty = stmt.kind {
+                // FIXME: Properly collect tokens for empty statements.
+                TokenStream::token_alone(token::Semi, stmt.span)
+            } else {
+                TokenStream::from_ast(stmt)
+            };
+            mk_delimited(stmt.span, MetaVarKind::Stmt, stream)
+        }
+        MatchedSingle(ParseNtResult::Pat(pat, pat_kind)) => {
+            mk_delimited(pat.span, MetaVarKind::Pat(*pat_kind), TokenStream::from_ast(pat))
+        }
+        MatchedSingle(ParseNtResult::Expr(expr, kind)) => {
+            let (can_begin_literal_maybe_minus, can_begin_string_literal) = match &expr.kind {
+                ExprKind::Lit(_) => (true, true),
+                ExprKind::Unary(UnOp::Neg, e) if matches!(&e.kind, ExprKind::Lit(_)) => {
+                    (true, false)
+                }
+                _ => (false, false),
+            };
+            mk_delimited(
+                expr.span,
+                MetaVarKind::Expr {
+                    kind: *kind,
+                    can_begin_literal_maybe_minus,
+                    can_begin_string_literal,
+                },
+                TokenStream::from_ast(expr),
+            )
+        }
+        MatchedSingle(ParseNtResult::Literal(lit)) => {
+            mk_delimited(lit.span, MetaVarKind::Literal, TokenStream::from_ast(lit))
+        }
+        MatchedSingle(ParseNtResult::Ty(ty)) => {
+            let is_path = matches!(&ty.kind, TyKind::Path(None, _path));
+            mk_delimited(ty.span, MetaVarKind::Ty { is_path }, TokenStream::from_ast(ty))
+        }
+        MatchedSingle(ParseNtResult::Meta(attr_item)) => {
+            let has_meta_form = attr_item.meta_kind().is_some();
+            mk_delimited(
+                attr_item.span(),
+                MetaVarKind::Meta { has_meta_form },
+                TokenStream::from_ast(attr_item),
+            )
+        }
+        MatchedSingle(ParseNtResult::Path(path)) => {
+            mk_delimited(path.span, MetaVarKind::Path, TokenStream::from_ast(path))
+        }
+        MatchedSingle(ParseNtResult::Vis(vis)) => {
+            mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis))
+        }
+        MatchedSeq(..) => {
+            // We were unable to descend far enough. This is an error.
+            return Err(dcx.create_err(VarStillRepeating { span: sp, ident }));
+        }
+    };
+
+    tscx.result.push(tt);
+    Ok(())
+}
+
+/// Turn `${expr(...)}` metavariable expressionss into tokens.
+fn transcribe_metavar_expr<'tx>(
+    tscx: &mut TranscrCtx<'tx, '_>,
+    dspan: DelimSpan,
+    expr: &MetaVarExpr,
+) -> PResult<'tx, ()> {
+    let dcx = tscx.psess.dcx();
+    let tt = match *expr {
+        MetaVarExpr::Concat(ref elements) => metavar_expr_concat(tscx, dspan, elements)?,
+        MetaVarExpr::Count(original_ident, depth) => {
+            let matched = matched_from_ident(dcx, original_ident, tscx.interp)?;
+            let count = count_repetitions(dcx, depth, matched, &tscx.repeats, &dspan)?;
+            TokenTree::token_alone(
+                TokenKind::lit(token::Integer, sym::integer(count), None),
+                tscx.visited_dspan(dspan),
+            )
+        }
+        MetaVarExpr::Ignore(original_ident) => {
+            // Used to ensure that `original_ident` is present in the LHS
+            let _ = matched_from_ident(dcx, original_ident, tscx.interp)?;
+            return Ok(());
         }
+        MetaVarExpr::Index(depth) => match tscx.repeats.iter().nth_back(depth) {
+            Some((index, _)) => TokenTree::token_alone(
+                TokenKind::lit(token::Integer, sym::integer(*index), None),
+                tscx.visited_dspan(dspan),
+            ),
+            None => {
+                return Err(out_of_bounds_err(dcx, tscx.repeats.len(), dspan.entire(), "index"));
+            }
+        },
+        MetaVarExpr::Len(depth) => match tscx.repeats.iter().nth_back(depth) {
+            Some((_, length)) => TokenTree::token_alone(
+                TokenKind::lit(token::Integer, sym::integer(*length), None),
+                tscx.visited_dspan(dspan),
+            ),
+            None => {
+                return Err(out_of_bounds_err(dcx, tscx.repeats.len(), dspan.entire(), "len"));
+            }
+        },
+    };
+    tscx.result.push(tt);
+    Ok(())
+}
+
+/// Handle the `${concat(...)}` metavariable expression.
+fn metavar_expr_concat<'tx>(
+    tscx: &mut TranscrCtx<'tx, '_>,
+    dspan: DelimSpan,
+    elements: &[MetaVarExprConcatElem],
+) -> PResult<'tx, TokenTree> {
+    let dcx = tscx.psess.dcx();
+    let mut concatenated = String::new();
+    for element in elements.into_iter() {
+        let symbol = match element {
+            MetaVarExprConcatElem::Ident(elem) => elem.name,
+            MetaVarExprConcatElem::Literal(elem) => *elem,
+            MetaVarExprConcatElem::Var(ident) => {
+                match matched_from_ident(dcx, *ident, tscx.interp)? {
+                    NamedMatch::MatchedSeq(named_matches) => {
+                        let Some((curr_idx, _)) = tscx.repeats.last() else {
+                            return Err(dcx.struct_span_err(dspan.entire(), "invalid syntax"));
+                        };
+                        match &named_matches[*curr_idx] {
+                            // FIXME(c410-f3r) Nested repetitions are unimplemented
+                            MatchedSeq(_) => unimplemented!(),
+                            MatchedSingle(pnr) => extract_symbol_from_pnr(dcx, pnr, ident.span)?,
+                        }
+                    }
+                    NamedMatch::MatchedSingle(pnr) => {
+                        extract_symbol_from_pnr(dcx, pnr, ident.span)?
+                    }
+                }
+            }
+        };
+        concatenated.push_str(symbol.as_str());
+    }
+    let symbol = nfc_normalize(&concatenated);
+    let concatenated_span = tscx.visited_dspan(dspan);
+    if !rustc_lexer::is_ident(symbol.as_str()) {
+        return Err(dcx.struct_span_err(
+            concatenated_span,
+            "`${concat(..)}` is not generating a valid identifier",
+        ));
     }
+    tscx.psess.symbol_gallery.insert(symbol, concatenated_span);
+
+    // The current implementation marks the span as coming from the macro regardless of
+    // contexts of the concatenated identifiers but this behavior may change in the
+    // future.
+    Ok(TokenTree::Token(
+        Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
+        Spacing::Alone,
+    ))
 }
 
 /// Store the metavariable span for this original span into a side table.
@@ -639,7 +776,7 @@ fn lockstep_iter_size(
                 size.with(lockstep_iter_size(tt, interpolations, repeats))
             })
         }
-        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
+        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl { name, .. } => {
             let name = MacroRulesNormalizedIdent::new(*name);
             match lookup_cur_matched(name, interpolations, repeats) {
                 Some(matched) => match matched {
@@ -671,13 +808,13 @@ fn lockstep_iter_size(
 /// * `[ $( ${count(foo, 0)} ),* ]` will be the same as `[ $( ${count(foo)} ),* ]`
 /// * `[ $( ${count(foo, 1)} ),* ]` will return an error because `${count(foo, 1)}` is
 ///   declared inside a single repetition and the index `1` implies two nested repetitions.
-fn count_repetitions<'a>(
-    dcx: DiagCtxtHandle<'a>,
+fn count_repetitions<'dx>(
+    dcx: DiagCtxtHandle<'dx>,
     depth_user: usize,
     mut matched: &NamedMatch,
     repeats: &[(usize, usize)],
     sp: &DelimSpan,
-) -> PResult<'a, usize> {
+) -> PResult<'dx, usize> {
     // Recursively count the number of matches in `matched` at given depth
     // (or at the top-level of `matched` if no depth is given).
     fn count<'a>(depth_curr: usize, depth_max: usize, matched: &NamedMatch) -> PResult<'a, usize> {
@@ -762,102 +899,6 @@ fn out_of_bounds_err<'a>(dcx: DiagCtxtHandle<'a>, max: usize, span: Span, ty: &s
     dcx.struct_span_err(span, msg)
 }
 
-fn transcribe_metavar_expr<'a>(
-    dcx: DiagCtxtHandle<'a>,
-    expr: &MetaVarExpr,
-    interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
-    marker: &mut Marker,
-    repeats: &[(usize, usize)],
-    result: &mut Vec<TokenTree>,
-    sp: &DelimSpan,
-    symbol_gallery: &SymbolGallery,
-) -> PResult<'a, ()> {
-    let mut visited_span = || {
-        let mut span = sp.entire();
-        marker.mark_span(&mut span);
-        span
-    };
-    match *expr {
-        MetaVarExpr::Concat(ref elements) => {
-            let mut concatenated = String::new();
-            for element in elements.into_iter() {
-                let symbol = match element {
-                    MetaVarExprConcatElem::Ident(elem) => elem.name,
-                    MetaVarExprConcatElem::Literal(elem) => *elem,
-                    MetaVarExprConcatElem::Var(ident) => {
-                        match matched_from_ident(dcx, *ident, interp)? {
-                            NamedMatch::MatchedSeq(named_matches) => {
-                                let Some((curr_idx, _)) = repeats.last() else {
-                                    return Err(dcx.struct_span_err(sp.entire(), "invalid syntax"));
-                                };
-                                match &named_matches[*curr_idx] {
-                                    // FIXME(c410-f3r) Nested repetitions are unimplemented
-                                    MatchedSeq(_) => unimplemented!(),
-                                    MatchedSingle(pnr) => {
-                                        extract_symbol_from_pnr(dcx, pnr, ident.span)?
-                                    }
-                                }
-                            }
-                            NamedMatch::MatchedSingle(pnr) => {
-                                extract_symbol_from_pnr(dcx, pnr, ident.span)?
-                            }
-                        }
-                    }
-                };
-                concatenated.push_str(symbol.as_str());
-            }
-            let symbol = nfc_normalize(&concatenated);
-            let concatenated_span = visited_span();
-            if !rustc_lexer::is_ident(symbol.as_str()) {
-                return Err(dcx.struct_span_err(
-                    concatenated_span,
-                    "`${concat(..)}` is not generating a valid identifier",
-                ));
-            }
-            symbol_gallery.insert(symbol, concatenated_span);
-            // The current implementation marks the span as coming from the macro regardless of
-            // contexts of the concatenated identifiers but this behavior may change in the
-            // future.
-            result.push(TokenTree::Token(
-                Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
-                Spacing::Alone,
-            ));
-        }
-        MetaVarExpr::Count(original_ident, depth) => {
-            let matched = matched_from_ident(dcx, original_ident, interp)?;
-            let count = count_repetitions(dcx, depth, matched, repeats, sp)?;
-            let tt = TokenTree::token_alone(
-                TokenKind::lit(token::Integer, sym::integer(count), None),
-                visited_span(),
-            );
-            result.push(tt);
-        }
-        MetaVarExpr::Ignore(original_ident) => {
-            // Used to ensure that `original_ident` is present in the LHS
-            let _ = matched_from_ident(dcx, original_ident, interp)?;
-        }
-        MetaVarExpr::Index(depth) => match repeats.iter().nth_back(depth) {
-            Some((index, _)) => {
-                result.push(TokenTree::token_alone(
-                    TokenKind::lit(token::Integer, sym::integer(*index), None),
-                    visited_span(),
-                ));
-            }
-            None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "index")),
-        },
-        MetaVarExpr::Len(depth) => match repeats.iter().nth_back(depth) {
-            Some((_, length)) => {
-                result.push(TokenTree::token_alone(
-                    TokenKind::lit(token::Integer, sym::integer(*length), None),
-                    visited_span(),
-                ));
-            }
-            None => return Err(out_of_bounds_err(dcx, repeats.len(), sp.entire(), "len")),
-        },
-    }
-    Ok(())
-}
-
 /// Extracts an metavariable symbol that can be an identifier, a token tree or a literal.
 fn extract_symbol_from_pnr<'a>(
     dcx: DiagCtxtHandle<'a>,
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index 2c486a02bdf..6e1c6df4bcb 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -339,9 +339,9 @@ impl MutVisitor for PlaceholderExpander {
         }
     }
 
-    fn visit_method_receiver_expr(&mut self, expr: &mut P<ast::Expr>) {
+    fn visit_method_receiver_expr(&mut self, expr: &mut ast::Expr) {
         match expr.kind {
-            ast::ExprKind::MacCall(_) => *expr = self.remove(expr.id).make_method_receiver_expr(),
+            ast::ExprKind::MacCall(_) => *expr = *self.remove(expr.id).make_method_receiver_expr(),
             _ => walk_expr(self, expr),
         }
     }
diff --git a/compiler/rustc_expand/src/stats.rs b/compiler/rustc_expand/src/stats.rs
index 6b2ad30dffd..b4c4eac028f 100644
--- a/compiler/rustc_expand/src/stats.rs
+++ b/compiler/rustc_expand/src/stats.rs
@@ -15,15 +15,11 @@ pub struct MacroStat {
     /// Number of uses of the macro.
     pub uses: usize,
 
-    /// Net increase in number of lines of code (when pretty-printed), i.e.
-    /// `lines(output) - lines(invocation)`. Can be negative because a macro
-    /// output may be smaller than the invocation.
-    pub lines: isize,
-
-    /// Net increase in number of lines of code (when pretty-printed), i.e.
-    /// `bytes(output) - bytes(invocation)`. Can be negative because a macro
-    /// output may be smaller than the invocation.
-    pub bytes: isize,
+    /// Number of lines of code (when pretty-printed).
+    pub lines: usize,
+
+    /// Number of bytes of code (when pretty-printed).
+    pub bytes: usize,
 }
 
 pub(crate) fn elems_to_string<T>(elems: &SmallVec<[T; 1]>, f: impl Fn(&T) -> String) -> String {
@@ -131,16 +127,12 @@ pub(crate) fn update_macro_stats(
     input: &str,
     fragment: &AstFragment,
 ) {
-    fn lines_and_bytes(s: &str) -> (usize, usize) {
-        (s.trim_end().split('\n').count(), s.len())
-    }
-
     // Measure the size of the output by pretty-printing it and counting
     // the lines and bytes.
     let name = Symbol::intern(&pprust::path_to_string(path));
     let output = fragment.to_string();
-    let (in_l, in_b) = lines_and_bytes(input);
-    let (out_l, out_b) = lines_and_bytes(&output);
+    let num_lines = output.trim_end().split('\n').count();
+    let num_bytes = output.len();
 
     // This code is useful for debugging `-Zmacro-stats`. For every
     // invocation it prints the full input and output.
@@ -157,7 +149,7 @@ pub(crate) fn update_macro_stats(
             {name}: [{crate_name}] ({fragment_kind:?}) {span}\n\
             -------------------------------\n\
             {input}\n\
-            -- ({in_l} lines, {in_b} bytes) --> ({out_l} lines, {out_b} bytes) --\n\
+            -- {num_lines} lines, {num_bytes} bytes --\n\
             {output}\n\
         "
         );
@@ -166,6 +158,6 @@ pub(crate) fn update_macro_stats(
     // The recorded size is the difference between the input and the output.
     let entry = ecx.macro_stats.entry((name, macro_kind)).or_insert(MacroStat::default());
     entry.uses += 1;
-    entry.lines += out_l as isize - in_l as isize;
-    entry.bytes += out_b as isize - in_b as isize;
+    entry.lines += num_lines;
+    entry.bytes += num_bytes;
 }
diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml
index a5ae06473cb..78d7b698b72 100644
--- a/compiler/rustc_feature/Cargo.toml
+++ b/compiler/rustc_feature/Cargo.toml
@@ -5,8 +5,9 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
+rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_span = { path = "../rustc_span" }
-serde = { version = "1.0.125", features = [ "derive" ] }
+serde = { version = "1.0.125", features = ["derive"] }
 serde_json = "1.0.59"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index b5218ec267c..8e1392998d4 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -5,6 +5,7 @@ use std::sync::LazyLock;
 use AttributeDuplicates::*;
 use AttributeGate::*;
 use AttributeType::*;
+use rustc_attr_data_structures::EncodeCrossCrate;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_span::edition::Edition;
 use rustc_span::{Symbol, sym};
@@ -368,12 +369,6 @@ macro_rules! experimental {
     };
 }
 
-#[derive(PartialEq)]
-pub enum EncodeCrossCrate {
-    Yes,
-    No,
-}
-
 pub struct BuiltinAttribute {
     pub name: Symbol,
     /// Whether this attribute is encode cross crate.
@@ -495,6 +490,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
     ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No),
+    gated!(align, Normal, template!(List: "alignment"), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(align)),
     ungated!(unsafe(Edition2024) export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
     ungated!(unsafe(Edition2024) link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
     ungated!(unsafe(Edition2024) no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
@@ -656,6 +652,19 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         EncodeCrossCrate::Yes, min_generic_const_args, experimental!(type_const),
     ),
 
+    // The `#[loop_match]` and `#[const_continue]` attributes are part of the
+    // lang experiment for RFC 3720 tracked in:
+    //
+    // - https://github.com/rust-lang/rust/issues/132306
+    gated!(
+        const_continue, Normal, template!(Word), ErrorFollowing,
+        EncodeCrossCrate::No, loop_match, experimental!(const_continue)
+    ),
+    gated!(
+        loop_match, Normal, template!(Word), ErrorFollowing,
+        EncodeCrossCrate::No, loop_match, experimental!(loop_match)
+    ),
+
     // ==========================================================================
     // Internal attributes: Stability, deprecation, and unsafe:
     // ==========================================================================
@@ -709,7 +718,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     rustc_attr!(
         rustc_pub_transparent, Normal, template!(Word),
-        WarnFollowing, EncodeCrossCrate::Yes,
+        ErrorFollowing, EncodeCrossCrate::Yes,
         "used internally to mark types with a `transparent` representation when it is guaranteed by the documentation",
     ),
 
@@ -1082,7 +1091,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "the `#[rustc_main]` attribute is used internally to specify test entry point function",
     ),
     rustc_attr!(
-        rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), WarnFollowing,
+        rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), ErrorFollowing,
         EncodeCrossCrate::No,
         "the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \
         from method dispatch when the receiver is of the following type, for compatibility in \
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index 0cd090b25a4..ddb99585bc2 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -54,6 +54,7 @@ declare_features! (
 
     /// Allows using the `amdgpu-kernel` ABI.
     (removed, abi_amdgpu_kernel, "1.77.0", Some(51575), None, 120495),
+    (removed, abi_c_cmse_nonsecure_call, "CURRENT_RUSTC_VERSION", Some(81391), Some("renamed to abi_cmse_nonsecure_call"), 142146),
     (removed, advanced_slice_patterns, "1.42.0", Some(62254),
      Some("merged into `#![feature(slice_patterns)]`"), 67712),
     (removed, allocator, "1.0.0", None, None),
@@ -285,4 +286,18 @@ declare_features! (
     // -------------------------------------------------------------------------
     // feature-group-end: removed features
     // -------------------------------------------------------------------------
+
+
+    // -------------------------------------------------------------------------
+    // feature-group-start: removed library features
+    // -------------------------------------------------------------------------
+    //
+    // FIXME(#141617): we should have a better way to track removed library features, but we reuse
+    // the infrastructure here so users still get hints. The symbols used here can be remove from
+    // `symbol.rs` when that happens.
+    (removed, concat_idents, "CURRENT_RUSTC_VERSION", Some(29599),
+     Some("use the `${concat(..)}` metavariable expression instead"), 142704),
+    // -------------------------------------------------------------------------
+    // feature-group-end: removed library features
+    // -------------------------------------------------------------------------
 );
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index a7bc6207149..e73a4e1766c 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -353,8 +353,8 @@ declare_features! (
 
     /// Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`.
     (unstable, abi_avr_interrupt, "1.45.0", Some(69664)),
-    /// Allows `extern "C-cmse-nonsecure-call" fn()`.
-    (unstable, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391)),
+    /// Allows `extern "cmse-nonsecure-call" fn()`.
+    (unstable, abi_cmse_nonsecure_call, "CURRENT_RUSTC_VERSION", Some(81391)),
     /// Allows `extern "custom" fn()`.
     (unstable, abi_custom, "CURRENT_RUSTC_VERSION", Some(140829)),
     /// Allows `extern "gpu-kernel" fn()`.
@@ -431,7 +431,7 @@ declare_features! (
     (unstable, closure_lifetime_binder, "1.64.0", Some(97362)),
     /// Allows `#[track_caller]` on closures and coroutines.
     (unstable, closure_track_caller, "1.57.0", Some(87417)),
-    /// Allows `extern "C-cmse-nonsecure-entry" fn()`.
+    /// Allows `extern "cmse-nonsecure-entry" fn()`.
     (unstable, cmse_nonsecure_entry, "1.48.0", Some(75835)),
     /// Allows `async {}` expressions in const contexts.
     (unstable, const_async_blocks, "1.53.0", Some(85368)),
@@ -510,7 +510,7 @@ declare_features! (
     (unstable, ffi_pure, "1.45.0", Some(58329)),
     /// Controlling the behavior of fmt::Debug
     (unstable, fmt_debug, "1.82.0", Some(129709)),
-    /// Allows using `#[repr(align(...))]` on function items
+    /// Allows using `#[align(...)]` on function items
     (unstable, fn_align, "1.53.0", Some(82232)),
     /// Support delegating implementation of functions to other already implemented functions.
     (incomplete, fn_delegation, "1.76.0", Some(118212)),
@@ -557,6 +557,8 @@ declare_features! (
     /// Allows using `#[link(kind = "link-arg", name = "...")]`
     /// to pass custom arguments to the linker.
     (unstable, link_arg_attribute, "1.76.0", Some(99427)),
+    /// Allows fused `loop`/`match` for direct intraprocedural jumps.
+    (incomplete, loop_match, "CURRENT_RUSTC_VERSION", Some(132306)),
     /// Give access to additional metadata about declarative macro meta-variables.
     (unstable, macro_metavar_expr, "1.61.0", Some(83527)),
     /// Provides a way to concatenate identifiers using metavariable expressions.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 556f50a85af..679904c7cfe 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1302,6 +1302,7 @@ impl AttributeExt for Attribute {
             // FIXME: should not be needed anymore when all attrs are parsed
             Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
+            Attribute::Parsed(AttributeKind::MayDangle(span)) => *span,
             a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
         }
     }
@@ -1345,12 +1346,13 @@ impl AttributeExt for Attribute {
         }
     }
 
-    #[inline]
-    fn style(&self) -> AttrStyle {
-        match &self {
-            Attribute::Unparsed(u) => u.style,
-            Attribute::Parsed(AttributeKind::DocComment { style, .. }) => *style,
-            _ => panic!(),
+    fn doc_resolution_scope(&self) -> Option<AttrStyle> {
+        match self {
+            Attribute::Parsed(AttributeKind::DocComment { style, .. }) => Some(*style),
+            Attribute::Unparsed(attr) if self.has_name(sym::doc) && self.value_str().is_some() => {
+                Some(attr.style)
+            }
+            _ => None,
         }
     }
 }
@@ -1441,11 +1443,6 @@ impl Attribute {
     pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
         AttributeExt::doc_str_and_comment_kind(self)
     }
-
-    #[inline]
-    pub fn style(&self) -> AttrStyle {
-        AttributeExt::style(self)
-    }
 }
 
 /// Attributes owned by a HIR owner.
@@ -2285,16 +2282,9 @@ pub struct Expr<'hir> {
 }
 
 impl Expr<'_> {
-    pub fn precedence(
-        &self,
-        for_each_attr: &dyn Fn(HirId, &mut dyn FnMut(&Attribute)),
-    ) -> ExprPrecedence {
+    pub fn precedence(&self, has_attr: &dyn Fn(HirId) -> bool) -> ExprPrecedence {
         let prefix_attrs_precedence = || -> ExprPrecedence {
-            let mut has_outer_attr = false;
-            for_each_attr(self.hir_id, &mut |attr: &Attribute| {
-                has_outer_attr |= matches!(attr.style(), AttrStyle::Outer)
-            });
-            if has_outer_attr { ExprPrecedence::Prefix } else { ExprPrecedence::Unambiguous }
+            if has_attr(self.hir_id) { ExprPrecedence::Prefix } else { ExprPrecedence::Unambiguous }
         };
 
         match &self.kind {
@@ -2350,7 +2340,7 @@ impl Expr<'_> {
             | ExprKind::Use(..)
             | ExprKind::Err(_) => prefix_attrs_precedence(),
 
-            ExprKind::DropTemps(expr, ..) => expr.precedence(for_each_attr),
+            ExprKind::DropTemps(expr, ..) => expr.precedence(has_attr),
         }
     }
 
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index f768bd157ab..4ec2bbfc5ab 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -1,5 +1,5 @@
 hir_analysis_abi_custom_clothed_function =
-    items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    items with the "custom" ABI can only be declared externally or defined via naked functions
     .suggestion = convert this to an `#[unsafe(naked)]` function
 
 hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`
@@ -46,6 +46,9 @@ hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the {$
 
 hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes
 
+hir_analysis_async_drop_without_sync_drop = `AsyncDrop` impl without `Drop` impl
+    .help = type implementing `AsyncDrop` trait must also implement `Drop` trait to be used in sync context and unwinds
+
 hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit while auto-dereferencing `{$ty}`
     .label = deref recursion limit reached
     .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`)
@@ -70,10 +73,10 @@ hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are fo
     .label = `for<...>` is here
 
 hir_analysis_cmse_call_generic =
-    function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
+    function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type
 
 hir_analysis_cmse_entry_generic =
-    functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+    functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type
 
 hir_analysis_cmse_inputs_stack_spill =
     arguments for `{$abi}` function too large to pass via registers
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 32fec0604c0..485dd1d2204 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1,8 +1,9 @@
 use std::cell::LazyCell;
 use std::ops::ControlFlow;
 
-use rustc_abi::FieldIdx;
+use rustc_abi::{ExternAbi, FieldIdx};
 use rustc_attr_data_structures::ReprAttr::ReprPacked;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::codes::*;
 use rustc_errors::{EmissionGuarantee, MultiSpan};
@@ -12,7 +13,6 @@ use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::{Obligation, ObligationCauseCode};
 use rustc_lint_defs::builtin::{
     REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS,
-    UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
 };
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
@@ -52,49 +52,22 @@ fn add_abi_diag_help<T: EmissionGuarantee>(abi: ExternAbi, diag: &mut Diag<'_, T
 }
 
 pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi) {
-    // FIXME: this should be checked earlier, e.g. in `rustc_ast_lowering`, to fix
-    // things like #86232.
+    // FIXME: This should be checked earlier, e.g. in `rustc_ast_lowering`, as this
+    // currently only guards function imports, function definitions, and function pointer types.
+    // Functions in trait declarations can still use "deprecated" ABIs without any warning.
 
     match AbiMap::from_target(&tcx.sess.target).canonize_abi(abi, false) {
         AbiMapping::Direct(..) => (),
+        // already erred in rustc_ast_lowering
         AbiMapping::Invalid => {
-            let mut err = struct_span_code_err!(
-                tcx.dcx(),
-                span,
-                E0570,
-                "`{abi}` is not a supported ABI for the current target",
-            );
-            add_abi_diag_help(abi, &mut err);
-            err.emit();
+            tcx.dcx().span_delayed_bug(span, format!("{abi} should be rejected in ast_lowering"));
         }
         AbiMapping::Deprecated(..) => {
             tcx.node_span_lint(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| {
-                lint.primary_message("use of calling convention not supported on this target");
-                add_abi_diag_help(abi, lint);
-            });
-        }
-    }
-}
-
-pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi) {
-    // This is always an FCW, even for `AbiMapping::Invalid`, since we started linting later than
-    // in `check_abi` above.
-    match AbiMap::from_target(&tcx.sess.target).canonize_abi(abi, false) {
-        AbiMapping::Direct(..) => (),
-        // This is not a redundant match arm: these ABIs started linting after introducing
-        // UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS already existed and we want to
-        // avoid expanding the scope of that lint so it can move to a hard error sooner.
-        AbiMapping::Deprecated(..) => {
-            tcx.node_span_lint(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| {
-                lint.primary_message("use of calling convention not supported on this target");
-                add_abi_diag_help(abi, lint);
-            });
-        }
-        AbiMapping::Invalid => {
-            tcx.node_span_lint(UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, hir_id, span, |lint| {
                 lint.primary_message(format!(
-                    "the calling convention {abi} is not supported on this target"
+                    "{abi} is not a supported ABI for the current target"
                 ));
+                add_abi_diag_help(abi, lint);
             });
         }
     }
@@ -103,7 +76,7 @@ pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ex
 pub fn check_custom_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, fn_sig: FnSig<'_>, fn_sig_span: Span) {
     if fn_sig.abi == ExternAbi::Custom {
         // Function definitions that use `extern "custom"` must be naked functions.
-        if !tcx.has_attr(def_id, sym::naked) {
+        if !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(_)) {
             tcx.dcx().emit_err(crate::errors::AbiCustomClothedFunction {
                 span: fn_sig_span,
                 naked_span: tcx.def_span(def_id).shrink_to_lo(),
@@ -867,6 +840,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             let hir::ItemKind::ForeignMod { abi, items } = it.kind else {
                 return;
             };
+
             check_abi(tcx, it.hir_id(), it.span, abi);
 
             for item in items {
@@ -1384,7 +1358,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
                 ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)),
                 ty::Array(ty, _) => check_non_exhaustive(tcx, *ty),
                 ty::Adt(def, args) => {
-                    if !def.did().is_local() && !tcx.has_attr(def.did(), sym::rustc_pub_transparent)
+                    if !def.did().is_local()
+                        && !attrs::find_attr!(
+                            tcx.get_all_attrs(def.did()),
+                            AttributeKind::PubTransparent(_)
+                        )
                     {
                         let non_exhaustive = def.is_variant_list_non_exhaustive()
                             || def
diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs
index 3bad36da999..b556683e80a 100644
--- a/compiler/rustc_hir_analysis/src/check/entry.rs
+++ b/compiler/rustc_hir_analysis/src/check/entry.rs
@@ -1,14 +1,15 @@
 use std::ops::Not;
 
 use rustc_abi::ExternAbi;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_hir as hir;
 use rustc_hir::Node;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, TyCtxt, TypingMode};
 use rustc_session::config::EntryFnType;
+use rustc_span::Span;
 use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
-use rustc_span::{Span, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
 
@@ -98,8 +99,10 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         error = true;
     }
 
-    for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
-        tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span(), annotated: main_span });
+    if let Some(attr_span) =
+        find_attr!(tcx.get_all_attrs(main_def_id), AttributeKind::TrackCaller(span) => *span)
+    {
+        tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span });
         error = true;
     }
 
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 5b8aa28102c..5cec3331bb1 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -72,8 +72,8 @@ pub mod wfcheck;
 
 use std::num::NonZero;
 
-pub use check::{check_abi, check_abi_fn_ptr, check_custom_abi};
-use rustc_abi::{ExternAbi, VariantIdx};
+pub use check::{check_abi, check_custom_abi};
+use rustc_abi::VariantIdx;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
 use rustc_hir::LangItem;
@@ -114,7 +114,15 @@ pub fn provide(providers: &mut Providers) {
 }
 
 fn adt_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::Destructor> {
-    tcx.calculate_dtor(def_id, always_applicable::check_drop_impl)
+    let dtor = tcx.calculate_dtor(def_id, always_applicable::check_drop_impl);
+    if dtor.is_none() && tcx.features().async_drop() {
+        if let Some(async_dtor) = adt_async_destructor(tcx, def_id) {
+            // When type has AsyncDrop impl, but doesn't have Drop impl, generate error
+            let span = tcx.def_span(async_dtor.impl_did);
+            tcx.dcx().emit_err(errors::AsyncDropWithoutSyncDrop { span });
+        }
+    }
+    dtor
 }
 
 fn adt_async_destructor(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::AsyncDestructor> {
@@ -303,9 +311,7 @@ fn default_body_is_unstable(
         reason: reason_str,
     });
 
-    let inject_span = item_did
-        .as_local()
-        .and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
+    let inject_span = item_did.is_local().then(|| tcx.crate_level_attribute_injection_span());
     rustc_session::parse::add_feature_diagnostics_for_issue(
         &mut err,
         &tcx.sess,
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 20d0e87b7a7..d05e381f8c8 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -382,8 +382,6 @@ fn check_trait_item<'tcx>(
         _ => (None, trait_item.span),
     };
 
-    check_dyn_incompatible_self_trait_by_name(tcx, trait_item);
-
     // Check that an item definition in a subtrait is shadowing a supertrait item.
     lint_item_shadowing_supertrait_item(tcx, def_id);
 
@@ -832,70 +830,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GATArgsCollector<'tcx> {
     }
 }
 
-fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
-    match ty.kind {
-        hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
-            [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
-            _ => false,
-        },
-        _ => false,
-    }
-}
-
-/// Detect when a dyn-incompatible trait is referring to itself in one of its associated items.
-///
-/// In such cases, suggest using `Self` instead.
-fn check_dyn_incompatible_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) {
-    let (trait_ident, trait_def_id) =
-        match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(item.hir_id()).def_id) {
-            hir::Node::Item(item) => match item.kind {
-                hir::ItemKind::Trait(_, _, ident, ..) => (ident, item.owner_id),
-                _ => return,
-            },
-            _ => return,
-        };
-    let mut trait_should_be_self = vec![];
-    match &item.kind {
-        hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty))
-            if could_be_self(trait_def_id.def_id, ty) =>
-        {
-            trait_should_be_self.push(ty.span)
-        }
-        hir::TraitItemKind::Fn(sig, _) => {
-            for ty in sig.decl.inputs {
-                if could_be_self(trait_def_id.def_id, ty) {
-                    trait_should_be_self.push(ty.span);
-                }
-            }
-            match sig.decl.output {
-                hir::FnRetTy::Return(ty) if could_be_self(trait_def_id.def_id, ty) => {
-                    trait_should_be_self.push(ty.span);
-                }
-                _ => {}
-            }
-        }
-        _ => {}
-    }
-    if !trait_should_be_self.is_empty() {
-        if tcx.is_dyn_compatible(trait_def_id) {
-            return;
-        }
-        let sugg = trait_should_be_self.iter().map(|span| (*span, "Self".to_string())).collect();
-        tcx.dcx()
-            .struct_span_err(
-                trait_should_be_self,
-                "associated item referring to unboxed trait object for its own trait",
-            )
-            .with_span_label(trait_ident.span, "in this trait")
-            .with_multipart_suggestion(
-                "you might have meant to use `Self` to refer to the implementing type",
-                sugg,
-                Applicability::MachineApplicable,
-            )
-            .emit();
-    }
-}
-
 fn lint_item_shadowing_supertrait_item<'tcx>(tcx: TyCtxt<'tcx>, trait_item_def_id: LocalDefId) {
     let item_name = tcx.item_name(trait_item_def_id.to_def_id());
     let trait_def_id = tcx.local_parent(trait_item_def_id);
@@ -1064,7 +998,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
                     Ok(..) => Some(vec![(adt_const_params_feature_string, sym::adt_const_params)]),
                 };
                 if let Some(features) = may_suggest_feature {
-                    tcx.disabled_nightly_features(&mut diag, Some(param.hir_id), features);
+                    tcx.disabled_nightly_features(&mut diag, features);
                 }
 
                 Err(diag.emit())
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 6e22ac5a28a..c967e87bfd8 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -21,6 +21,7 @@ use std::ops::Bound;
 
 use rustc_abi::ExternAbi;
 use rustc_ast::Recovered;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_data_structures::unord::UnordMap;
 use rustc_errors::{
@@ -34,16 +35,22 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::util::{Discr, IntTypeExt};
-use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypingMode, fold_regions};
+use rustc_middle::ty::{
+    self, AdtKind, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, TypingMode, fold_regions,
+};
 use rustc_middle::{bug, span_bug};
 use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::{ObligationCtxt, hir_ty_lowering_dyn_compatibility_violations};
+use rustc_trait_selection::traits::{
+    FulfillmentError, ObligationCtxt, hir_ty_lowering_dyn_compatibility_violations,
+};
 use tracing::{debug, instrument};
 
 use crate::errors;
-use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason};
+use crate::hir_ty_lowering::{
+    FeedConstTy, HirTyLowerer, InherentAssocCandidate, RegionInferReason,
+};
 
 pub(crate) mod dump;
 mod generics_of;
@@ -364,6 +371,58 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
         self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_ident))
     }
 
+    #[instrument(level = "debug", skip(self, _span), ret)]
+    fn select_inherent_assoc_candidates(
+        &self,
+        _span: Span,
+        self_ty: Ty<'tcx>,
+        candidates: Vec<InherentAssocCandidate>,
+    ) -> (Vec<InherentAssocCandidate>, Vec<FulfillmentError<'tcx>>) {
+        assert!(!self_ty.has_infer());
+
+        // We don't just call the normal normalization routine here as we can't provide the
+        // correct `ParamEnv` and it would be wrong to invoke arbitrary trait solving under
+        // the wrong `ParamEnv`. Expanding free aliases doesn't need a `ParamEnv` so we do
+        // this just to make resolution a little bit smarter.
+        let self_ty = self.tcx.expand_free_alias_tys(self_ty);
+        debug!("select_inherent_assoc_candidates: self_ty={:?}", self_ty);
+
+        let candidates = candidates
+            .into_iter()
+            .filter(|&InherentAssocCandidate { impl_, .. }| {
+                let impl_ty = self.tcx().type_of(impl_).instantiate_identity();
+
+                // See comment on doing this operation for `self_ty`
+                let impl_ty = self.tcx.expand_free_alias_tys(impl_ty);
+                debug!("select_inherent_assoc_candidates: impl_ty={:?}", impl_ty);
+
+                // We treat parameters in the self ty as rigid and parameters in the impl ty as infers
+                // because it allows `impl<T> Foo<T>` to unify with `Foo<u8>::IAT`, while also disallowing
+                // `Foo<T>::IAT` from unifying with `impl Foo<u8>`.
+                //
+                // We don't really care about a depth limit here because we're only working with user-written
+                // types and if they wrote a type that would take hours to walk then that's kind of on them. On
+                // the other hand the default depth limit is relatively low and could realistically be hit by
+                // users in normal cases.
+                //
+                // `DeepRejectCtxt` leads to slightly worse IAT resolution than real type equality in cases
+                // where the `impl_ty` has repeated uses of generic parameters. E.g. `impl<T> Foo<T, T>` would
+                // be considered a valid candidate when resolving `Foo<u8, u16>::IAT`.
+                //
+                // Not replacing escaping bound vars in `self_ty` with placeholders also leads to slightly worse
+                // resolution, but it probably won't come up in practice and it would be backwards compatible
+                // to switch over to doing that.
+                ty::DeepRejectCtxt::relate_rigid_infer(self.tcx).types_may_unify_with_depth(
+                    self_ty,
+                    impl_ty,
+                    usize::MAX,
+                )
+            })
+            .collect();
+
+        (candidates, vec![])
+    }
+
     fn lower_assoc_item_path(
         &self,
         span: Span,
@@ -1093,22 +1152,11 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
     let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive);
     let is_fundamental = tcx.has_attr(def_id, sym::fundamental);
 
-    // FIXME: We could probably do way better attribute validation here.
-    let mut skip_array_during_method_dispatch = false;
-    let mut skip_boxed_slice_during_method_dispatch = false;
-    for attr in tcx.get_attrs(def_id, sym::rustc_skip_during_method_dispatch) {
-        if let Some(lst) = attr.meta_item_list() {
-            for item in lst {
-                if let Some(ident) = item.ident() {
-                    match ident.as_str() {
-                        "array" => skip_array_during_method_dispatch = true,
-                        "boxed_slice" => skip_boxed_slice_during_method_dispatch = true,
-                        _ => (),
-                    }
-                }
-            }
-        }
-    }
+    let [skip_array_during_method_dispatch, skip_boxed_slice_during_method_dispatch] = find_attr!(
+        tcx.get_all_attrs(def_id),
+        AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span:_ } => [*array, *boxed_slice]
+    )
+    .unwrap_or([false; 2]);
 
     let specialization_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
         ty::trait_def::TraitSpecializationKind::Marker
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index d45f0475e99..95743f9a63e 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -2177,84 +2177,80 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
     /// Walk the generics of the item for a trait bound whose self type
     /// corresponds to the expected res, and return the trait def id.
     fn for_each_trait_bound_on_res(&self, expected_res: Res) -> impl Iterator<Item = DefId> {
-        std::iter::from_coroutine(
-            #[coroutine]
-            move || {
-                let mut scope = self.scope;
-                loop {
-                    let hir_id = match *scope {
-                        Scope::Binder { hir_id, .. } => Some(hir_id),
-                        Scope::Root { opt_parent_item: Some(parent_def_id) } => {
-                            Some(self.tcx.local_def_id_to_hir_id(parent_def_id))
-                        }
-                        Scope::Body { .. }
-                        | Scope::ObjectLifetimeDefault { .. }
-                        | Scope::Supertrait { .. }
-                        | Scope::TraitRefBoundary { .. }
-                        | Scope::LateBoundary { .. }
-                        | Scope::Opaque { .. }
-                        | Scope::Root { opt_parent_item: None } => None,
-                    };
+        gen move {
+            let mut scope = self.scope;
+            loop {
+                let hir_id = match *scope {
+                    Scope::Binder { hir_id, .. } => Some(hir_id),
+                    Scope::Root { opt_parent_item: Some(parent_def_id) } => {
+                        Some(self.tcx.local_def_id_to_hir_id(parent_def_id))
+                    }
+                    Scope::Body { .. }
+                    | Scope::ObjectLifetimeDefault { .. }
+                    | Scope::Supertrait { .. }
+                    | Scope::TraitRefBoundary { .. }
+                    | Scope::LateBoundary { .. }
+                    | Scope::Opaque { .. }
+                    | Scope::Root { opt_parent_item: None } => None,
+                };
 
-                    if let Some(hir_id) = hir_id {
-                        let node = self.tcx.hir_node(hir_id);
-                        // If this is a `Self` bound in a trait, yield the trait itself.
-                        // Specifically, we don't need to look at any supertraits since
-                        // we already do that in `BoundVarContext::supertrait_hrtb_vars`.
-                        if let Res::SelfTyParam { trait_: _ } = expected_res
-                            && let hir::Node::Item(item) = node
-                            && let hir::ItemKind::Trait(..) = item.kind
-                        {
-                            // Yield the trait's def id. Supertraits will be
-                            // elaborated from that.
-                            yield item.owner_id.def_id.to_def_id();
-                        } else if let Some(generics) = node.generics() {
-                            for pred in generics.predicates {
-                                let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind
-                                else {
-                                    continue;
-                                };
-                                let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
-                                    pred.bounded_ty.kind
-                                else {
-                                    continue;
-                                };
-                                // Match the expected res.
-                                if bounded_path.res != expected_res {
-                                    continue;
-                                }
-                                for pred in pred.bounds {
-                                    match pred {
-                                        hir::GenericBound::Trait(poly_trait_ref) => {
-                                            if let Some(def_id) =
-                                                poly_trait_ref.trait_ref.trait_def_id()
-                                            {
-                                                yield def_id;
-                                            }
+                if let Some(hir_id) = hir_id {
+                    let node = self.tcx.hir_node(hir_id);
+                    // If this is a `Self` bound in a trait, yield the trait itself.
+                    // Specifically, we don't need to look at any supertraits since
+                    // we already do that in `BoundVarContext::supertrait_hrtb_vars`.
+                    if let Res::SelfTyParam { trait_: _ } = expected_res
+                        && let hir::Node::Item(item) = node
+                        && let hir::ItemKind::Trait(..) = item.kind
+                    {
+                        // Yield the trait's def id. Supertraits will be
+                        // elaborated from that.
+                        yield item.owner_id.def_id.to_def_id();
+                    } else if let Some(generics) = node.generics() {
+                        for pred in generics.predicates {
+                            let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind else {
+                                continue;
+                            };
+                            let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
+                                pred.bounded_ty.kind
+                            else {
+                                continue;
+                            };
+                            // Match the expected res.
+                            if bounded_path.res != expected_res {
+                                continue;
+                            }
+                            for pred in pred.bounds {
+                                match pred {
+                                    hir::GenericBound::Trait(poly_trait_ref) => {
+                                        if let Some(def_id) =
+                                            poly_trait_ref.trait_ref.trait_def_id()
+                                        {
+                                            yield def_id;
                                         }
-                                        hir::GenericBound::Outlives(_)
-                                        | hir::GenericBound::Use(_, _) => {}
                                     }
+                                    hir::GenericBound::Outlives(_)
+                                    | hir::GenericBound::Use(_, _) => {}
                                 }
                             }
                         }
                     }
+                }
 
-                    match *scope {
-                        Scope::Binder { s, .. }
-                        | Scope::Body { s, .. }
-                        | Scope::ObjectLifetimeDefault { s, .. }
-                        | Scope::Supertrait { s, .. }
-                        | Scope::TraitRefBoundary { s }
-                        | Scope::LateBoundary { s, .. }
-                        | Scope::Opaque { s, .. } => {
-                            scope = s;
-                        }
-                        Scope::Root { .. } => break,
+                match *scope {
+                    Scope::Binder { s, .. }
+                    | Scope::Body { s, .. }
+                    | Scope::ObjectLifetimeDefault { s, .. }
+                    | Scope::Supertrait { s, .. }
+                    | Scope::TraitRefBoundary { s }
+                    | Scope::LateBoundary { s, .. }
+                    | Scope::Opaque { s, .. } => {
+                        scope = s;
                     }
+                    Scope::Root { .. } => break,
                 }
-            },
-        )
+            }
+        }
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 8de2aec95a7..c920e25ad37 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -127,6 +127,7 @@ pub(crate) enum AssocItemNotFoundSugg<'a> {
     SimilarInOtherTrait {
         #[primary_span]
         span: Span,
+        trait_name: &'a str,
         assoc_kind: &'static str,
         suggested_name: Symbol,
     },
@@ -1712,3 +1713,11 @@ pub(crate) struct AbiCustomClothedFunction {
     )]
     pub naked_span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_async_drop_without_sync_drop)]
+#[help]
+pub(crate) struct AsyncDropWithoutSyncDrop {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
index ebeb3b58208..82e5f65476f 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
@@ -18,7 +18,7 @@ pub(crate) fn validate_cmse_abi<'tcx>(
     fn_sig: ty::PolyFnSig<'tcx>,
 ) {
     match abi {
-        ExternAbi::CCmseNonSecureCall => {
+        ExternAbi::CmseNonSecureCall => {
             let hir_node = tcx.hir_node(hir_id);
             let hir::Node::Ty(hir::Ty {
                 span: bare_fn_span,
@@ -38,7 +38,7 @@ pub(crate) fn validate_cmse_abi<'tcx>(
                     dcx,
                     span,
                     E0781,
-                    "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
+                    "the `\"cmse-nonsecure-call\"` ABI is only allowed on function pointers"
                 )
                 .emit();
                 return;
@@ -78,7 +78,7 @@ pub(crate) fn validate_cmse_abi<'tcx>(
                 }
             };
         }
-        ExternAbi::CCmseNonSecureEntry => {
+        ExternAbi::CmseNonSecureEntry => {
             let hir_node = tcx.hir_node(hir_id);
             let Some(hir::FnSig { decl, span: fn_sig_span, .. }) = hir_node.fn_sig() else {
                 // might happen when this ABI is used incorrectly. That will be handled elsewhere
@@ -203,11 +203,11 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError
     match layout_err {
         TooGeneric(ty) => {
             match abi {
-                ExternAbi::CCmseNonSecureCall => {
+                ExternAbi::CmseNonSecureCall => {
                     // prevent double reporting of this error
                     !ty.is_impl_trait()
                 }
-                ExternAbi::CCmseNonSecureEntry => true,
+                ExternAbi::CmseNonSecureEntry => true,
                 _ => bug!("invalid ABI: {abi}"),
             }
         }
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 1cda6dff21e..f211137ddd6 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -26,6 +26,7 @@ use rustc_trait_selection::traits::{
 use smallvec::SmallVec;
 use tracing::debug;
 
+use super::InherentAssocCandidate;
 use crate::errors::{
     self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams,
     ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
@@ -308,6 +309,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         // change the associated item.
                         err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
                             span: assoc_ident.span,
+                            trait_name: &trait_name,
                             assoc_kind: assoc_kind_str,
                             suggested_name,
                         });
@@ -793,7 +795,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         name: Ident,
         self_ty: Ty<'tcx>,
-        candidates: Vec<(DefId, (DefId, DefId))>,
+        candidates: Vec<InherentAssocCandidate>,
         fulfillment_errors: Vec<FulfillmentError<'tcx>>,
         span: Span,
         assoc_tag: ty::AssocTag,
@@ -827,8 +829,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             let type_candidates = candidates
                 .iter()
                 .take(limit)
-                .map(|&(impl_, _)| {
-                    format!("- `{}`", tcx.at(span).type_of(impl_).instantiate_identity())
+                .map(|cand| {
+                    format!("- `{}`", tcx.at(span).type_of(cand.impl_).instantiate_identity())
                 })
                 .collect::<Vec<_>>()
                 .join("\n");
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index bf407cbaccb..51cd1b7704d 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -33,13 +33,14 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId};
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
-use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause};
+use rustc_infer::traits::DynCompatibilityViolation;
+use rustc_macros::{TypeFoldable, TypeVisitable};
 use rustc_middle::middle::stability::AllowUnstable;
 use rustc_middle::mir::interpret::LitToConstInput;
 use rustc_middle::ty::print::PrintPolyTraitRefExt as _;
 use rustc_middle::ty::{
-    self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt,
-    TypeVisitableExt, TypingMode, Upcast, fold_regions,
+    self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt,
+    TypingMode, Upcast, fold_regions,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
@@ -47,10 +48,10 @@ use rustc_session::parse::feature_err;
 use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::wf::object_region_bounds;
-use rustc_trait_selection::traits::{self, ObligationCtxt};
+use rustc_trait_selection::traits::{self, FulfillmentError};
 use tracing::{debug, instrument};
 
-use crate::check::check_abi_fn_ptr;
+use crate::check::check_abi;
 use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation};
 use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
 use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
@@ -99,6 +100,13 @@ pub enum RegionInferReason<'a> {
     OutlivesBound,
 }
 
+#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Debug)]
+pub struct InherentAssocCandidate {
+    pub impl_: DefId,
+    pub assoc_item: DefId,
+    pub scope: DefId,
+}
+
 /// A context which can lower type-system entities from the [HIR][hir] to
 /// the [`rustc_middle::ty`] representation.
 ///
@@ -148,6 +156,13 @@ pub trait HirTyLowerer<'tcx> {
         assoc_ident: Ident,
     ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>;
 
+    fn select_inherent_assoc_candidates(
+        &self,
+        span: Span,
+        self_ty: Ty<'tcx>,
+        candidates: Vec<InherentAssocCandidate>,
+    ) -> (Vec<InherentAssocCandidate>, Vec<FulfillmentError<'tcx>>);
+
     /// Lower a path to an associated item (of a trait) to a projection.
     ///
     /// This method has to be defined by the concrete lowering context because
@@ -1449,48 +1464,32 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             .filter_map(|&impl_| {
                 let (item, scope) =
                     self.probe_assoc_item_unchecked(name, assoc_tag, block, impl_)?;
-                Some((impl_, (item.def_id, scope)))
+                Some(InherentAssocCandidate { impl_, assoc_item: item.def_id, scope })
             })
             .collect();
 
-        if candidates.is_empty() {
-            return Ok(None);
-        }
-
-        //
-        // Select applicable inherent associated type candidates modulo regions.
-        //
-
-        // In contexts that have no inference context, just make a new one.
-        // We do need a local variable to store it, though.
-        let infcx = match self.infcx() {
-            Some(infcx) => infcx,
-            None => {
-                assert!(!self_ty.has_infer());
-                &tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis())
-            }
-        };
+        let (applicable_candidates, fulfillment_errors) =
+            self.select_inherent_assoc_candidates(span, self_ty, candidates.clone());
 
-        // FIXME(inherent_associated_types): Acquiring the ParamEnv this early leads to cycle errors
-        // when inside of an ADT (#108491) or where clause.
-        let param_env = tcx.param_env(block.owner);
+        let InherentAssocCandidate { impl_, assoc_item, scope: def_scope } =
+            match &applicable_candidates[..] {
+                &[] => Err(self.report_unresolved_inherent_assoc_item(
+                    name,
+                    self_ty,
+                    candidates,
+                    fulfillment_errors,
+                    span,
+                    assoc_tag,
+                )),
 
-        let mut universes = if self_ty.has_escaping_bound_vars() {
-            vec![None; self_ty.outer_exclusive_binder().as_usize()]
-        } else {
-            vec![]
-        };
+                &[applicable_candidate] => Ok(applicable_candidate),
 
-        let (impl_, (assoc_item, def_scope)) = crate::traits::with_replaced_escaping_bound_vars(
-            infcx,
-            &mut universes,
-            self_ty,
-            |self_ty| {
-                self.select_inherent_assoc_candidates(
-                    infcx, name, span, self_ty, param_env, candidates, assoc_tag,
-                )
-            },
-        )?;
+                &[_, ..] => Err(self.report_ambiguous_inherent_assoc_item(
+                    name,
+                    candidates.into_iter().map(|cand| cand.assoc_item).collect(),
+                    span,
+                )),
+            }?;
 
         self.check_assoc_item(assoc_item, name, def_scope, block, span);
 
@@ -1507,78 +1506,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         Ok(Some((assoc_item, args)))
     }
 
-    fn select_inherent_assoc_candidates(
-        &self,
-        infcx: &InferCtxt<'tcx>,
-        name: Ident,
-        span: Span,
-        self_ty: Ty<'tcx>,
-        param_env: ParamEnv<'tcx>,
-        candidates: Vec<(DefId, (DefId, DefId))>,
-        assoc_tag: ty::AssocTag,
-    ) -> Result<(DefId, (DefId, DefId)), ErrorGuaranteed> {
-        let tcx = self.tcx();
-        let mut fulfillment_errors = Vec::new();
-
-        let applicable_candidates: Vec<_> = candidates
-            .iter()
-            .copied()
-            .filter(|&(impl_, _)| {
-                infcx.probe(|_| {
-                    let ocx = ObligationCtxt::new_with_diagnostics(infcx);
-                    let self_ty = ocx.normalize(&ObligationCause::dummy(), param_env, self_ty);
-
-                    let impl_args = infcx.fresh_args_for_item(span, impl_);
-                    let impl_ty = tcx.type_of(impl_).instantiate(tcx, impl_args);
-                    let impl_ty = ocx.normalize(&ObligationCause::dummy(), param_env, impl_ty);
-
-                    // Check that the self types can be related.
-                    if ocx.eq(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err() {
-                        return false;
-                    }
-
-                    // Check whether the impl imposes obligations we have to worry about.
-                    let impl_bounds = tcx.predicates_of(impl_).instantiate(tcx, impl_args);
-                    let impl_bounds =
-                        ocx.normalize(&ObligationCause::dummy(), param_env, impl_bounds);
-                    let impl_obligations = traits::predicates_for_generics(
-                        |_, _| ObligationCause::dummy(),
-                        param_env,
-                        impl_bounds,
-                    );
-                    ocx.register_obligations(impl_obligations);
-
-                    let mut errors = ocx.select_where_possible();
-                    if !errors.is_empty() {
-                        fulfillment_errors.append(&mut errors);
-                        return false;
-                    }
-
-                    true
-                })
-            })
-            .collect();
-
-        match &applicable_candidates[..] {
-            &[] => Err(self.report_unresolved_inherent_assoc_item(
-                name,
-                self_ty,
-                candidates,
-                fulfillment_errors,
-                span,
-                assoc_tag,
-            )),
-
-            &[applicable_candidate] => Ok(applicable_candidate),
-
-            &[_, ..] => Err(self.report_ambiguous_inherent_assoc_item(
-                name,
-                applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(),
-                span,
-            )),
-        }
-    }
-
     /// Given name and kind search for the assoc item in the provided scope and check if it's accessible[^1].
     ///
     /// [^1]: I.e., accessible in the provided scope wrt. visibility and stability.
@@ -1661,7 +1588,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             &infcx_
         };
 
-        tcx.all_traits()
+        tcx.all_traits_including_private()
             .filter(|trait_def_id| {
                 // Consider only traits with the associated type
                 tcx.associated_items(*trait_def_id)
@@ -2733,7 +2660,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         if let hir::Node::Ty(hir::Ty { kind: hir::TyKind::BareFn(bare_fn_ty), span, .. }) =
             tcx.hir_node(hir_id)
         {
-            check_abi_fn_ptr(tcx, hir_id, *span, bare_fn_ty.abi);
+            check_abi(tcx, hir_id, *span, bare_fn_ty.abi);
         }
 
         // reject function types that violate cmse ABI requirements
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index 4633f3951a7..fef0dbf2ece 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -1,7 +1,8 @@
+use rustc_hir::def::DefKind;
 use rustc_hir::intravisit::{self, Visitor, VisitorExt};
 use rustc_hir::{self as hir, AmbigArg, ForeignItem, ForeignItemKind};
 use rustc_infer::infer::TyCtxtInferExt;
-use rustc_infer::traits::{ObligationCause, WellFormedLoc};
+use rustc_infer::traits::{ObligationCause, ObligationCauseCode, WellFormedLoc};
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode, fold_regions};
@@ -107,6 +108,17 @@ fn diagnostic_hir_wf_check<'tcx>(
                     // over less-specific types (e.g. `Option<MyStruct<u8>>`)
                     if self.depth >= self.cause_depth {
                         self.cause = Some(error.obligation.cause);
+                        if let hir::TyKind::TraitObject(..) = ty.kind {
+                            if let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn =
+                                self.tcx.def_kind(self.def_id)
+                            {
+                                self.cause = Some(ObligationCause::new(
+                                    ty.span,
+                                    self.def_id,
+                                    ObligationCauseCode::DynCompatible(ty.span),
+                                ));
+                            }
+                        }
                         self.cause_depth = self.depth
                     }
                 }
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 7c8c9425a03..76ab2e57a1b 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -62,8 +62,8 @@ This API is completely unstable and subject to change.
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
 #![feature(assert_matches)]
-#![feature(coroutines)]
 #![feature(debug_closure_helpers)]
+#![feature(gen_blocks)]
 #![feature(if_let_guard)]
 #![feature(iter_from_coroutine)]
 #![feature(iter_intersperse)]
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index fc507285860..ff87d3dec6e 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -10,7 +10,7 @@ use std::vec;
 
 use rustc_abi::ExternAbi;
 use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
-use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs};
+use rustc_ast::{DUMMY_NODE_ID, DelimArgs};
 use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
 use rustc_ast_pretty::pp::{self, BoxMarker, Breaks};
 use rustc_ast_pretty::pprust::state::MacHeader;
@@ -22,7 +22,7 @@ use rustc_hir::{
     TyPatKind,
 };
 use rustc_span::source_map::SourceMap;
-use rustc_span::{FileName, Ident, Span, Symbol, kw};
+use rustc_span::{FileName, Ident, Span, Symbol, kw, sym};
 use {rustc_ast as ast, rustc_hir as hir};
 
 pub fn id_to_string(cx: &dyn rustc_hir::intravisit::HirTyCtxt<'_>, hir_id: HirId) -> String {
@@ -81,32 +81,24 @@ impl<'a> State<'a> {
     }
 
     fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
-        let for_each_attr = |id: HirId, callback: &mut dyn FnMut(&hir::Attribute)| {
-            self.attrs(id).iter().for_each(callback);
-        };
-        expr.precedence(&for_each_attr)
-    }
-
-    fn print_attrs_as_inner(&mut self, attrs: &[hir::Attribute]) {
-        self.print_either_attributes(attrs, ast::AttrStyle::Inner)
-    }
-
-    fn print_attrs_as_outer(&mut self, attrs: &[hir::Attribute]) {
-        self.print_either_attributes(attrs, ast::AttrStyle::Outer)
+        let has_attr = |id: HirId| !self.attrs(id).is_empty();
+        expr.precedence(&has_attr)
     }
 
-    fn print_either_attributes(&mut self, attrs: &[hir::Attribute], style: ast::AttrStyle) {
+    fn print_attrs(&mut self, attrs: &[hir::Attribute]) {
         if attrs.is_empty() {
             return;
         }
 
         for attr in attrs {
-            self.print_attribute_inline(attr, style);
+            self.print_attribute_as_style(attr, ast::AttrStyle::Outer);
         }
         self.hardbreak_if_not_bol();
     }
 
-    fn print_attribute_inline(&mut self, attr: &hir::Attribute, style: AttrStyle) {
+    /// Print a single attribute as if it has style `style`, disregarding the
+    /// actual style of the attribute.
+    fn print_attribute_as_style(&mut self, attr: &hir::Attribute, style: ast::AttrStyle) {
         match &attr {
             hir::Attribute::Unparsed(unparsed) => {
                 self.maybe_print_comment(unparsed.span.lo());
@@ -118,14 +110,17 @@ impl<'a> State<'a> {
                 self.word("]");
                 self.hardbreak()
             }
-            hir::Attribute::Parsed(AttributeKind::DocComment { style, kind, comment, .. }) => {
+            hir::Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => {
                 self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string(
-                    *kind, *style, *comment,
+                    *kind, style, *comment,
                 ));
                 self.hardbreak()
             }
             hir::Attribute::Parsed(pa) => {
-                self.word("#[attr = ");
+                match style {
+                    ast::AttrStyle::Inner => self.word("#![attr = "),
+                    ast::AttrStyle::Outer => self.word("#[attr = "),
+                }
                 pa.print_attribute(self);
                 self.word("]");
                 self.hardbreak()
@@ -281,10 +276,17 @@ pub fn print_crate<'a>(
         ann,
     };
 
+    // Print all attributes, regardless of actual style, as inner attributes
+    // since this is the crate root with nothing above it to print outer
+    // attributes.
+    for attr in s.attrs(hir::CRATE_HIR_ID) {
+        s.print_attribute_as_style(attr, ast::AttrStyle::Inner);
+    }
+
     // When printing the AST, we sometimes need to inject `#[no_std]` here.
     // Since you can't compile the HIR, it's not necessary.
 
-    s.print_mod(krate, (*attrs)(hir::CRATE_HIR_ID));
+    s.print_mod(krate);
     s.print_remaining_comments();
     s.s.eof()
 }
@@ -299,7 +301,7 @@ where
 }
 
 pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String {
-    to_string(ann, |s| s.print_attribute_inline(attr, AttrStyle::Outer))
+    to_string(ann, |s| s.print_attribute_as_style(attr, ast::AttrStyle::Outer))
 }
 
 pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String {
@@ -361,8 +363,7 @@ impl<'a> State<'a> {
         self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span);
     }
 
-    fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[hir::Attribute]) {
-        self.print_attrs_as_inner(attrs);
+    fn print_mod(&mut self, _mod: &hir::Mod<'_>) {
         for &item_id in _mod.item_ids {
             self.ann.nested(self, Nested::Item(item_id));
         }
@@ -479,7 +480,7 @@ impl<'a> State<'a> {
     fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(item.span.lo());
-        self.print_attrs_as_outer(self.attrs(item.hir_id()));
+        self.print_attrs(self.attrs(item.hir_id()));
         match item.kind {
             hir::ForeignItemKind::Fn(sig, arg_idents, generics) => {
                 let (cb, ib) = self.head("");
@@ -565,7 +566,7 @@ impl<'a> State<'a> {
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(item.span.lo());
         let attrs = self.attrs(item.hir_id());
-        self.print_attrs_as_outer(attrs);
+        self.print_attrs(attrs);
         self.ann.pre(self, AnnNode::Item(item));
         match item.kind {
             hir::ItemKind::ExternCrate(orig_name, ident) => {
@@ -647,14 +648,13 @@ impl<'a> State<'a> {
                 self.print_ident(ident);
                 self.nbsp();
                 self.bopen(ib);
-                self.print_mod(mod_, attrs);
+                self.print_mod(mod_);
                 self.bclose(item.span, cb);
             }
             hir::ItemKind::ForeignMod { abi, items } => {
                 let (cb, ib) = self.head("extern");
                 self.word_nbsp(abi.to_string());
                 self.bopen(ib);
-                self.print_attrs_as_inner(self.attrs(item.hir_id()));
                 for item in items {
                     self.ann.nested(self, Nested::ForeignItem(item.id));
                 }
@@ -731,7 +731,6 @@ impl<'a> State<'a> {
 
                 self.space();
                 self.bopen(ib);
-                self.print_attrs_as_inner(attrs);
                 for impl_item in items {
                     self.ann.nested(self, Nested::ImplItem(impl_item.id));
                 }
@@ -822,7 +821,7 @@ impl<'a> State<'a> {
         for v in variants {
             self.space_if_not_bol();
             self.maybe_print_comment(v.span.lo());
-            self.print_attrs_as_outer(self.attrs(v.hir_id));
+            self.print_attrs(self.attrs(v.hir_id));
             let ib = self.ibox(INDENT_UNIT);
             self.print_variant(v);
             self.word(",");
@@ -857,7 +856,7 @@ impl<'a> State<'a> {
                     self.popen();
                     self.commasep(Inconsistent, struct_def.fields(), |s, field| {
                         s.maybe_print_comment(field.span.lo());
-                        s.print_attrs_as_outer(s.attrs(field.hir_id));
+                        s.print_attrs(s.attrs(field.hir_id));
                         s.print_type(field.ty);
                     });
                     self.pclose();
@@ -878,7 +877,7 @@ impl<'a> State<'a> {
                 for field in struct_def.fields() {
                     self.hardbreak_if_not_bol();
                     self.maybe_print_comment(field.span.lo());
-                    self.print_attrs_as_outer(self.attrs(field.hir_id));
+                    self.print_attrs(self.attrs(field.hir_id));
                     self.print_ident(field.ident);
                     self.word_nbsp(":");
                     self.print_type(field.ty);
@@ -916,7 +915,7 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::SubItem(ti.hir_id()));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(ti.span.lo());
-        self.print_attrs_as_outer(self.attrs(ti.hir_id()));
+        self.print_attrs(self.attrs(ti.hir_id()));
         match ti.kind {
             hir::TraitItemKind::Const(ty, default) => {
                 self.print_associated_const(ti.ident, ti.generics, ty, default);
@@ -944,7 +943,7 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::SubItem(ii.hir_id()));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(ii.span.lo());
-        self.print_attrs_as_outer(self.attrs(ii.hir_id()));
+        self.print_attrs(self.attrs(ii.hir_id()));
 
         match ii.kind {
             hir::ImplItemKind::Const(ty, expr) => {
@@ -1028,27 +1027,16 @@ impl<'a> State<'a> {
     }
 
     fn print_block(&mut self, blk: &hir::Block<'_>, cb: BoxMarker, ib: BoxMarker) {
-        self.print_block_with_attrs(blk, &[], cb, ib)
+        self.print_block_maybe_unclosed(blk, Some(cb), ib)
     }
 
     fn print_block_unclosed(&mut self, blk: &hir::Block<'_>, ib: BoxMarker) {
-        self.print_block_maybe_unclosed(blk, &[], None, ib)
-    }
-
-    fn print_block_with_attrs(
-        &mut self,
-        blk: &hir::Block<'_>,
-        attrs: &[hir::Attribute],
-        cb: BoxMarker,
-        ib: BoxMarker,
-    ) {
-        self.print_block_maybe_unclosed(blk, attrs, Some(cb), ib)
+        self.print_block_maybe_unclosed(blk, None, ib)
     }
 
     fn print_block_maybe_unclosed(
         &mut self,
         blk: &hir::Block<'_>,
-        attrs: &[hir::Attribute],
         cb: Option<BoxMarker>,
         ib: BoxMarker,
     ) {
@@ -1060,8 +1048,6 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::Block(blk));
         self.bopen(ib);
 
-        self.print_attrs_as_inner(attrs);
-
         for st in blk.stmts {
             self.print_stmt(st);
         }
@@ -1251,7 +1237,7 @@ impl<'a> State<'a> {
 
     fn print_expr_field(&mut self, field: &hir::ExprField<'_>) {
         let cb = self.cbox(INDENT_UNIT);
-        self.print_attrs_as_outer(self.attrs(field.hir_id));
+        self.print_attrs(self.attrs(field.hir_id));
         if !field.is_shorthand {
             self.print_ident(field.ident);
             self.word_space(":");
@@ -1349,6 +1335,10 @@ impl<'a> State<'a> {
                 self.word_nbsp("raw");
                 self.print_mutability(mutability, true);
             }
+            hir::BorrowKind::Pin => {
+                self.word_nbsp("pin");
+                self.print_mutability(mutability, true);
+            }
         }
         self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Prefix);
     }
@@ -1451,7 +1441,7 @@ impl<'a> State<'a> {
 
     fn print_expr(&mut self, expr: &hir::Expr<'_>) {
         self.maybe_print_comment(expr.span.lo());
-        self.print_attrs_as_outer(self.attrs(expr.hir_id));
+        self.print_attrs(self.attrs(expr.hir_id));
         let ib = self.ibox(INDENT_UNIT);
         self.ann.pre(self, AnnNode::Expr(expr));
         match expr.kind {
@@ -1517,7 +1507,7 @@ impl<'a> State<'a> {
                 self.bopen(ib);
 
                 // Print `let _t = $init;`:
-                let temp = Ident::from_str("_t");
+                let temp = Ident::with_dummy_span(sym::_t);
                 self.print_local(false, Some(init), None, |this| this.print_ident(temp));
                 self.word(";");
 
@@ -2076,7 +2066,7 @@ impl<'a> State<'a> {
             self.space();
         }
         let cb = self.cbox(INDENT_UNIT);
-        self.print_attrs_as_outer(self.attrs(field.hir_id));
+        self.print_attrs(self.attrs(field.hir_id));
         if !field.is_shorthand {
             self.print_ident(field.ident);
             self.word_nbsp(":");
@@ -2086,7 +2076,7 @@ impl<'a> State<'a> {
     }
 
     fn print_param(&mut self, arg: &hir::Param<'_>) {
-        self.print_attrs_as_outer(self.attrs(arg.hir_id));
+        self.print_attrs(self.attrs(arg.hir_id));
         self.print_pat(arg.pat);
     }
 
@@ -2121,7 +2111,7 @@ impl<'a> State<'a> {
         let cb = self.cbox(INDENT_UNIT);
         self.ann.pre(self, AnnNode::Arm(arm));
         let ib = self.ibox(0);
-        self.print_attrs_as_outer(self.attrs(arm.hir_id));
+        self.print_attrs(self.attrs(arm.hir_id));
         self.print_pat(arm.pat);
         self.space();
         if let Some(ref g) = arm.guard {
@@ -2409,7 +2399,7 @@ impl<'a> State<'a> {
     }
 
     fn print_where_predicate(&mut self, predicate: &hir::WherePredicate<'_>) {
-        self.print_attrs_as_outer(self.attrs(predicate.hir_id));
+        self.print_attrs(self.attrs(predicate.hir_id));
         match *predicate.kind {
             hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
                 bound_generic_params,
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index ac7ff65528d..c21b16c9f9f 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -1,6 +1,6 @@
-hir_typeck_abi_custom_call =
-    functions with the `"custom"` ABI cannot be called
-    .note = an `extern "custom"` function can only be called from within inline assembly
+hir_typeck_abi_cannot_be_called =
+    functions with the {$abi} ABI cannot be called
+    .note = an `extern {$abi}` function can only be called using inline assembly
 
 hir_typeck_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
 
@@ -79,6 +79,9 @@ hir_typeck_cast_unknown_pointer = cannot cast {$to ->
     .note = the type information given here is insufficient to check whether the pointer cast is valid
     .label_from = the type information given here is insufficient to check whether the pointer cast is valid
 
+hir_typeck_const_continue_bad_label =
+    `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+
 hir_typeck_const_select_must_be_const = this argument must be a `const fn`
     .help = consult the documentation on `const_eval_select` for more information
 
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 80bff09d0a4..f790c51f8f1 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -1,6 +1,6 @@
 use std::iter;
 
-use rustc_abi::ExternAbi;
+use rustc_abi::{CanonAbi, ExternAbi};
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
 use rustc_hir::def::{self, CtorKind, Namespace, Res};
@@ -16,6 +16,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{Span, sym};
+use rustc_target::spec::{AbiMap, AbiMapping};
 use rustc_trait_selection::error_reporting::traits::DefIdOrName;
 use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -84,7 +85,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         while result.is_none() && autoderef.next().is_some() {
             result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef);
         }
-        self.check_call_custom_abi(autoderef.final_ty(false), call_expr.span);
+
+        match autoderef.final_ty(false).kind() {
+            ty::FnDef(def_id, _) => {
+                let abi = self.tcx.fn_sig(def_id).skip_binder().skip_binder().abi;
+                self.check_call_abi(abi, call_expr.span);
+            }
+            ty::FnPtr(_, header) => {
+                self.check_call_abi(header.abi, call_expr.span);
+            }
+            _ => { /* cannot have a non-rust abi */ }
+        }
+
         self.register_predicates(autoderef.into_obligations());
 
         let output = match result {
@@ -137,19 +149,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         output
     }
 
-    /// Functions of type `extern "custom" fn(/* ... */)` cannot be called using `ExprKind::Call`.
+    /// Can a function with this ABI be called with a rust call expression?
     ///
-    /// These functions have a calling convention that is unknown to rust, hence it cannot generate
-    /// code for the call. The only way to execute such a function is via inline assembly.
-    fn check_call_custom_abi(&self, callee_ty: Ty<'tcx>, span: Span) {
-        let abi = match callee_ty.kind() {
-            ty::FnDef(def_id, _) => self.tcx.fn_sig(def_id).skip_binder().skip_binder().abi,
-            ty::FnPtr(_, header) => header.abi,
-            _ => return,
+    /// Some ABIs cannot be called from rust, either because rust does not know how to generate
+    /// code for the call, or because a call does not semantically make sense.
+    pub(crate) fn check_call_abi(&self, abi: ExternAbi, span: Span) {
+        let canon_abi = match AbiMap::from_target(&self.sess().target).canonize_abi(abi, false) {
+            AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => canon_abi,
+            AbiMapping::Invalid => {
+                // This should be reported elsewhere, but we want to taint this body
+                // so that we don't try to evaluate calls to ABIs that are invalid.
+                let guar = self.dcx().span_delayed_bug(
+                    span,
+                    format!("invalid abi for platform should have reported an error: {abi}"),
+                );
+                self.set_tainted_by_errors(guar);
+                return;
+            }
+        };
+
+        let valid = match canon_abi {
+            // Rust doesn't know how to call functions with this ABI.
+            CanonAbi::Custom => false,
+
+            // These is an entry point for the host, and cannot be called on the GPU.
+            CanonAbi::GpuKernel => false,
+
+            // The interrupt ABIs should only be called by the CPU. They have complex
+            // pre- and postconditions, and can use non-standard instructions like `iret` on x86.
+            CanonAbi::Interrupt(_) => false,
+
+            CanonAbi::C
+            | CanonAbi::Rust
+            | CanonAbi::RustCold
+            | CanonAbi::Arm(_)
+            | CanonAbi::X86(_) => true,
         };
 
-        if let ExternAbi::Custom = abi {
-            self.tcx.dcx().emit_err(errors::AbiCustomCall { span });
+        if !valid {
+            let err = crate::errors::AbiCannotBeCalled { span, abi };
+            self.tcx.dcx().emit_err(err);
         }
     }
 
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 24092c01125..0ce0bc313c7 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -46,7 +46,7 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
 use rustc_infer::infer::relate::RelateResult;
 use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
 use rustc_infer::traits::{
-    IfExpressionCause, MatchExpressionArmCause, Obligation, PredicateObligation,
+    IfExpressionCause, ImplSource, MatchExpressionArmCause, Obligation, PredicateObligation,
     PredicateObligations, SelectionError,
 };
 use rustc_middle::span_bug;
@@ -704,6 +704,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                     // be silent, as it causes a type mismatch later.
                 }
 
+                Ok(Some(ImplSource::UserDefined(impl_source))) => {
+                    queue.extend(impl_source.nested);
+                    // Certain incoherent `CoerceUnsized` implementations may cause ICEs,
+                    // so check the impl's validity. Taint the body so that we don't try
+                    // to evaluate these invalid coercions in CTFE. We only need to do this
+                    // for local impls, since upstream impls should be valid.
+                    if impl_source.impl_def_id.is_local()
+                        && let Err(guar) =
+                            self.tcx.ensure_ok().coerce_unsized_info(impl_source.impl_def_id)
+                    {
+                        self.fcx.set_tainted_by_errors(guar);
+                    }
+                }
                 Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
             }
         }
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 5b55fbe9150..e5684f8cbe6 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1110,27 +1110,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    // Returns whether the given expression is a destruct assignment desugaring.
-    // For example, `(a, b) = (1, &2);`
-    // Here we try to find the pattern binding of the expression,
-    // `default_binding_modes` is false only for destruct assignment desugaring.
+    /// Returns whether the given expression is a destruct assignment desugaring.
+    /// For example, `(a, b) = (1, &2);`
+    /// Here we try to find the pattern binding of the expression,
+    /// `default_binding_modes` is false only for destruct assignment desugaring.
     pub(crate) fn is_destruct_assignment_desugaring(&self, expr: &hir::Expr<'_>) -> bool {
         if let hir::ExprKind::Path(hir::QPath::Resolved(
             _,
             hir::Path { res: hir::def::Res::Local(bind_hir_id), .. },
         )) = expr.kind
-        {
-            let bind = self.tcx.hir_node(*bind_hir_id);
-            let parent = self.tcx.parent_hir_node(*bind_hir_id);
-            if let hir::Node::Pat(hir::Pat {
+            && let bind = self.tcx.hir_node(*bind_hir_id)
+            && let parent = self.tcx.parent_hir_node(*bind_hir_id)
+            && let hir::Node::Pat(hir::Pat {
                 kind: hir::PatKind::Binding(_, _hir_id, _, _), ..
             }) = bind
-                && let hir::Node::Pat(hir::Pat { default_binding_modes: false, .. }) = parent
-            {
-                return true;
-            }
+            && let hir::Node::Pat(hir::Pat { default_binding_modes: false, .. }) = parent
+        {
+            true
+        } else {
+            false
         }
-        false
     }
 
     fn explain_self_literal(
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index abb8cdc1cdf..3606c778fc4 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -2,6 +2,7 @@
 
 use std::borrow::Cow;
 
+use rustc_abi::ExternAbi;
 use rustc_ast::Label;
 use rustc_errors::codes::*;
 use rustc_errors::{
@@ -30,8 +31,6 @@ pub(crate) struct BaseExpressionDoubleDot {
     )]
     pub default_field_values_suggestion: Option<Span>,
     #[subdiagnostic]
-    pub default_field_values_help: Option<BaseExpressionDoubleDotEnableDefaultFieldValues>,
-    #[subdiagnostic]
     pub add_expr: Option<BaseExpressionDoubleDotAddExpr>,
     #[subdiagnostic]
     pub remove_dots: Option<BaseExpressionDoubleDotRemove>,
@@ -61,10 +60,6 @@ pub(crate) struct BaseExpressionDoubleDotAddExpr {
     pub span: Span,
 }
 
-#[derive(Subdiagnostic)]
-#[help(hir_typeck_base_expression_double_dot_enable_default_field_values)]
-pub(crate) struct BaseExpressionDoubleDotEnableDefaultFieldValues;
-
 #[derive(Diagnostic)]
 #[diag(hir_typeck_field_multiply_specified_in_initializer, code = E0062)]
 pub(crate) struct FieldMultiplySpecifiedInInitializer {
@@ -1165,8 +1160,17 @@ pub(crate) struct NakedFunctionsMustNakedAsm {
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_typeck_abi_custom_call)]
-pub(crate) struct AbiCustomCall {
+#[diag(hir_typeck_abi_cannot_be_called)]
+pub(crate) struct AbiCannotBeCalled {
+    #[primary_span]
+    #[note]
+    pub span: Span,
+    pub abi: ExternAbi,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_const_continue_bad_label)]
+pub(crate) struct ConstContinueBadLabel {
     #[primary_span]
     pub span: Span,
 }
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 55c39d960e7..2bc9dadb665 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -5,8 +5,9 @@
 //!
 //! See [`rustc_hir_analysis::check`] for more context on type checking in general.
 
-use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx};
+use rustc_abi::{FIRST_VARIANT, FieldIdx};
 use rustc_ast::util::parser::ExprPrecedence;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::unord::UnordMap;
@@ -18,7 +19,7 @@ use rustc_errors::{
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{Attribute, ExprKind, HirId, QPath};
+use rustc_hir::{ExprKind, HirId, QPath};
 use rustc_hir_analysis::NoVariantNamed;
 use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _};
 use rustc_infer::infer;
@@ -43,10 +44,9 @@ use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectatio
 use crate::coercion::{CoerceMany, DynamicCoerceMany};
 use crate::errors::{
     AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
-    BaseExpressionDoubleDotEnableDefaultFieldValues, BaseExpressionDoubleDotRemove,
-    CantDereference, FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct,
-    HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType, NoFieldOnVariant,
-    ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
+    BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer,
+    FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn, NoFieldOnType,
+    NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
     TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
 };
 use crate::{
@@ -56,7 +56,7 @@ use crate::{
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(crate) fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
-        let for_each_attr = |id: HirId, callback: &mut dyn FnMut(&Attribute)| {
+        let has_attr = |id: HirId| -> bool {
             for attr in self.tcx.hir_attrs(id) {
                 // For the purpose of rendering suggestions, disregard attributes
                 // that originate from desugaring of any kind. For example, `x?`
@@ -72,11 +72,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 //     let y: u32 = (x?).try_into().unwrap();
                 //                  +  +++++++++++++++++++++
                 if attr.span().desugaring_kind().is_none() {
-                    callback(attr);
+                    return true;
                 }
             }
+            false
         };
-        expr.precedence(&for_each_attr)
+        expr.precedence(&has_attr)
     }
 
     /// Check an expr with an expectation type, and also demand that the expr's
@@ -689,7 +690,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.check_named_place_expr(oprnd);
                 Ty::new_ptr(self.tcx, ty, mutbl)
             }
-            hir::BorrowKind::Ref => {
+            hir::BorrowKind::Ref | hir::BorrowKind::Pin => {
                 // Note: at this point, we cannot say what the best lifetime
                 // is to use for resulting pointer. We want to use the
                 // shortest lifetime possible so as to avoid spurious borrowck
@@ -705,7 +706,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // whose address was taken can actually be made to live as long
                 // as it needs to live.
                 let region = self.next_region_var(infer::BorrowRegion(expr.span));
-                Ty::new_ref(self.tcx, region, ty, mutbl)
+                match kind {
+                    hir::BorrowKind::Ref => Ty::new_ref(self.tcx, region, ty, mutbl),
+                    hir::BorrowKind::Pin => Ty::new_pinned_ref(self.tcx, region, ty, mutbl),
+                    _ => unreachable!(),
+                }
             }
         }
     }
@@ -1651,13 +1656,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     Some(method.def_id),
                 );
 
-                // Functions of type `extern "custom" fn(/* ... */)` cannot be called using
-                // `ExprKind::MethodCall`. These functions have a calling convention that is
-                // unknown to rust, hence it cannot generate code for the call. The only way
-                // to execute such a function is via inline assembly.
-                if let ExternAbi::Custom = method.sig.abi {
-                    self.tcx.dcx().emit_err(crate::errors::AbiCustomCall { span: expr.span });
-                }
+                self.check_call_abi(method.sig.abi, expr.span);
 
                 method.sig.output()
             }
@@ -2158,7 +2157,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             if !self.tcx.features().default_field_values() {
-                let sugg = self.tcx.crate_level_attribute_injection_span(expr.hir_id);
+                let sugg = self.tcx.crate_level_attribute_injection_span();
                 self.dcx().emit_err(BaseExpressionDoubleDot {
                     span: span.shrink_to_hi(),
                     // We only mention enabling the feature if this is a nightly rustc *and* the
@@ -2166,18 +2165,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     default_field_values_suggestion: if self.tcx.sess.is_nightly_build()
                         && missing_mandatory_fields.is_empty()
                         && !missing_optional_fields.is_empty()
-                        && sugg.is_some()
-                    {
-                        sugg
-                    } else {
-                        None
-                    },
-                    default_field_values_help: if self.tcx.sess.is_nightly_build()
-                        && missing_mandatory_fields.is_empty()
-                        && !missing_optional_fields.is_empty()
-                        && sugg.is_none()
                     {
-                        Some(BaseExpressionDoubleDotEnableDefaultFieldValues)
+                        Some(sugg)
                     } else {
                         None
                     },
@@ -3795,7 +3784,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) -> Ty<'tcx> {
         if let rustc_ast::AsmMacro::NakedAsm = asm.asm_macro {
-            if !self.tcx.has_attr(self.body_id, sym::naked) {
+            if !find_attr!(self.tcx.get_all_attrs(self.body_id), AttributeKind::Naked(..)) {
                 self.tcx.dcx().emit_err(NakedAsmOutsideNakedFn { span });
             }
         }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index e979798a402..8c18642e54a 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -12,7 +12,9 @@ use hir::def_id::CRATE_DEF_ID;
 use rustc_errors::DiagCtxtHandle;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::{self as hir, HirId, ItemLocalMap};
-use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason};
+use rustc_hir_analysis::hir_ty_lowering::{
+    HirTyLowerer, InherentAssocCandidate, RegionInferReason,
+};
 use rustc_infer::infer;
 use rustc_infer::traits::{DynCompatibilityViolation, Obligation};
 use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
@@ -20,7 +22,9 @@ use rustc_session::Session;
 use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym};
 use rustc_trait_selection::error_reporting::TypeErrCtxt;
 use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations;
-use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
+use rustc_trait_selection::traits::{
+    self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt,
+};
 
 use crate::coercion::DynamicCoerceMany;
 use crate::fallback::DivergingFallbackBehavior;
@@ -310,6 +314,67 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
         ))
     }
 
+    fn select_inherent_assoc_candidates(
+        &self,
+        span: Span,
+        self_ty: Ty<'tcx>,
+        candidates: Vec<InherentAssocCandidate>,
+    ) -> (Vec<InherentAssocCandidate>, Vec<FulfillmentError<'tcx>>) {
+        let tcx = self.tcx();
+        let infcx = &self.infcx;
+        let mut fulfillment_errors = vec![];
+
+        let mut filter_iat_candidate = |self_ty, impl_| {
+            let ocx = ObligationCtxt::new_with_diagnostics(self);
+            let self_ty = ocx.normalize(&ObligationCause::dummy(), self.param_env, self_ty);
+
+            let impl_args = infcx.fresh_args_for_item(span, impl_);
+            let impl_ty = tcx.type_of(impl_).instantiate(tcx, impl_args);
+            let impl_ty = ocx.normalize(&ObligationCause::dummy(), self.param_env, impl_ty);
+
+            // Check that the self types can be related.
+            if ocx.eq(&ObligationCause::dummy(), self.param_env, impl_ty, self_ty).is_err() {
+                return false;
+            }
+
+            // Check whether the impl imposes obligations we have to worry about.
+            let impl_bounds = tcx.predicates_of(impl_).instantiate(tcx, impl_args);
+            let impl_bounds = ocx.normalize(&ObligationCause::dummy(), self.param_env, impl_bounds);
+            let impl_obligations = traits::predicates_for_generics(
+                |_, _| ObligationCause::dummy(),
+                self.param_env,
+                impl_bounds,
+            );
+            ocx.register_obligations(impl_obligations);
+
+            let mut errors = ocx.select_where_possible();
+            if !errors.is_empty() {
+                fulfillment_errors.append(&mut errors);
+                return false;
+            }
+
+            true
+        };
+
+        let mut universes = if self_ty.has_escaping_bound_vars() {
+            vec![None; self_ty.outer_exclusive_binder().as_usize()]
+        } else {
+            vec![]
+        };
+
+        let candidates =
+            traits::with_replaced_escaping_bound_vars(infcx, &mut universes, self_ty, |self_ty| {
+                candidates
+                    .into_iter()
+                    .filter(|&InherentAssocCandidate { impl_, .. }| {
+                        infcx.probe(|_| filter_iat_candidate(self_ty, impl_))
+                    })
+                    .collect()
+            });
+
+        (candidates, fulfillment_errors)
+    }
+
     fn lower_assoc_item_path(
         &self,
         span: Span,
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 043a687914b..1cc618e2aee 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -42,6 +42,7 @@ mod writeback;
 
 pub use coercion::can_coerce;
 use fn_ctxt::FnCtxt;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
@@ -55,8 +56,8 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_session::config;
+use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{Span, sym};
 use tracing::{debug, instrument};
 use typeck_root_ctxt::TypeckRootCtxt;
 
@@ -173,7 +174,7 @@ fn typeck_with_inspect<'tcx>(
                 .map(|(idx, ty)| fcx.normalize(arg_span(idx), ty)),
         );
 
-        if tcx.has_attr(def_id, sym::naked) {
+        if find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(..)) {
             naked_functions::typeck_naked_fn(tcx, def_id, body);
         }
 
diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs
index b06e0704b6f..80eab578f13 100644
--- a/compiler/rustc_hir_typeck/src/loops.rs
+++ b/compiler/rustc_hir_typeck/src/loops.rs
@@ -2,6 +2,8 @@ use std::collections::BTreeMap;
 use std::fmt;
 
 use Context::*;
+use rustc_ast::Label;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
@@ -14,8 +16,9 @@ use rustc_span::hygiene::DesugaringKind;
 use rustc_span::{BytePos, Span};
 
 use crate::errors::{
-    BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
-    OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
+    BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ConstContinueBadLabel,
+    ContinueLabeledBlock, OutsideLoop, OutsideLoopSuggestion, UnlabeledCfInWhileCondition,
+    UnlabeledInLabeledBlock,
 };
 
 /// The context in which a block is encountered.
@@ -37,6 +40,11 @@ enum Context {
     AnonConst,
     /// E.g. `const { ... }`.
     ConstBlock,
+    /// E.g. `#[loop_match] loop { state = 'label: { /* ... */ } }`.
+    LoopMatch {
+        /// The label of the labeled block (not of the loop itself).
+        labeled_block: Label,
+    },
 }
 
 #[derive(Clone)]
@@ -141,7 +149,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
                 }
             }
             hir::ExprKind::Loop(ref b, _, source, _) => {
-                self.with_context(Loop(source), |v| v.visit_block(b));
+                let cx = match self.is_loop_match(e, b) {
+                    Some(labeled_block) => LoopMatch { labeled_block },
+                    None => Loop(source),
+                };
+
+                self.with_context(cx, |v| v.visit_block(b));
             }
             hir::ExprKind::Closure(&hir::Closure {
                 ref fn_decl, body, fn_decl_span, kind, ..
@@ -197,6 +210,23 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
                     Err(hir::LoopIdError::UnresolvedLabel) => None,
                 };
 
+                // A `#[const_continue]` must break to a block in a `#[loop_match]`.
+                if find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::ConstContinue(_)) {
+                    if let Some(break_label) = break_label.label {
+                        let is_target_label = |cx: &Context| match cx {
+                            Context::LoopMatch { labeled_block } => {
+                                break_label.ident.name == labeled_block.ident.name
+                            }
+                            _ => false,
+                        };
+
+                        if !self.cx_stack.iter().rev().any(is_target_label) {
+                            let span = break_label.ident.span;
+                            self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
+                        }
+                    }
+                }
+
                 if let Some(Node::Block(_)) = loop_id.map(|id| self.tcx.hir_node(id)) {
                     return;
                 }
@@ -299,7 +329,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
         cx_pos: usize,
     ) {
         match self.cx_stack[cx_pos] {
-            LabeledBlock | Loop(_) => {}
+            LabeledBlock | Loop(_) | LoopMatch { .. } => {}
             Closure(closure_span) => {
                 self.tcx.dcx().emit_err(BreakInsideClosure {
                     span,
@@ -380,4 +410,36 @@ impl<'hir> CheckLoopVisitor<'hir> {
             });
         }
     }
+
+    /// Is this a loop annotated with `#[loop_match]` that looks syntactically sound?
+    fn is_loop_match(
+        &self,
+        e: &'hir hir::Expr<'hir>,
+        body: &'hir hir::Block<'hir>,
+    ) -> Option<Label> {
+        if !find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::LoopMatch(_)) {
+            return None;
+        }
+
+        // NOTE: Diagnostics are emitted during MIR construction.
+
+        // Accept either `state = expr` or `state = expr;`.
+        let loop_body_expr = match body.stmts {
+            [] => match body.expr {
+                Some(expr) => expr,
+                None => return None,
+            },
+            [single] if body.expr.is_none() => match single.kind {
+                hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => expr,
+                _ => return None,
+            },
+            [..] => return None,
+        };
+
+        let hir::ExprKind::Assign(_, rhs_expr, _) = loop_body_expr.kind else { return None };
+
+        let hir::ExprKind::Block(_, label) = rhs_expr.kind else { return None };
+
+        label
+    }
 }
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index a3fdf200c8e..589dbb53116 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1727,7 +1727,6 @@ impl<'tcx> Pick<'tcx> {
             }
             tcx.disabled_nightly_features(
                 lint,
-                Some(scope_expr_id),
                 self.unstable_candidates.iter().map(|(candidate, feature)| {
                     (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature)
                 }),
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 288d915e85c..f446bc468e4 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -1723,8 +1723,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Don't emit a suggestion if we found an actual method
             // that had unsatisfied trait bounds
             if unsatisfied_predicates.is_empty()
-                // ...or if we already suggested that name because of `rustc_confusable` annotation.
+                // ...or if we already suggested that name because of `rustc_confusable` annotation
                 && Some(similar_candidate.name()) != confusable_suggested
+                // and if we aren't in an expansion.
+                && !span.from_expansion()
             {
                 self.find_likely_intended_associated_item(
                     &mut err,
@@ -3475,9 +3477,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         err: &mut Diag<'_>,
         item_name: Ident,
-        valid_out_of_scope_traits: Vec<DefId>,
+        mut valid_out_of_scope_traits: Vec<DefId>,
         explain: bool,
     ) -> bool {
+        valid_out_of_scope_traits.retain(|id| self.tcx.is_user_visible_dep(id.krate));
         if !valid_out_of_scope_traits.is_empty() {
             let mut candidates = valid_out_of_scope_traits;
             candidates.sort_by_key(|id| self.tcx.def_path_str(id));
@@ -4382,7 +4385,7 @@ pub(crate) struct TraitInfo {
 /// Retrieves all traits in this crate and any dependent crates,
 /// and wraps them into `TraitInfo` for custom sorting.
 pub(crate) fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {
-    tcx.all_traits().map(|def_id| TraitInfo { def_id }).collect()
+    tcx.all_traits_including_private().map(|def_id| TraitInfo { def_id }).collect()
 }
 
 fn print_disambiguation_help<'tcx>(
diff --git a/compiler/rustc_hir_typeck/src/naked_functions.rs b/compiler/rustc_hir_typeck/src/naked_functions.rs
index 2518d6478e6..d055fa68fc3 100644
--- a/compiler/rustc_hir_typeck/src/naked_functions.rs
+++ b/compiler/rustc_hir_typeck/src/naked_functions.rs
@@ -1,12 +1,13 @@
 //! Checks validity of naked functions.
 
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::{ExprKind, HirIdSet, StmtKind};
 use rustc_middle::span_bug;
 use rustc_middle::ty::TyCtxt;
-use rustc_span::{Span, sym};
+use rustc_span::Span;
 
 use crate::errors::{
     NakedFunctionsAsmBlock, NakedFunctionsMustNakedAsm, NoPatterns, ParamsNotAllowed,
@@ -20,7 +21,7 @@ pub(crate) fn typeck_naked_fn<'tcx>(
     def_id: LocalDefId,
     body: &'tcx hir::Body<'tcx>,
 ) {
-    debug_assert!(tcx.has_attr(def_id, sym::naked));
+    debug_assert!(find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Naked(..)));
     check_no_patterns(tcx, body.params);
     check_no_parameters_use(tcx, body);
     check_asm(tcx, def_id, body);
diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs
index 67f13192b52..2385c68ef6b 100644
--- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs
+++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs
@@ -7,8 +7,11 @@
 //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
 
 use rustc_macros::extension;
-use rustc_middle::bug;
-use rustc_middle::ty::{self, FnMutDelegate, GenericArgKind, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{
+    self, DelayedMap, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
+    TypeVisitableExt, TypeVisitor,
+};
+use rustc_type_ir::TypeVisitable;
 
 use crate::infer::canonical::{Canonical, CanonicalVarValues};
 
@@ -58,23 +61,169 @@ where
     T: TypeFoldable<TyCtxt<'tcx>>,
 {
     if var_values.var_values.is_empty() {
-        value
-    } else {
-        let delegate = FnMutDelegate {
-            regions: &mut |br: ty::BoundRegion| match var_values[br.var].kind() {
-                GenericArgKind::Lifetime(l) => l,
-                r => bug!("{:?} is a region but value is {:?}", br, r),
-            },
-            types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].kind() {
-                GenericArgKind::Type(ty) => ty,
-                r => bug!("{:?} is a type but value is {:?}", bound_ty, r),
-            },
-            consts: &mut |bound_ct: ty::BoundVar| match var_values[bound_ct].kind() {
-                GenericArgKind::Const(ct) => ct,
-                c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
-            },
-        };
-
-        tcx.replace_escaping_bound_vars_uncached(value, delegate)
+        return value;
     }
+
+    value.fold_with(&mut CanonicalInstantiator {
+        tcx,
+        current_index: ty::INNERMOST,
+        var_values: var_values.var_values,
+        cache: Default::default(),
+    })
+}
+
+/// Replaces the bound vars in a canonical binder with var values.
+struct CanonicalInstantiator<'tcx> {
+    tcx: TyCtxt<'tcx>,
+
+    // The values that the bound vars are are being instantiated with.
+    var_values: ty::GenericArgsRef<'tcx>,
+
+    /// As with `BoundVarReplacer`, represents the index of a binder *just outside*
+    /// the ones we have visited.
+    current_index: ty::DebruijnIndex,
+
+    // Instantiation is a pure function of `DebruijnIndex` and `Ty`.
+    cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for CanonicalInstantiator<'tcx> {
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match *t.kind() {
+            ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
+                self.var_values[bound_ty.var.as_usize()].expect_ty()
+            }
+            _ => {
+                if !t.has_vars_bound_at_or_above(self.current_index) {
+                    t
+                } else if let Some(&t) = self.cache.get(&(self.current_index, t)) {
+                    t
+                } else {
+                    let res = t.super_fold_with(self);
+                    assert!(self.cache.insert((self.current_index, t), res));
+                    res
+                }
+            }
+        }
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match r.kind() {
+            ty::ReBound(debruijn, br) if debruijn == self.current_index => {
+                self.var_values[br.var.as_usize()].expect_region()
+            }
+            _ => r,
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        match ct.kind() {
+            ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
+                self.var_values[bound_const.as_usize()].expect_const()
+            }
+            _ => ct.super_fold_with(self),
+        }
+    }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
+
+    fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
+        if !c.has_vars_bound_at_or_above(self.current_index) {
+            return c;
+        }
+
+        // Since instantiation is a function of `DebruijnIndex`, we don't want
+        // to have to cache more copies of clauses when we're inside of binders.
+        // Since we currently expect to only have clauses in the outermost
+        // debruijn index, we just fold if we're inside of a binder.
+        if self.current_index > ty::INNERMOST {
+            return c.super_fold_with(self);
+        }
+
+        // Our cache key is `(clauses, var_values)`, but we also don't care about
+        // var values that aren't named in the clauses, since they can change without
+        // affecting the output. Since `ParamEnv`s are cached first, we compute the
+        // last var value that is mentioned in the clauses, and cut off the list so
+        // that we have more hits in the cache.
+
+        // We also cache the computation of "highest var named by clauses" since that
+        // is both expensive (depending on the size of the clauses) and a pure function.
+        let index = *self
+            .tcx
+            .highest_var_in_clauses_cache
+            .lock()
+            .entry(c)
+            .or_insert_with(|| highest_var_in_clauses(c));
+        let c_args = &self.var_values[..=index];
+
+        if let Some(c) = self.tcx.clauses_cache.lock().get(&(c, c_args)) {
+            c
+        } else {
+            let folded = c.super_fold_with(self);
+            self.tcx.clauses_cache.lock().insert((c, c_args), folded);
+            folded
+        }
+    }
+}
+
+fn highest_var_in_clauses<'tcx>(c: ty::Clauses<'tcx>) -> usize {
+    struct HighestVarInClauses {
+        max_var: usize,
+        current_index: ty::DebruijnIndex,
+    }
+    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HighestVarInClauses {
+        fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+            &mut self,
+            t: &ty::Binder<'tcx, T>,
+        ) -> Self::Result {
+            self.current_index.shift_in(1);
+            let t = t.super_visit_with(self);
+            self.current_index.shift_out(1);
+            t
+        }
+        fn visit_ty(&mut self, t: Ty<'tcx>) {
+            if let ty::Bound(debruijn, bound_ty) = *t.kind()
+                && debruijn == self.current_index
+            {
+                self.max_var = self.max_var.max(bound_ty.var.as_usize());
+            } else if t.has_vars_bound_at_or_above(self.current_index) {
+                t.super_visit_with(self);
+            }
+        }
+        fn visit_region(&mut self, r: ty::Region<'tcx>) {
+            if let ty::ReBound(debruijn, bound_region) = r.kind()
+                && debruijn == self.current_index
+            {
+                self.max_var = self.max_var.max(bound_region.var.as_usize());
+            }
+        }
+        fn visit_const(&mut self, ct: ty::Const<'tcx>) {
+            if let ty::ConstKind::Bound(debruijn, bound_const) = ct.kind()
+                && debruijn == self.current_index
+            {
+                self.max_var = self.max_var.max(bound_const.as_usize());
+            } else if ct.has_vars_bound_at_or_above(self.current_index) {
+                ct.super_visit_with(self);
+            }
+        }
+    }
+    let mut visitor = HighestVarInClauses { max_var: 0, current_index: ty::INNERMOST };
+    c.visit_with(&mut visitor);
+    visitor.max_var
 }
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index ff28dbeaee6..a72a7958787 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-rustc-rayon-core = { version = "0.5.0" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_lowering = { path = "../rustc_ast_lowering" }
 rustc_ast_passes = { path = "../rustc_ast_passes" }
@@ -43,6 +42,7 @@ rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
 rustc_target = { path = "../rustc_target" }
+rustc_thread_pool = { path = "../rustc_thread_pool" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_traits = { path = "../rustc_traits" }
 rustc_ty_utils = { path = "../rustc_ty_utils" }
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index e824e9d4aa9..c46e879b976 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -18,7 +18,6 @@ use rustc_parse::parser::attr::AllowLeadingUnsafe;
 use rustc_query_impl::QueryCtxt;
 use rustc_query_system::query::print_query_stack;
 use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName};
-use rustc_session::filesearch::sysroot_with_fallback;
 use rustc_session::parse::ParseSess;
 use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint};
 use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs};
@@ -52,10 +51,9 @@ pub struct Compiler {
 pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
     cfgs.into_iter()
         .map(|s| {
-            let psess = ParseSess::with_silent_emitter(
+            let psess = ParseSess::with_fatal_emitter(
                 vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
                 format!("this error occurred on the command line: `--cfg={s}`"),
-                true,
             );
             let filename = FileName::cfg_spec_source_code(&s);
 
@@ -116,10 +114,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
     let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
 
     for s in specs {
-        let psess = ParseSess::with_silent_emitter(
+        let psess = ParseSess::with_fatal_emitter(
             vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
             format!("this error occurred on the command line: `--check-cfg={s}`"),
-            true,
         );
         let filename = FileName::cfg_spec_source_code(&s);
 
@@ -407,8 +404,11 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
 
     crate::callbacks::setup_callbacks();
 
-    let sysroot = config.opts.sysroot.clone();
-    let target = config::build_target_config(&early_dcx, &config.opts.target_triple, &sysroot);
+    let target = config::build_target_config(
+        &early_dcx,
+        &config.opts.target_triple,
+        config.opts.sysroot.path(),
+    );
     let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
     let path_mapping = config.opts.file_path_mapping();
     let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target);
@@ -428,7 +428,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
             let codegen_backend = match config.make_codegen_backend {
                 None => util::get_codegen_backend(
                     &early_dcx,
-                    &sysroot,
+                    &config.opts.sysroot,
                     config.opts.unstable_opts.codegen_backend.as_deref(),
                     &target,
                 ),
@@ -442,7 +442,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
             let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
 
             let bundle = match rustc_errors::fluent_bundle(
-                sysroot_with_fallback(&config.opts.sysroot),
+                &config.opts.sysroot.all_paths().collect::<Vec<_>>(),
                 config.opts.unstable_opts.translate_lang.clone(),
                 config.opts.unstable_opts.translate_additional_ftl.as_deref(),
                 config.opts.unstable_opts.translate_directionality_markers,
@@ -471,7 +471,6 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
                 locale_resources,
                 config.lint_caps,
                 target,
-                sysroot,
                 util::rustc_version_str().unwrap_or("unknown"),
                 config.ice_file,
                 config.using_internal_features,
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 02d1ebdb31a..edfb05e2ccd 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -11,6 +11,7 @@ use rustc_data_structures::jobserver::Proxy;
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal};
 use rustc_data_structures::{parallel, thousands};
+use rustc_errors::timings::TimingSection;
 use rustc_expand::base::{ExtCtxt, LintStoreExpand};
 use rustc_feature::Features;
 use rustc_fs_util::try_canonicalize;
@@ -192,7 +193,7 @@ fn configure_and_expand(
         // Create the config for macro expansion
         let recursion_limit = get_recursion_limit(pre_configured_attrs, sess);
         let cfg = rustc_expand::expand::ExpansionConfig {
-            crate_name: crate_name.to_string(),
+            crate_name,
             features,
             recursion_limit,
             trace_mac: sess.opts.unstable_opts.trace_macros,
@@ -354,9 +355,9 @@ fn print_macro_stats(ecx: &ExtCtxt<'_>) {
             "{prefix} {:<name_w$}{:>uses_w$}{:>lines_w$}{:>avg_lines_w$}{:>bytes_w$}{:>avg_bytes_w$}",
             name,
             thousands::usize_with_underscores(uses),
-            thousands::isize_with_underscores(lines),
+            thousands::usize_with_underscores(lines),
             thousands::f64p1_with_underscores(avg_lines),
-            thousands::isize_with_underscores(bytes),
+            thousands::usize_with_underscores(bytes),
             thousands::f64p1_with_underscores(avg_bytes),
         );
     }
@@ -370,7 +371,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) {
     let mut lint_buffer = resolver.lint_buffer.steal();
 
     if sess.opts.unstable_opts.input_stats {
-        input_stats::print_ast_stats(krate, "POST EXPANSION AST STATS", "ast-stats");
+        input_stats::print_ast_stats(tcx, krate);
     }
 
     // Needs to go *after* expansion to be able to check the results of macro expansion.
@@ -1011,8 +1012,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
 
     // Prefetch this to prevent multiple threads from blocking on it later.
     // This is needed since the `hir_id_validator::check_crate` call above is not guaranteed
-    // to use `hir_crate`.
-    tcx.ensure_done().hir_crate(());
+    // to use `hir_crate_items`.
+    tcx.ensure_done().hir_crate_items(());
 
     let sess = tcx.sess;
     sess.time("misc_checking_1", || {
@@ -1176,6 +1177,8 @@ pub(crate) fn start_codegen<'tcx>(
     codegen_backend: &dyn CodegenBackend,
     tcx: TyCtxt<'tcx>,
 ) -> (Box<dyn Any>, EncodedMetadata) {
+    tcx.sess.timings.start_section(tcx.sess.dcx(), TimingSection::Codegen);
+
     // Hook for tests.
     if let Some((def_id, _)) = tcx.entry_fn(())
         && tcx.has_attr(def_id, sym::rustc_delayed_bug_from_inside_query)
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 877440ec7d2..2bc30fa7cb0 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -48,6 +48,7 @@ impl Linker {
         let (codegen_results, work_products) = sess.time("finish_ongoing_codegen", || {
             codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames)
         });
+        sess.timings.end_section(sess.dcx(), TimingSection::Codegen);
 
         sess.dcx().abort_if_errors();
 
@@ -89,7 +90,7 @@ impl Linker {
         }
 
         let _timer = sess.prof.verbose_generic_activity("link_crate");
-        let _timing = sess.timings.start_section(sess.dcx(), TimingSection::Linking);
+        let _timing = sess.timings.section_guard(sess.dcx(), TimingSection::Linking);
         codegen_backend.link(sess, codegen_results, self.metadata, &self.output_filenames)
     }
 }
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index a0012b04c4f..360b5629e9d 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -41,9 +41,11 @@ where
 
     let matches = optgroups().parse(args).unwrap();
     let sessopts = build_session_options(&mut early_dcx, &matches);
-    let sysroot = sessopts.sysroot.clone();
-    let target =
-        rustc_session::config::build_target_config(&early_dcx, &sessopts.target_triple, &sysroot);
+    let target = rustc_session::config::build_target_config(
+        &early_dcx,
+        &sessopts.target_triple,
+        sessopts.sysroot.path(),
+    );
     let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target);
     let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm();
     let sm_inputs = Some(SourceMapInputs {
@@ -72,7 +74,6 @@ where
             vec![],
             Default::default(),
             target,
-            sysroot,
             "",
             None,
             &USING_INTERNAL_FEATURES,
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 8bdc24d47d9..0ca4fcc66ca 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -11,7 +11,7 @@ use rustc_data_structures::sync;
 use rustc_metadata::{DylibError, load_symbol_from_dylib};
 use rustc_middle::ty::CurrentGcx;
 use rustc_parse::validate_attr;
-use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, host_tuple};
+use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple};
 use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer};
 use rustc_session::output::{CRATE_TYPES, categorize_crate_type};
 use rustc_session::{EarlyDiagCtxt, Session, filesearch};
@@ -208,7 +208,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
 
     let proxy_ = Arc::clone(&proxy);
     let proxy__ = Arc::clone(&proxy);
-    let builder = rayon_core::ThreadPoolBuilder::new()
+    let builder = rustc_thread_pool::ThreadPoolBuilder::new()
         .thread_name(|_| "rustc".to_string())
         .acquire_thread_handler(move || proxy_.acquire_thread())
         .release_thread_handler(move || proxy__.release_thread())
@@ -218,7 +218,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
             // locals to it. The new thread runs the deadlock handler.
 
             let current_gcx2 = current_gcx2.clone();
-            let registry = rayon_core::Registry::current();
+            let registry = rustc_thread_pool::Registry::current();
             let session_globals = rustc_span::with_session_globals(|session_globals| {
                 session_globals as *const SessionGlobals as usize
             });
@@ -265,7 +265,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
             builder
                 .build_scoped(
                     // Initialize each new worker thread when created.
-                    move |thread: rayon_core::ThreadBuilder| {
+                    move |thread: rustc_thread_pool::ThreadBuilder| {
                         // Register the thread for use with the `WorkerLocal` type.
                         registry.register();
 
@@ -274,7 +274,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
                         })
                     },
                     // Run `f` on the first thread in the thread pool.
-                    move |pool: &rayon_core::ThreadPool| {
+                    move |pool: &rustc_thread_pool::ThreadPool| {
                         pool.install(|| f(current_gcx.into_inner(), proxy))
                     },
                 )
@@ -305,7 +305,7 @@ fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBacken
 /// A name of `None` indicates that the default backend should be used.
 pub fn get_codegen_backend(
     early_dcx: &EarlyDiagCtxt,
-    sysroot: &Path,
+    sysroot: &Sysroot,
     backend_name: Option<&str>,
     target: &Target,
 ) -> Box<dyn CodegenBackend> {
@@ -336,25 +336,24 @@ pub fn get_codegen_backend(
 // This is used for rustdoc, but it uses similar machinery to codegen backend
 // loading, so we leave the code here. It is potentially useful for other tools
 // that want to invoke the rustc binary while linking to rustc as well.
-pub fn rustc_path<'a>() -> Option<&'a Path> {
+pub fn rustc_path<'a>(sysroot: &Sysroot) -> Option<&'a Path> {
     static RUSTC_PATH: OnceLock<Option<PathBuf>> = OnceLock::new();
 
-    const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR");
-
-    RUSTC_PATH.get_or_init(|| get_rustc_path_inner(BIN_PATH)).as_deref()
-}
-
-fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
-    let candidate = filesearch::get_or_default_sysroot()
-        .join(bin_path)
-        .join(if cfg!(target_os = "windows") { "rustc.exe" } else { "rustc" });
-    candidate.exists().then_some(candidate)
+    RUSTC_PATH
+        .get_or_init(|| {
+            let candidate = sysroot
+                .default
+                .join(env!("RUSTC_INSTALL_BINDIR"))
+                .join(if cfg!(target_os = "windows") { "rustc.exe" } else { "rustc" });
+            candidate.exists().then_some(candidate)
+        })
+        .as_deref()
 }
 
 #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
 fn get_codegen_sysroot(
     early_dcx: &EarlyDiagCtxt,
-    sysroot: &Path,
+    sysroot: &Sysroot,
     backend_name: &str,
 ) -> MakeBackendFn {
     // For now we only allow this function to be called once as it'll dlopen a
@@ -369,10 +368,9 @@ fn get_codegen_sysroot(
     );
 
     let target = host_tuple();
-    let sysroot_candidates = filesearch::sysroot_with_fallback(&sysroot);
 
-    let sysroot = sysroot_candidates
-        .iter()
+    let sysroot = sysroot
+        .all_paths()
         .map(|sysroot| {
             filesearch::make_target_lib_path(sysroot, target).with_file_name("codegen-backends")
         })
@@ -381,8 +379,8 @@ fn get_codegen_sysroot(
             f.exists()
         })
         .unwrap_or_else(|| {
-            let candidates = sysroot_candidates
-                .iter()
+            let candidates = sysroot
+                .all_paths()
                 .map(|p| p.display().to_string())
                 .collect::<Vec<_>>()
                 .join("\n* ");
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index dedea54f8e0..5bbe69c8d65 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -21,6 +21,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast::visit::{FnCtxt, FnKind};
 use rustc_ast::{self as ast, *};
 use rustc_ast_pretty::pprust::expr_to_string;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_errors::{Applicability, LintDiagnostic};
 use rustc_feature::GateIssue;
 use rustc_hir as hir;
@@ -954,7 +955,7 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN
 impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         let attrs = cx.tcx.hir_attrs(it.hir_id());
-        let check_no_mangle_on_generic_fn = |attr: &hir::Attribute,
+        let check_no_mangle_on_generic_fn = |attr_span: Span,
                                              impl_generics: Option<&hir::Generics<'_>>,
                                              generics: &hir::Generics<'_>,
                                              span| {
@@ -967,7 +968,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
                         cx.emit_span_lint(
                             NO_MANGLE_GENERIC_ITEMS,
                             span,
-                            BuiltinNoMangleGeneric { suggestion: attr.span() },
+                            BuiltinNoMangleGeneric { suggestion: attr_span },
                         );
                         break;
                     }
@@ -976,14 +977,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
         };
         match it.kind {
             hir::ItemKind::Fn { generics, .. } => {
-                if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
-                    .or_else(|| attr::find_by_name(attrs, sym::no_mangle))
+                if let Some(attr_span) = attr::find_by_name(attrs, sym::export_name)
+                    .map(|at| at.span())
+                    .or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span))
                 {
-                    check_no_mangle_on_generic_fn(attr, None, generics, it.span);
+                    check_no_mangle_on_generic_fn(attr_span, None, generics, it.span);
                 }
             }
             hir::ItemKind::Const(..) => {
-                if attr::contains_name(attrs, sym::no_mangle) {
+                if find_attr!(attrs, AttributeKind::NoMangle(..)) {
                     // account for "pub const" (#45562)
                     let start = cx
                         .tcx
@@ -1008,11 +1010,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
                 for it in *items {
                     if let hir::AssocItemKind::Fn { .. } = it.kind {
                         let attrs = cx.tcx.hir_attrs(it.id.hir_id());
-                        if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
-                            .or_else(|| attr::find_by_name(attrs, sym::no_mangle))
+                        if let Some(attr_span) = attr::find_by_name(attrs, sym::export_name)
+                            .map(|at| at.span())
+                            .or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span))
                         {
                             check_no_mangle_on_generic_fn(
-                                attr,
+                                attr_span,
                                 Some(generics),
                                 cx.tcx.hir_get_generics(it.id.owner_id.def_id).unwrap(),
                                 it.span,
@@ -1181,11 +1184,11 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
         if fn_kind.asyncness().is_async()
             && !cx.tcx.features().async_fn_track_caller()
             // Now, check if the function has the `#[track_caller]` attribute
-            && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller)
+            && let Some(attr_span) = find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::TrackCaller(span) => *span)
         {
             cx.emit_span_lint(
                 UNGATED_ASYNC_FN_TRACK_CALLER,
-                attr.span(),
+                attr_span,
                 BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess },
             );
         }
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index b6bf45dfbcf..95663204ec3 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -524,6 +524,20 @@ pub trait LintContext {
         });
     }
 
+    /// Emit a lint at `span` from a lazily-constructed lint struct (some type that implements
+    /// `LintDiagnostic`, typically generated by `#[derive(LintDiagnostic)]`).
+    fn emit_span_lint_lazy<S: Into<MultiSpan>, L: for<'a> LintDiagnostic<'a, ()>>(
+        &self,
+        lint: &'static Lint,
+        span: S,
+        decorator: impl FnOnce() -> L,
+    ) {
+        self.opt_span_lint(lint, Some(span), |lint| {
+            let decorator = decorator();
+            decorator.decorate_lint(lint);
+        });
+    }
+
     /// Emit a lint at the appropriate level, with an associated span.
     ///
     /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature
@@ -711,6 +725,15 @@ impl<'tcx> LateContext<'tcx> {
 
     /// Gets the absolute path of `def_id` as a vector of `Symbol`.
     ///
+    /// Note that this is kinda expensive because it has to
+    /// travel the tree and pretty-print. Use sparingly.
+    ///
+    /// If you're trying to match for an item given by its path, use a
+    /// diagnostic item. If you're only interested in given sections, use more
+    /// specific functions, such as [`TyCtxt::crate_name`]
+    ///
+    /// FIXME: It would be great if this could be optimized.
+    ///
     /// # Examples
     ///
     /// ```rust,ignore (no context or def id available)
@@ -855,14 +878,15 @@ impl<'tcx> LateContext<'tcx> {
     /// rendering diagnostic. This is not the same as the precedence that would
     /// be used for pretty-printing HIR by rustc_hir_pretty.
     pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
-        let for_each_attr = |id: hir::HirId, callback: &mut dyn FnMut(&hir::Attribute)| {
+        let has_attr = |id: hir::HirId| -> bool {
             for attr in self.tcx.hir_attrs(id) {
                 if attr.span().desugaring_kind().is_none() {
-                    callback(attr);
+                    return true;
                 }
             }
+            false
         };
-        expr.precedence(&for_each_attr)
+        expr.precedence(&has_attr)
     }
 
     /// If the given expression is a local binding, find the initializer expression.
diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs
index 4c2b82a9a23..481e116d06e 100644
--- a/compiler/rustc_lint/src/expect.rs
+++ b/compiler/rustc_lint/src/expect.rs
@@ -1,5 +1,4 @@
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::CRATE_OWNER_ID;
 use rustc_middle::lint::LintExpectation;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
@@ -18,7 +17,7 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp
 
     let mut expectations = Vec::new();
 
-    for owner in std::iter::once(CRATE_OWNER_ID).chain(krate.owners()) {
+    for owner in krate.owners() {
         let lints = tcx.shallow_lint_levels_on(owner);
         expectations.extend_from_slice(&lints.expectations);
     }
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index c52dbd892bf..c72f8571153 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -482,7 +482,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                     let name = lint_name.as_str();
                     let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
                     let requested_level = RequestedLevel { level, lint_name };
-                    let lint = RenamedLintFromCommandLine { name, suggestion, requested_level };
+                    let lint =
+                        RenamedLintFromCommandLine { name, replace, suggestion, requested_level };
                     self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
                 }
                 CheckLintNameResult::Removed(ref reason) => {
@@ -824,7 +825,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                                 RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
                             let name =
                                 tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
-                            let lint = RenamedLint { name: name.as_str(), suggestion };
+                            let lint = RenamedLint { name: name.as_str(), replace, suggestion };
                             self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
                         }
 
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 20568f35a47..48982bda0a0 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -608,6 +608,7 @@ fn register_builtins(store: &mut LintStore) {
         "converted into hard error, see issue #127323 \
          <https://github.com/rust-lang/rust/issues/127323> for more information",
     );
+    store.register_removed("unsupported_fn_ptr_calling_conventions", "converted into hard error");
     store.register_removed(
         "undefined_naked_function_abi",
         "converted into hard error, see PR #139001 \
diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs
index 95b7b69bd5a..5465968e984 100644
--- a/compiler/rustc_lint/src/lifetime_syntax.rs
+++ b/compiler/rustc_lint/src/lifetime_syntax.rs
@@ -422,12 +422,12 @@ fn build_mismatch_suggestion(
     lifetime_name: &str,
     infos: &[&Info<'_>],
 ) -> lints::MismatchedLifetimeSyntaxesSuggestion {
-    let lifetime_name = lifetime_name.to_owned();
+    let lifetime_name_sugg = lifetime_name.to_owned();
 
     let suggestions = infos.iter().map(|info| info.suggestion(&lifetime_name)).collect();
 
     lints::MismatchedLifetimeSyntaxesSuggestion::Explicit {
-        lifetime_name,
+        lifetime_name_sugg,
         suggestions,
         tool_only: false,
     }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index abdf8e3853b..5e05b58146e 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1089,6 +1089,7 @@ pub(crate) struct DeprecatedLintNameFromCommandLine<'a> {
 #[diag(lint_renamed_lint)]
 pub(crate) struct RenamedLint<'a> {
     pub name: &'a str,
+    pub replace: &'a str,
     #[subdiagnostic]
     pub suggestion: RenamedLintSuggestion<'a>,
 }
@@ -1109,6 +1110,7 @@ pub(crate) enum RenamedLintSuggestion<'a> {
 #[diag(lint_renamed_lint)]
 pub(crate) struct RenamedLintFromCommandLine<'a> {
     pub name: &'a str,
+    pub replace: &'a str,
     #[subdiagnostic]
     pub suggestion: RenamedLintSuggestion<'a>,
     #[subdiagnostic]
@@ -1353,6 +1355,8 @@ pub(crate) struct NonUpperCaseGlobal<'a> {
     pub name: &'a str,
     #[subdiagnostic]
     pub sub: NonUpperCaseGlobalSub,
+    #[subdiagnostic]
+    pub usages: Vec<NonUpperCaseGlobalSubTool>,
 }
 
 #[derive(Subdiagnostic)]
@@ -1362,14 +1366,29 @@ pub(crate) enum NonUpperCaseGlobalSub {
         #[primary_span]
         span: Span,
     },
-    #[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+    #[suggestion(lint_suggestion, code = "{replace}")]
     Suggestion {
         #[primary_span]
         span: Span,
+        #[applicability]
+        applicability: Applicability,
         replace: String,
     },
 }
 
+#[derive(Subdiagnostic)]
+#[suggestion(
+    lint_suggestion,
+    code = "{replace}",
+    applicability = "machine-applicable",
+    style = "tool-only"
+)]
+pub(crate) struct NonUpperCaseGlobalSubTool {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) replace: String,
+}
+
 // noop_method_call.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_noop_method_call)]
@@ -3227,7 +3246,7 @@ pub(crate) enum MismatchedLifetimeSyntaxesSuggestion {
     },
 
     Explicit {
-        lifetime_name: String,
+        lifetime_name_sugg: String,
         suggestions: Vec<(Span, String)>,
         tool_only: bool,
     },
@@ -3261,7 +3280,7 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
                 diag.multipart_suggestion_with_style(
                     fluent::lint_mismatched_lifetime_syntaxes_suggestion_implicit,
                     suggestions,
-                    Applicability::MachineApplicable,
+                    Applicability::MaybeIncorrect,
                     style(tool_only),
                 );
             }
@@ -3276,22 +3295,21 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
                 diag.multipart_suggestion_with_style(
                     fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed,
                     suggestions,
-                    Applicability::MachineApplicable,
+                    Applicability::MaybeIncorrect,
                     style(tool_only),
                 );
             }
 
-            Explicit { lifetime_name, suggestions, tool_only } => {
-                diag.arg("lifetime_name", lifetime_name);
-
+            Explicit { lifetime_name_sugg, suggestions, tool_only } => {
+                diag.arg("lifetime_name_sugg", lifetime_name_sugg);
                 let msg = diag.eagerly_translate(
                     fluent::lint_mismatched_lifetime_syntaxes_suggestion_explicit,
                 );
-
+                diag.remove_arg("lifetime_name_sugg");
                 diag.multipart_suggestion_with_style(
                     msg,
                     suggestions,
-                    Applicability::MachineApplicable,
+                    Applicability::MaybeIncorrect,
                     style(tool_only),
                 );
             }
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 1b60466a589..97e627f2eb2 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -1,9 +1,12 @@
 use rustc_abi::ExternAbi;
-use rustc_attr_data_structures::{AttributeKind, ReprAttr};
+use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr};
 use rustc_attr_parsing::AttributeParser;
+use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::FnKind;
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{FnKind, Visitor};
 use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind};
+use rustc_middle::hir::nested_filter::All;
 use rustc_middle::ty;
 use rustc_session::config::CrateType;
 use rustc_session::{declare_lint, declare_lint_pass};
@@ -13,7 +16,7 @@ use {rustc_ast as ast, rustc_hir as hir};
 
 use crate::lints::{
     NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
-    NonUpperCaseGlobal, NonUpperCaseGlobalSub,
+    NonUpperCaseGlobal, NonUpperCaseGlobalSub, NonUpperCaseGlobalSubTool,
 };
 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 
@@ -396,7 +399,9 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
         match &fk {
             FnKind::Method(ident, sig, ..) => match method_context(cx, id) {
                 MethodLateContext::PlainImpl => {
-                    if sig.header.abi != ExternAbi::Rust && cx.tcx.has_attr(id, sym::no_mangle) {
+                    if sig.header.abi != ExternAbi::Rust
+                        && find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..))
+                    {
                         return;
                     }
                     self.check_snake_case(cx, "method", ident);
@@ -408,7 +413,9 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
             },
             FnKind::ItemFn(ident, _, header) => {
                 // Skip foreign-ABI #[no_mangle] functions (Issue #31924)
-                if header.abi != ExternAbi::Rust && cx.tcx.has_attr(id, sym::no_mangle) {
+                if header.abi != ExternAbi::Rust
+                    && find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..))
+                {
                     return;
                 }
                 self.check_snake_case(cx, "function", ident);
@@ -489,22 +496,82 @@ declare_lint! {
 declare_lint_pass!(NonUpperCaseGlobals => [NON_UPPER_CASE_GLOBALS]);
 
 impl NonUpperCaseGlobals {
-    fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) {
+    fn check_upper_case(cx: &LateContext<'_>, sort: &str, did: Option<LocalDefId>, ident: &Ident) {
         let name = ident.name.as_str();
         if name.chars().any(|c| c.is_lowercase()) {
             let uc = NonSnakeCase::to_snake_case(name).to_uppercase();
+
+            // If the item is exported, suggesting changing it's name would be breaking-change
+            // and could break users without a "nice" applicable fix, so let's avoid it.
+            let can_change_usages = if let Some(did) = did {
+                !cx.tcx.effective_visibilities(()).is_exported(did)
+            } else {
+                false
+            };
+
             // We cannot provide meaningful suggestions
             // if the characters are in the category of "Lowercase Letter".
             let sub = if *name != uc {
-                NonUpperCaseGlobalSub::Suggestion { span: ident.span, replace: uc }
+                NonUpperCaseGlobalSub::Suggestion {
+                    span: ident.span,
+                    replace: uc.clone(),
+                    applicability: if can_change_usages {
+                        Applicability::MachineApplicable
+                    } else {
+                        Applicability::MaybeIncorrect
+                    },
+                }
             } else {
                 NonUpperCaseGlobalSub::Label { span: ident.span }
             };
-            cx.emit_span_lint(
-                NON_UPPER_CASE_GLOBALS,
-                ident.span,
-                NonUpperCaseGlobal { sort, name, sub },
-            );
+
+            struct UsageCollector<'a, 'tcx> {
+                cx: &'tcx LateContext<'a>,
+                did: DefId,
+                collected: Vec<Span>,
+            }
+
+            impl<'v, 'tcx> Visitor<'v> for UsageCollector<'v, 'tcx> {
+                type NestedFilter = All;
+
+                fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+                    self.cx.tcx
+                }
+
+                fn visit_path(
+                    &mut self,
+                    path: &rustc_hir::Path<'v>,
+                    _id: rustc_hir::HirId,
+                ) -> Self::Result {
+                    if let Some(final_seg) = path.segments.last()
+                        && final_seg.res.opt_def_id() == Some(self.did)
+                    {
+                        self.collected.push(final_seg.ident.span);
+                    }
+                }
+            }
+
+            cx.emit_span_lint_lazy(NON_UPPER_CASE_GLOBALS, ident.span, || {
+                // Compute usages lazily as it can expansive and useless when the lint is allowed.
+                // cf. https://github.com/rust-lang/rust/pull/142645#issuecomment-2993024625
+                let usages = if can_change_usages
+                    && *name != uc
+                    && let Some(did) = did
+                {
+                    let mut usage_collector =
+                        UsageCollector { cx, did: did.to_def_id(), collected: Vec::new() };
+                    cx.tcx.hir_walk_toplevel_module(&mut usage_collector);
+                    usage_collector
+                        .collected
+                        .into_iter()
+                        .map(|span| NonUpperCaseGlobalSubTool { span, replace: uc.clone() })
+                        .collect()
+                } else {
+                    vec![]
+                };
+
+                NonUpperCaseGlobal { sort, name, sub, usages }
+            });
         }
     }
 }
@@ -514,12 +581,22 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
         let attrs = cx.tcx.hir_attrs(it.hir_id());
         match it.kind {
             hir::ItemKind::Static(_, ident, ..)
-                if !ast::attr::contains_name(attrs, sym::no_mangle) =>
+                if !find_attr!(attrs, AttributeKind::NoMangle(..)) =>
             {
-                NonUpperCaseGlobals::check_upper_case(cx, "static variable", &ident);
+                NonUpperCaseGlobals::check_upper_case(
+                    cx,
+                    "static variable",
+                    Some(it.owner_id.def_id),
+                    &ident,
+                );
             }
             hir::ItemKind::Const(ident, ..) => {
-                NonUpperCaseGlobals::check_upper_case(cx, "constant", &ident);
+                NonUpperCaseGlobals::check_upper_case(
+                    cx,
+                    "constant",
+                    Some(it.owner_id.def_id),
+                    &ident,
+                );
             }
             _ => {}
         }
@@ -527,7 +604,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
 
     fn check_trait_item(&mut self, cx: &LateContext<'_>, ti: &hir::TraitItem<'_>) {
         if let hir::TraitItemKind::Const(..) = ti.kind {
-            NonUpperCaseGlobals::check_upper_case(cx, "associated constant", &ti.ident);
+            NonUpperCaseGlobals::check_upper_case(cx, "associated constant", None, &ti.ident);
         }
     }
 
@@ -535,7 +612,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
         if let hir::ImplItemKind::Const(..) = ii.kind
             && !assoc_item_in_trait_impl(cx, ii)
         {
-            NonUpperCaseGlobals::check_upper_case(cx, "associated constant", &ii.ident);
+            NonUpperCaseGlobals::check_upper_case(cx, "associated constant", None, &ii.ident);
         }
     }
 
@@ -551,6 +628,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
                     NonUpperCaseGlobals::check_upper_case(
                         cx,
                         "constant in pattern",
+                        None,
                         &segment.ident,
                     );
                 }
@@ -560,7 +638,12 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
 
     fn check_generic_param(&mut self, cx: &LateContext<'_>, param: &hir::GenericParam<'_>) {
         if let GenericParamKind::Const { .. } = param.kind {
-            NonUpperCaseGlobals::check_upper_case(cx, "const parameter", &param.name.ident());
+            NonUpperCaseGlobals::check_upper_case(
+                cx,
+                "const parameter",
+                Some(param.def_id),
+                &param.name.ident(),
+            );
         }
     }
 }
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 1620f425794..a868c887493 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -2,6 +2,7 @@ use std::iter;
 
 use rustc_ast::util::{classify, parser};
 use rustc_ast::{self as ast, ExprKind, HasAttrs as _, StmtKind};
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_errors::{MultiSpan, pluralize};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -368,10 +369,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
         }
 
         fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
-            if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) {
+            if let Some(reason) = find_attr!(
+                cx.tcx.get_all_attrs(def_id),
+                AttributeKind::MustUse { reason, .. } => reason
+            ) {
                 // check for #[must_use = "..."]
-                let reason = attr.value_str();
-                Some(MustUsePath::Def(span, def_id, reason))
+                Some(MustUsePath::Def(span, def_id, *reason))
             } else {
                 None
             }
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index b0ea9689e50..5fa00fcc4a0 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -122,7 +122,6 @@ declare_lint_pass! {
         UNSAFE_OP_IN_UNSAFE_FN,
         UNSTABLE_NAME_COLLISIONS,
         UNSTABLE_SYNTAX_PRE_EXPANSION,
-        UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
         UNUSED_ASSIGNMENTS,
         UNUSED_ASSOCIATED_TYPE_BOUNDS,
         UNUSED_ATTRIBUTES,
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index bc9516b2e0c..04fdada8024 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -220,7 +220,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
     }
 
     /// Generates the code for a field with no attributes.
-    fn generate_field_arg(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
+    fn generate_field_arg(&mut self, binding_info: &BindingInfo<'_>) -> (TokenStream, TokenStream) {
         let diag = &self.parent.diag;
 
         let field = binding_info.ast();
@@ -230,12 +230,16 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
         let ident = field.ident.as_ref().unwrap();
         let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
 
-        quote! {
+        let args = quote! {
             #diag.arg(
                 stringify!(#ident),
                 #field_binding
             );
-        }
+        };
+        let remove_args = quote! {
+            #diag.remove_arg(stringify!(#ident));
+        };
+        (args, remove_args)
     }
 
     /// Generates the necessary code for all attributes on a field.
@@ -600,8 +604,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
 
             calls.extend(call);
         }
-
-        let plain_args: TokenStream = self
+        let store_args = quote! {
+            #diag.store_args();
+        };
+        let restore_args = quote! {
+            #diag.restore_args();
+        };
+        let (plain_args, remove_args): (TokenStream, TokenStream) = self
             .variant
             .bindings()
             .iter()
@@ -610,12 +619,23 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
             .collect();
 
         let formatting_init = &self.formatting_init;
+
+        // For #[derive(Subdiagnostic)]
+        //
+        // - Store args of the main diagnostic for later restore.
+        // - add args of subdiagnostic.
+        // - Generate the calls, such as note, label, etc.
+        // - Remove the arguments for allowing Vec<Subdiagnostic> to be used.
+        // - Restore the arguments for allowing main and subdiagnostic share the same fields.
         Ok(quote! {
             #init
             #formatting_init
             #attr_args
+            #store_args
             #plain_args
             #calls
+            #remove_args
+            #restore_args
         })
     }
 }
diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs
index fcae33c73c9..1089c4ac71c 100644
--- a/compiler/rustc_metadata/src/dependency_format.rs
+++ b/compiler/rustc_metadata/src/dependency_format.rs
@@ -314,7 +314,7 @@ fn add_library(
                     crate_name: tcx.crate_name(cnum),
                     non_static_deps: unavailable_as_static
                         .drain(..)
-                        .map(|cnum| NonStaticCrateDep { crate_name: tcx.crate_name(cnum) })
+                        .map(|cnum| NonStaticCrateDep { crate_name_: tcx.crate_name(cnum) })
                         .collect(),
                     rustc_driver_help: linking_to_rustc_driver.then_some(RustcDriverHelp),
                 });
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index 71da4290174..4a3b43167cf 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -45,7 +45,8 @@ pub struct CrateDepMultiple {
 #[derive(Subdiagnostic)]
 #[note(metadata_crate_dep_not_static)]
 pub struct NonStaticCrateDep {
-    pub crate_name: Symbol,
+    /// It's different from `crate_name` in main Diagnostic.
+    pub crate_name_: Symbol,
 }
 
 #[derive(Subdiagnostic)]
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 389a4ab7466..23ffb1e487f 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -2,10 +2,10 @@
 #![allow(internal_features)]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
-#![feature(coroutines)]
 #![feature(decl_macro)]
 #![feature(error_iter)]
 #![feature(file_buffered)]
+#![feature(gen_blocks)]
 #![feature(if_let_guard)]
 #![feature(iter_from_coroutine)]
 #![feature(macro_metavar_expr)]
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index 259bcb1b96d..941f16bd960 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -321,7 +321,7 @@ impl<'a> CrateLocator<'a> {
 
         CrateLocator {
             only_needs_metadata,
-            sysroot: &sess.sysroot,
+            sysroot: sess.opts.sysroot.path(),
             metadata_loader,
             cfg_version: sess.cfg_version,
             crate_name,
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 0bc980b4d9f..d886f25247f 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -3,7 +3,7 @@
 use std::iter::TrustedLen;
 use std::path::{Path, PathBuf};
 use std::sync::{Arc, OnceLock};
-use std::{io, iter, mem};
+use std::{io, mem};
 
 pub(super) use cstore_impl::provide;
 use rustc_ast as ast;
@@ -1272,34 +1272,30 @@ impl<'a> CrateMetadataRef<'a> {
         id: DefIndex,
         sess: &'a Session,
     ) -> impl Iterator<Item = ModChild> {
-        iter::from_coroutine(
-            #[coroutine]
-            move || {
-                if let Some(data) = &self.root.proc_macro_data {
-                    // If we are loading as a proc macro, we want to return
-                    // the view of this crate as a proc macro crate.
-                    if id == CRATE_DEF_INDEX {
-                        for child_index in data.macros.decode(self) {
-                            yield self.get_mod_child(child_index, sess);
-                        }
-                    }
-                } else {
-                    // Iterate over all children.
-                    let non_reexports =
-                        self.root.tables.module_children_non_reexports.get(self, id);
-                    for child_index in non_reexports.unwrap().decode(self) {
+        gen move {
+            if let Some(data) = &self.root.proc_macro_data {
+                // If we are loading as a proc macro, we want to return
+                // the view of this crate as a proc macro crate.
+                if id == CRATE_DEF_INDEX {
+                    for child_index in data.macros.decode(self) {
                         yield self.get_mod_child(child_index, sess);
                     }
+                }
+            } else {
+                // Iterate over all children.
+                let non_reexports = self.root.tables.module_children_non_reexports.get(self, id);
+                for child_index in non_reexports.unwrap().decode(self) {
+                    yield self.get_mod_child(child_index, sess);
+                }
 
-                    let reexports = self.root.tables.module_children_reexports.get(self, id);
-                    if !reexports.is_default() {
-                        for reexport in reexports.decode((self, sess)) {
-                            yield reexport;
-                        }
+                let reexports = self.root.tables.module_children_reexports.get(self, id);
+                if !reexports.is_default() {
+                    for reexport in reexports.decode((self, sess)) {
+                        yield reexport;
                     }
                 }
-            },
-        )
+            }
+        }
     }
 
     fn is_ctfe_mir_available(self, id: DefIndex) -> bool {
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 00bd32eb0eb..d74918235b6 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -5,7 +5,7 @@ use std::io::{Read, Seek, Write};
 use std::path::{Path, PathBuf};
 use std::sync::Arc;
 
-use rustc_ast::attr::AttributeExt;
+use rustc_attr_data_structures::EncodeCrossCrate;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::memmap::{Mmap, MmapMut};
 use rustc_data_structures::sync::{join, par_for_each_in};
@@ -762,6 +762,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         assert_eq!(total_bytes, computed_total_bytes);
 
         if tcx.sess.opts.unstable_opts.meta_stats {
+            use std::fmt::Write;
+
             self.opaque.flush();
 
             // Rewind and re-read all the metadata to count the zero bytes we wrote.
@@ -777,31 +779,44 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             assert_eq!(self.opaque.file().stream_position().unwrap(), pos_before_rewind);
 
             stats.sort_by_key(|&(_, usize)| usize);
+            stats.reverse(); // bigger items first
 
             let prefix = "meta-stats";
             let perc = |bytes| (bytes * 100) as f64 / total_bytes as f64;
 
-            eprintln!("{prefix} METADATA STATS");
-            eprintln!("{} {:<23}{:>10}", prefix, "Section", "Size");
-            eprintln!("{prefix} ----------------------------------------------------------------");
+            let section_w = 23;
+            let size_w = 10;
+            let banner_w = 64;
+
+            // We write all the text into a string and print it with a single
+            // `eprint!`. This is an attempt to minimize interleaved text if multiple
+            // rustc processes are printing macro-stats at the same time (e.g. with
+            // `RUSTFLAGS='-Zmeta-stats' cargo build`). It still doesn't guarantee
+            // non-interleaving, though.
+            let mut s = String::new();
+            _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w));
+            _ = writeln!(s, "{prefix} METADATA STATS: {}", tcx.crate_name(LOCAL_CRATE));
+            _ = writeln!(s, "{prefix} {:<section_w$}{:>size_w$}", "Section", "Size");
+            _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w));
             for (label, size) in stats {
-                eprintln!(
-                    "{} {:<23}{:>10} ({:4.1}%)",
-                    prefix,
+                _ = writeln!(
+                    s,
+                    "{prefix} {:<section_w$}{:>size_w$} ({:4.1}%)",
                     label,
                     usize_with_underscores(size),
                     perc(size)
                 );
             }
-            eprintln!("{prefix} ----------------------------------------------------------------");
-            eprintln!(
-                "{} {:<23}{:>10} (of which {:.1}% are zero bytes)",
-                prefix,
+            _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w));
+            _ = writeln!(
+                s,
+                "{prefix} {:<section_w$}{:>size_w$} (of which {:.1}% are zero bytes)",
                 "Total",
                 usize_with_underscores(total_bytes),
                 perc(zero_bytes)
             );
-            eprintln!("{prefix}");
+            _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w));
+            eprint!("{s}");
         }
 
         root
@@ -824,9 +839,13 @@ struct AnalyzeAttrState<'a> {
 /// visibility: this is a piece of data that can be computed once per defid, and not once per
 /// attribute. Some attributes would only be usable downstream if they are public.
 #[inline]
-fn analyze_attr(attr: &impl AttributeExt, state: &mut AnalyzeAttrState<'_>) -> bool {
+fn analyze_attr(attr: &hir::Attribute, state: &mut AnalyzeAttrState<'_>) -> bool {
     let mut should_encode = false;
-    if let Some(name) = attr.name()
+    if let hir::Attribute::Parsed(p) = attr
+        && p.encode_cross_crate() == EncodeCrossCrate::No
+    {
+        // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates.
+    } else if let Some(name) = attr.name()
         && !rustc_feature::encode_cross_crate(name)
     {
         // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates.
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 43c1af642dd..edd0af6e4f5 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -9,7 +9,6 @@ bitflags = "2.4.1"
 either = "1.5.0"
 gsgdt = "0.1.2"
 polonius-engine = "0.13.0"
-rustc-rayon-core = { version = "0.5.0" }
 rustc_abi = { path = "../rustc_abi" }
 rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
@@ -33,6 +32,7 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
+rustc_thread_pool = { path = "../rustc_thread_pool" }
 rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 thin-vec = "0.2.12"
diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs
index e5e1ae508ed..291707878a3 100644
--- a/compiler/rustc_middle/src/hir/map.rs
+++ b/compiler/rustc_middle/src/hir/map.rs
@@ -328,8 +328,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     /// Returns an iterator of the `DefId`s for all body-owners in this
-    /// crate. If you would prefer to iterate over the bodies
-    /// themselves, you can do `self.hir_crate(()).body_ids.iter()`.
+    /// crate.
     #[inline]
     pub fn hir_body_owners(self) -> impl Iterator<Item = LocalDefId> {
         self.hir_crate_items(()).body_owners.iter().copied()
@@ -396,12 +395,11 @@ impl<'tcx> TyCtxt<'tcx> {
     where
         V: Visitor<'tcx>,
     {
-        let krate = self.hir_crate(());
-        for info in krate.owners.iter() {
-            if let MaybeOwner::Owner(info) = info {
-                for attrs in info.attrs.map.values() {
-                    walk_list!(visitor, visit_attribute, *attrs);
-                }
+        let krate = self.hir_crate_items(());
+        for owner in krate.owners() {
+            let attrs = self.hir_attr_map(owner);
+            for attrs in attrs.map.values() {
+                walk_list!(visitor, visit_attribute, *attrs);
             }
         }
         V::Result::output()
@@ -1225,6 +1223,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod
         ..
     } = collector;
     ModuleItems {
+        add_root: false,
         submodules: submodules.into_boxed_slice(),
         free_items: items.into_boxed_slice(),
         trait_items: trait_items.into_boxed_slice(),
@@ -1255,11 +1254,20 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems {
         body_owners,
         opaques,
         nested_bodies,
-        delayed_lint_items,
+        mut delayed_lint_items,
         ..
     } = collector;
 
+    // The crate could have delayed lints too, but would not be picked up by the visitor.
+    // The `delayed_lint_items` list is smart - it only contains items which we know from
+    // earlier passes is guaranteed to contain lints. It's a little harder to determine that
+    // for sure here, so we simply always add the crate to the list. If it has no lints,
+    // we'll discover that later. The cost of this should be low, there's only one crate
+    // after all compared to the many items we have we wouldn't want to iterate over later.
+    delayed_lint_items.push(CRATE_OWNER_ID);
+
     ModuleItems {
+        add_root: true,
         submodules: submodules.into_boxed_slice(),
         free_items: items.into_boxed_slice(),
         trait_items: trait_items.into_boxed_slice(),
diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index 9f79ed4b5a5..d7a8dce0536 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -24,6 +24,9 @@ use crate::ty::{EarlyBinder, ImplSubject, TyCtxt};
 /// bodies. The Ids are in visitor order. This is used to partition a pass between modules.
 #[derive(Debug, HashStable, Encodable, Decodable)]
 pub struct ModuleItems {
+    /// Whether this represents the whole crate, in which case we need to add `CRATE_OWNER_ID` to
+    /// the iterators if we want to account for the crate root.
+    add_root: bool,
     submodules: Box<[OwnerId]>,
     free_items: Box<[ItemId]>,
     trait_items: Box<[TraitItemId]>,
@@ -66,9 +69,10 @@ impl ModuleItems {
     }
 
     pub fn owners(&self) -> impl Iterator<Item = OwnerId> {
-        self.free_items
-            .iter()
-            .map(|id| id.owner_id)
+        self.add_root
+            .then_some(CRATE_OWNER_ID)
+            .into_iter()
+            .chain(self.free_items.iter().map(|id| id.owner_id))
             .chain(self.trait_items.iter().map(|id| id.owner_id))
             .chain(self.impl_items.iter().map(|id| id.owner_id))
             .chain(self.foreign_items.iter().map(|id| id.owner_id))
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index c488ef9b575..ce2cb33c173 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -40,12 +40,12 @@
 #![feature(box_patterns)]
 #![feature(closure_track_caller)]
 #![feature(core_intrinsics)]
-#![feature(coroutines)]
 #![feature(debug_closure_helpers)]
 #![feature(decl_macro)]
 #![feature(discriminant_kind)]
 #![feature(extern_types)]
 #![feature(file_buffered)]
+#![feature(gen_blocks)]
 #![feature(if_let_guard)]
 #![feature(intra_doc_pointers)]
 #![feature(iter_from_coroutine)]
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index f21cf5fa45e..2f16d385efb 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -47,8 +47,7 @@ pub struct CodegenFnAttrs {
     /// be generated against a specific instruction set. Only usable on architectures which allow
     /// switching between multiple instruction sets.
     pub instruction_set: Option<InstructionSetAttr>,
-    /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be
-    /// aligned to.
+    /// The `#[align(...)]` attribute. Determines the alignment of the function body.
     pub alignment: Option<Align>,
     /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around
     /// the function entry.
diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs
index 1d67d0fe3bb..64a1f2aff15 100644
--- a/compiler/rustc_middle/src/middle/exported_symbols.rs
+++ b/compiler/rustc_middle/src/middle/exported_symbols.rs
@@ -22,7 +22,7 @@ impl SymbolExportLevel {
 }
 
 /// Kind of exported symbols.
-#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable)]
+#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable, Hash)]
 pub enum SymbolExportKind {
     Text,
     Data,
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index b9a014d14c0..d0e72a86d8a 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -378,6 +378,14 @@ pub enum ExprKind<'tcx> {
     Loop {
         body: ExprId,
     },
+    /// A `#[loop_match] loop { state = 'blk: { match state { ... } } }` expression.
+    LoopMatch {
+        /// The state variable that is updated, and also the scrutinee of the match.
+        state: ExprId,
+        region_scope: region::Scope,
+        arms: Box<[ArmId]>,
+        match_span: Span,
+    },
     /// Special expression representing the `let` part of an `if let` or similar construct
     /// (including `if let` guards in match arms, and let-chains formed by `&&`).
     ///
@@ -454,6 +462,11 @@ pub enum ExprKind<'tcx> {
     Continue {
         label: region::Scope,
     },
+    /// A `#[const_continue] break` expression.
+    ConstContinue {
+        label: region::Scope,
+        value: ExprId,
+    },
     /// A `return` expression.
     Return {
         value: Option<ExprId>,
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index d8743814d79..c9ef723aea4 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -83,7 +83,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
             visitor.visit_pat(pat);
         }
         Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
-        Match { scrutinee, ref arms, .. } => {
+        LoopMatch { state: scrutinee, ref arms, .. } | Match { scrutinee, ref arms, .. } => {
             visitor.visit_expr(&visitor.thir()[scrutinee]);
             for &arm in &**arms {
                 visitor.visit_arm(&visitor.thir()[arm]);
@@ -108,6 +108,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
             }
         }
         Continue { label: _ } => {}
+        ConstContinue { value, label: _ } => visitor.visit_expr(&visitor.thir()[value]),
         Return { value } => {
             if let Some(value) = value {
                 visitor.visit_expr(&visitor.thir()[value])
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 27079af06fc..d877bd5c626 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -397,6 +397,8 @@ pub enum ObligationCauseCode<'tcx> {
 
     RustCall,
 
+    DynCompatible(Span),
+
     /// Obligations to prove that a `Drop` or negative auto trait impl is not stronger than
     /// the ADT it's being implemented for.
     AlwaysApplicableImpl,
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index df67bb505a6..b8c7d6cf3b1 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -422,53 +422,49 @@ pub fn analyze_coroutine_closure_captures<'a, 'tcx: 'a, T>(
     child_captures: impl IntoIterator<Item = &'a CapturedPlace<'tcx>>,
     mut for_each: impl FnMut((usize, &'a CapturedPlace<'tcx>), (usize, &'a CapturedPlace<'tcx>)) -> T,
 ) -> impl Iterator<Item = T> {
-    std::iter::from_coroutine(
-        #[coroutine]
-        move || {
-            let mut child_captures = child_captures.into_iter().enumerate().peekable();
-
-            // One parent capture may correspond to several child captures if we end up
-            // refining the set of captures via edition-2021 precise captures. We want to
-            // match up any number of child captures with one parent capture, so we keep
-            // peeking off this `Peekable` until the child doesn't match anymore.
-            for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() {
-                // Make sure we use every field at least once, b/c why are we capturing something
-                // if it's not used in the inner coroutine.
-                let mut field_used_at_least_once = false;
-
-                // A parent matches a child if they share the same prefix of projections.
-                // The child may have more, if it is capturing sub-fields out of
-                // something that is captured by-move in the parent closure.
-                while child_captures.peek().is_some_and(|(_, child_capture)| {
-                    child_prefix_matches_parent_projections(parent_capture, child_capture)
-                }) {
-                    let (child_field_idx, child_capture) = child_captures.next().unwrap();
-                    // This analysis only makes sense if the parent capture is a
-                    // prefix of the child capture.
-                    assert!(
-                        child_capture.place.projections.len()
-                            >= parent_capture.place.projections.len(),
-                        "parent capture ({parent_capture:#?}) expected to be prefix of \
+    gen move {
+        let mut child_captures = child_captures.into_iter().enumerate().peekable();
+
+        // One parent capture may correspond to several child captures if we end up
+        // refining the set of captures via edition-2021 precise captures. We want to
+        // match up any number of child captures with one parent capture, so we keep
+        // peeking off this `Peekable` until the child doesn't match anymore.
+        for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() {
+            // Make sure we use every field at least once, b/c why are we capturing something
+            // if it's not used in the inner coroutine.
+            let mut field_used_at_least_once = false;
+
+            // A parent matches a child if they share the same prefix of projections.
+            // The child may have more, if it is capturing sub-fields out of
+            // something that is captured by-move in the parent closure.
+            while child_captures.peek().is_some_and(|(_, child_capture)| {
+                child_prefix_matches_parent_projections(parent_capture, child_capture)
+            }) {
+                let (child_field_idx, child_capture) = child_captures.next().unwrap();
+                // This analysis only makes sense if the parent capture is a
+                // prefix of the child capture.
+                assert!(
+                    child_capture.place.projections.len() >= parent_capture.place.projections.len(),
+                    "parent capture ({parent_capture:#?}) expected to be prefix of \
                     child capture ({child_capture:#?})"
-                    );
-
-                    yield for_each(
-                        (parent_field_idx, parent_capture),
-                        (child_field_idx, child_capture),
-                    );
-
-                    field_used_at_least_once = true;
-                }
+                );
 
-                // Make sure the field was used at least once.
-                assert!(
-                    field_used_at_least_once,
-                    "we captured {parent_capture:#?} but it was not used in the child coroutine?"
+                yield for_each(
+                    (parent_field_idx, parent_capture),
+                    (child_field_idx, child_capture),
                 );
+
+                field_used_at_least_once = true;
             }
-            assert_eq!(child_captures.next(), None, "leftover child captures?");
-        },
-    )
+
+            // Make sure the field was used at least once.
+            assert!(
+                field_used_at_least_once,
+                "we captured {parent_capture:#?} but it was not used in the child coroutine?"
+            );
+        }
+        assert_eq!(child_captures.next(), None, "leftover child captures?");
+    }
 }
 
 fn child_prefix_matches_parent_projections(
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index f1395c242f2..f8b6a30682b 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -432,6 +432,13 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied())
     }
 
+    fn impl_super_outlives(
+        self,
+        impl_def_id: DefId,
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> {
+        self.impl_super_outlives(impl_def_id)
+    }
+
     fn impl_is_const(self, def_id: DefId) -> bool {
         debug_assert_matches!(self.def_kind(def_id), DefKind::Impl { of_trait: true });
         self.is_conditionally_const(def_id)
@@ -1479,6 +1486,12 @@ pub struct GlobalCtxt<'tcx> {
 
     pub canonical_param_env_cache: CanonicalParamEnvCache<'tcx>,
 
+    /// Caches the index of the highest bound var in clauses in a canonical binder.
+    pub highest_var_in_clauses_cache: Lock<FxHashMap<ty::Clauses<'tcx>, usize>>,
+    /// Caches the instantiation of a canonical binder given a set of args.
+    pub clauses_cache:
+        Lock<FxHashMap<(ty::Clauses<'tcx>, &'tcx [ty::GenericArg<'tcx>]), ty::Clauses<'tcx>>>,
+
     /// Data layout specification for the current target.
     pub data_layout: TargetDataLayout,
 
@@ -1727,6 +1740,8 @@ impl<'tcx> TyCtxt<'tcx> {
             new_solver_evaluation_cache: Default::default(),
             new_solver_canonical_param_env_cache: Default::default(),
             canonical_param_env_cache: Default::default(),
+            highest_var_in_clauses_cache: Default::default(),
+            clauses_cache: Default::default(),
             data_layout,
             alloc_map: interpret::AllocMap::new(),
             current_gcx,
@@ -2079,23 +2094,20 @@ impl<'tcx> TyCtxt<'tcx> {
         self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
 
         let definitions = &self.untracked.definitions;
-        std::iter::from_coroutine(
-            #[coroutine]
-            || {
-                let mut i = 0;
-
-                // Recompute the number of definitions each time, because our caller may be creating
-                // new ones.
-                while i < { definitions.read().num_definitions() } {
-                    let local_def_index = rustc_span::def_id::DefIndex::from_usize(i);
-                    yield LocalDefId { local_def_index };
-                    i += 1;
-                }
+        gen {
+            let mut i = 0;
+
+            // Recompute the number of definitions each time, because our caller may be creating
+            // new ones.
+            while i < { definitions.read().num_definitions() } {
+                let local_def_index = rustc_span::def_id::DefIndex::from_usize(i);
+                yield LocalDefId { local_def_index };
+                i += 1;
+            }
 
-                // Freeze definitions once we finish iterating on them, to prevent adding new ones.
-                definitions.freeze();
-            },
-        )
+            // Freeze definitions once we finish iterating on them, to prevent adding new ones.
+            definitions.freeze();
+        }
     }
 
     pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable {
@@ -2113,7 +2125,7 @@ impl<'tcx> TyCtxt<'tcx> {
     ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap {
         // Create a dependency to the crate to be sure we re-execute this when the amount of
         // definitions change.
-        self.ensure_ok().hir_crate(());
+        self.ensure_ok().hir_crate_items(());
         // Freeze definitions once we start iterating on them, to prevent adding new ones
         // while iterating. If some query needs to add definitions, it should be `ensure`d above.
         self.untracked.definitions.freeze().def_path_hash_to_def_index_map()
@@ -2306,7 +2318,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     /// All traits in the crate graph, including those not visible to the user.
-    pub fn all_traits(self) -> impl Iterator<Item = DefId> {
+    pub fn all_traits_including_private(self) -> impl Iterator<Item = DefId> {
         iter::once(LOCAL_CRATE)
             .chain(self.crates(()).iter().copied())
             .flat_map(move |cnum| self.traits(cnum).iter().copied())
@@ -3160,42 +3172,33 @@ impl<'tcx> TyCtxt<'tcx> {
         lint_level(self.sess, lint, level, Some(span.into()), decorate);
     }
 
-    /// Find the crate root and the appropriate span where `use` and outer attributes can be
-    /// inserted at.
-    pub fn crate_level_attribute_injection_span(self, hir_id: HirId) -> Option<Span> {
-        for (_hir_id, node) in self.hir_parent_iter(hir_id) {
-            if let hir::Node::Crate(m) = node {
-                return Some(m.spans.inject_use_span.shrink_to_lo());
-            }
-        }
-        None
+    /// Find the appropriate span where `use` and outer attributes can be inserted at.
+    pub fn crate_level_attribute_injection_span(self) -> Span {
+        let node = self.hir_node(hir::CRATE_HIR_ID);
+        let hir::Node::Crate(m) = node else { bug!() };
+        m.spans.inject_use_span.shrink_to_lo()
     }
 
     pub fn disabled_nightly_features<E: rustc_errors::EmissionGuarantee>(
         self,
         diag: &mut Diag<'_, E>,
-        hir_id: Option<HirId>,
         features: impl IntoIterator<Item = (String, Symbol)>,
     ) {
         if !self.sess.is_nightly_build() {
             return;
         }
 
-        let span = hir_id.and_then(|id| self.crate_level_attribute_injection_span(id));
+        let span = self.crate_level_attribute_injection_span();
         for (desc, feature) in features {
             // FIXME: make this string translatable
             let msg =
                 format!("add `#![feature({feature})]` to the crate attributes to enable{desc}");
-            if let Some(span) = span {
-                diag.span_suggestion_verbose(
-                    span,
-                    msg,
-                    format!("#![feature({feature})]\n"),
-                    Applicability::MaybeIncorrect,
-                );
-            } else {
-                diag.help(msg);
-            }
+            diag.span_suggestion_verbose(
+                span,
+                msg,
+                format!("#![feature({feature})]\n"),
+                Applicability::MaybeIncorrect,
+            );
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs
index 5fc80bc7936..fa9995898ac 100644
--- a/compiler/rustc_middle/src/ty/context/tls.rs
+++ b/compiler/rustc_middle/src/ty/context/tls.rs
@@ -36,7 +36,7 @@ impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> {
 }
 
 // Import the thread-local variable from Rayon, which is preserved for Rayon jobs.
-use rayon_core::tlv::TLV;
+use rustc_thread_pool::tlv::TLV;
 
 #[inline]
 fn erase(context: &ImplicitCtxt<'_, '_>) -> *const () {
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
index d8bab58545f..2a336cc21f4 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
@@ -127,7 +127,9 @@ impl<'tcx> Ty<'tcx> {
                 InhabitedPredicate::True
             }
             Never => InhabitedPredicate::False,
-            Param(_) | Alias(ty::Projection | ty::Free, _) => InhabitedPredicate::GenericType(self),
+            Param(_) | Alias(ty::Inherent | ty::Projection | ty::Free, _) => {
+                InhabitedPredicate::GenericType(self)
+            }
             Alias(ty::Opaque, alias_ty) => {
                 match alias_ty.def_id.as_local() {
                     // Foreign opaque is considered inhabited.
@@ -139,12 +141,6 @@ impl<'tcx> Ty<'tcx> {
                     }
                 }
             }
-            // FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above.
-            // However it's unclear if the args passed to `InhabitedPredicate::instantiate` are of the correct
-            // format, i.e. don't contain parent args. If you hit this case, please verify this beforehand.
-            Alias(ty::Inherent, _) => {
-                bug!("unimplemented: inhabitedness checking for inherent projections")
-            }
             Tuple(tys) if tys.is_empty() => InhabitedPredicate::True,
             // use a query for more complex cases
             Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self),
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 13c281a6182..90b832df281 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1248,11 +1248,12 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi)
         | EfiApi
         | AvrInterrupt
         | AvrNonBlockingInterrupt
+        | CmseNonSecureCall
+        | CmseNonSecureEntry
+        | Custom
         | RiscvInterruptM
         | RiscvInterruptS
-        | CCmseNonSecureCall
-        | CCmseNonSecureEntry
-        | Custom
+        | RustInvalid
         | Unadjusted => false,
         Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
     }
diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml
index 1534865cdd3..c4c2d8a7ac8 100644
--- a/compiler/rustc_mir_build/Cargo.toml
+++ b/compiler/rustc_mir_build/Cargo.toml
@@ -11,6 +11,7 @@ rustc_abi = { path = "../rustc_abi" }
 rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
+rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index fae159103e7..e339520cd86 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -84,6 +84,15 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
 
 mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable
 
+mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
+    .label = this value is too generic
+    .note = the value must be a literal or a monomorphic const
+
+mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value
+
+mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
+    .label = this value must be a literal or a monomorphic const
+
 mir_build_const_defined_here = constant defined here
 
 mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns
@@ -212,6 +221,30 @@ mir_build_literal_in_range_out_of_bounds =
     literal out of range for `{$ty}`
     .label = this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}`
 
+mir_build_loop_match_arm_with_guard =
+    match arms that are part of a `#[loop_match]` cannot have guards
+
+mir_build_loop_match_bad_rhs =
+    this expression must be a single `match` wrapped in a labeled block
+
+mir_build_loop_match_bad_statements =
+    statements are not allowed in this position within a `#[loop_match]`
+
+mir_build_loop_match_invalid_match =
+    invalid match on `#[loop_match]` state
+    .note = a local variable must be the scrutinee within a `#[loop_match]`
+
+mir_build_loop_match_invalid_update =
+    invalid update of the `#[loop_match]` state
+    .label = the assignment must update this variable
+
+mir_build_loop_match_missing_assignment =
+    expected a single assignment expression
+
+mir_build_loop_match_unsupported_type =
+    this `#[loop_match]` state value has type `{$ty}`, which is not supported
+    .note = only integers, floats, bool, char, and enums without fields are supported
+
 mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
     lower range bound must be less than or equal to upper
     .label = lower bound larger than upper bound
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
index f8c64d7d13e..99148504a87 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
@@ -565,12 +565,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             | ExprKind::Match { .. }
             | ExprKind::If { .. }
             | ExprKind::Loop { .. }
+            | ExprKind::LoopMatch { .. }
             | ExprKind::Block { .. }
             | ExprKind::Let { .. }
             | ExprKind::Assign { .. }
             | ExprKind::AssignOp { .. }
             | ExprKind::Break { .. }
             | ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. }
             | ExprKind::Literal { .. }
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
index b23bc089cd4..9e07dd5da7e 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
@@ -538,6 +538,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             | ExprKind::RawBorrow { .. }
             | ExprKind::Adt { .. }
             | ExprKind::Loop { .. }
+            | ExprKind::LoopMatch { .. }
             | ExprKind::LogicalOp { .. }
             | ExprKind::Call { .. }
             | ExprKind::Field { .. }
@@ -548,6 +549,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             | ExprKind::UpvarRef { .. }
             | ExprKind::Break { .. }
             | ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. }
             | ExprKind::InlineAsm { .. }
diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs
index 34524aed406..5e4219dbf5b 100644
--- a/compiler/rustc_mir_build/src/builder/expr/category.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/category.rs
@@ -83,9 +83,11 @@ impl Category {
             | ExprKind::NamedConst { .. } => Some(Category::Constant),
 
             ExprKind::Loop { .. }
+            | ExprKind::LoopMatch { .. }
             | ExprKind::Block { .. }
             | ExprKind::Break { .. }
             | ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. } =>
             // FIXME(#27840) these probably want their own
diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs
index 2074fbce0ae..fe3d072fa88 100644
--- a/compiler/rustc_mir_build/src/builder/expr/into.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/into.rs
@@ -8,15 +8,16 @@ use rustc_hir::lang_items::LangItem;
 use rustc_middle::mir::*;
 use rustc_middle::span_bug;
 use rustc_middle::thir::*;
-use rustc_middle::ty::{CanonicalUserTypeAnnotation, Ty};
+use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
 use rustc_span::DUMMY_SP;
 use rustc_span::source_map::Spanned;
 use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::{debug, instrument};
 
 use crate::builder::expr::category::{Category, RvalueFunc};
-use crate::builder::matches::DeclareLetBindings;
+use crate::builder::matches::{DeclareLetBindings, HasMatchGuard};
 use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
+use crate::errors::{LoopMatchArmWithGuard, LoopMatchUnsupportedType};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Compile `expr`, storing the result into `destination`, which
@@ -244,6 +245,122 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     None
                 })
             }
+            ExprKind::LoopMatch { state, region_scope, match_span, ref arms } => {
+                // Intuitively, this is a combination of a loop containing a labeled block
+                // containing a match.
+                //
+                // The only new bit here is that the lowering of the match is wrapped in a
+                // `in_const_continuable_scope`, which makes the match arms and their target basic
+                // block available to the lowering of `#[const_continue]`.
+
+                fn is_supported_loop_match_type(ty: Ty<'_>) -> bool {
+                    match ty.kind() {
+                        ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => true,
+                        ty::Adt(adt_def, _) => match adt_def.adt_kind() {
+                            ty::AdtKind::Struct | ty::AdtKind::Union => false,
+                            ty::AdtKind::Enum => {
+                                adt_def.variants().iter().all(|v| v.fields.is_empty())
+                            }
+                        },
+                        _ => false,
+                    }
+                }
+
+                let state_ty = this.thir.exprs[state].ty;
+                if !is_supported_loop_match_type(state_ty) {
+                    let span = this.thir.exprs[state].span;
+                    this.tcx.dcx().emit_fatal(LoopMatchUnsupportedType { span, ty: state_ty })
+                }
+
+                let loop_block = this.cfg.start_new_block();
+
+                // Start the loop.
+                this.cfg.goto(block, source_info, loop_block);
+
+                this.in_breakable_scope(Some(loop_block), destination, expr_span, |this| {
+                    // Logic for `loop`.
+                    let mut body_block = this.cfg.start_new_block();
+                    this.cfg.terminate(
+                        loop_block,
+                        source_info,
+                        TerminatorKind::FalseUnwind {
+                            real_target: body_block,
+                            unwind: UnwindAction::Continue,
+                        },
+                    );
+                    this.diverge_from(loop_block);
+
+                    // Logic for `match`.
+                    let scrutinee_place_builder =
+                        unpack!(body_block = this.as_place_builder(body_block, state));
+                    let scrutinee_span = this.thir.exprs[state].span;
+                    let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
+
+                    let mut patterns = Vec::with_capacity(arms.len());
+                    for &arm_id in arms.iter() {
+                        let arm = &this.thir[arm_id];
+
+                        if let Some(guard) = arm.guard {
+                            let span = this.thir.exprs[guard].span;
+                            this.tcx.dcx().emit_fatal(LoopMatchArmWithGuard { span })
+                        }
+
+                        patterns.push((&*arm.pattern, HasMatchGuard::No));
+                    }
+
+                    // The `built_tree` maps match arms to their basic block (where control flow
+                    // jumps to when a value matches the arm). This structure is stored so that a
+                    // `#[const_continue]` can figure out what basic block to jump to.
+                    let built_tree = this.lower_match_tree(
+                        body_block,
+                        scrutinee_span,
+                        &scrutinee_place_builder,
+                        match_start_span,
+                        patterns,
+                        false,
+                    );
+
+                    let state_place = scrutinee_place_builder.to_place(this);
+
+                    // This is logic for the labeled block: a block is a drop scope, hence
+                    // `in_scope`, and a labeled block can be broken out of with a `break 'label`,
+                    // hence the `in_breakable_scope`.
+                    //
+                    // Then `in_const_continuable_scope` stores information for the lowering of
+                    // `#[const_continue]`, and finally the match is lowered in the standard way.
+                    unpack!(
+                        body_block = this.in_scope(
+                            (region_scope, source_info),
+                            LintLevel::Inherited,
+                            move |this| {
+                                this.in_breakable_scope(None, state_place, expr_span, |this| {
+                                    Some(this.in_const_continuable_scope(
+                                        arms.clone(),
+                                        built_tree.clone(),
+                                        state_place,
+                                        expr_span,
+                                        |this| {
+                                            this.lower_match_arms(
+                                                destination,
+                                                scrutinee_place_builder,
+                                                scrutinee_span,
+                                                arms,
+                                                built_tree,
+                                                this.source_info(match_span),
+                                            )
+                                        },
+                                    ))
+                                })
+                            }
+                        )
+                    );
+
+                    this.cfg.goto(body_block, source_info, loop_block);
+
+                    // Loops are only exited by `break` expressions.
+                    None
+                })
+            }
             ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
                 let fun = unpack!(block = this.as_local_operand(block, fun));
                 let args: Box<[_]> = args
@@ -601,6 +718,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
 
             ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Break { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. } => {
diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
index 2dff26f02f3..675beceea14 100644
--- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
@@ -98,6 +98,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             ExprKind::Break { label, value } => {
                 this.break_scope(block, value, BreakableTarget::Break(label), source_info)
             }
+            ExprKind::ConstContinue { label, value } => {
+                this.break_const_continuable_scope(block, value, label, source_info)
+            }
             ExprKind::Return { value } => {
                 this.break_scope(block, value, BreakableTarget::Return, source_info)
             }
diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs
index 977d4f3e931..270a7d4b154 100644
--- a/compiler/rustc_mir_build/src/builder/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs
@@ -18,7 +18,9 @@ use rustc_middle::bug;
 use rustc_middle::middle::region;
 use rustc_middle::mir::{self, *};
 use rustc_middle::thir::{self, *};
-use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
+use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
+use rustc_pattern_analysis::constructor::RangeEnd;
+use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};
 use rustc_span::{BytePos, Pos, Span, Symbol, sym};
 use tracing::{debug, instrument};
 
@@ -426,7 +428,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// (by [Builder::lower_match_tree]).
     ///
     /// `outer_source_info` is the SourceInfo for the whole match.
-    fn lower_match_arms(
+    pub(crate) fn lower_match_arms(
         &mut self,
         destination: Place<'tcx>,
         scrutinee_place_builder: PlaceBuilder<'tcx>,
@@ -1395,7 +1397,7 @@ pub(crate) struct ArmHasGuard(pub(crate) bool);
 /// A sub-branch in the output of match lowering. Match lowering has generated MIR code that will
 /// branch to `success_block` when the matched value matches the corresponding pattern. If there is
 /// a guard, its failure must continue to `otherwise_block`, which will resume testing patterns.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 struct MatchTreeSubBranch<'tcx> {
     span: Span,
     /// The block that is branched to if the corresponding subpattern matches.
@@ -1411,7 +1413,7 @@ struct MatchTreeSubBranch<'tcx> {
 }
 
 /// A branch in the output of match lowering.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 struct MatchTreeBranch<'tcx> {
     sub_branches: Vec<MatchTreeSubBranch<'tcx>>,
 }
@@ -1430,8 +1432,8 @@ struct MatchTreeBranch<'tcx> {
 /// Here the first arm gives the first `MatchTreeBranch`, which has two sub-branches, one for each
 /// alternative of the or-pattern. They are kept separate because each needs to bind `x` to a
 /// different place.
-#[derive(Debug)]
-struct BuiltMatchTree<'tcx> {
+#[derive(Debug, Clone)]
+pub(crate) struct BuiltMatchTree<'tcx> {
     branches: Vec<MatchTreeBranch<'tcx>>,
     otherwise_block: BasicBlock,
     /// If any of the branches had a guard, we collect here the places and locals to fakely borrow
@@ -1489,7 +1491,7 @@ impl<'tcx> MatchTreeBranch<'tcx> {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum HasMatchGuard {
+pub(crate) enum HasMatchGuard {
     Yes,
     No,
 }
@@ -1504,7 +1506,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`)
     /// or not (for `let` and `match`). In the refutable case we return the block to which we branch
     /// on failure.
-    fn lower_match_tree(
+    pub(crate) fn lower_match_tree(
         &mut self,
         block: BasicBlock,
         scrutinee_span: Span,
@@ -1890,7 +1892,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats);
         candidate.or_span = Some(match_pair.pattern_span);
         candidate.subcandidates = pats
-            .into_vec()
             .into_iter()
             .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))
             .collect();
@@ -2864,4 +2865,129 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         true
     }
+
+    /// Attempt to statically pick the `BasicBlock` that a value would resolve to at runtime.
+    pub(crate) fn static_pattern_match(
+        &self,
+        cx: &RustcPatCtxt<'_, 'tcx>,
+        valtree: ValTree<'tcx>,
+        arms: &[ArmId],
+        built_match_tree: &BuiltMatchTree<'tcx>,
+    ) -> Option<BasicBlock> {
+        let it = arms.iter().zip(built_match_tree.branches.iter());
+        for (&arm_id, branch) in it {
+            let pat = cx.lower_pat(&*self.thir.arms[arm_id].pattern);
+
+            // Peel off or-patterns if they exist.
+            if let rustc_pattern_analysis::rustc::Constructor::Or = pat.ctor() {
+                for pat in pat.iter_fields() {
+                    // For top-level or-patterns (the only ones we accept right now), when the
+                    // bindings are the same (e.g. there are none), the sub_branch is stored just
+                    // once.
+                    let sub_branch = branch
+                        .sub_branches
+                        .get(pat.idx)
+                        .or_else(|| branch.sub_branches.last())
+                        .unwrap();
+
+                    match self.static_pattern_match_inner(valtree, &pat.pat) {
+                        true => return Some(sub_branch.success_block),
+                        false => continue,
+                    }
+                }
+            } else if self.static_pattern_match_inner(valtree, &pat) {
+                return Some(branch.sub_branches[0].success_block);
+            }
+        }
+
+        None
+    }
+
+    /// Helper for [`Self::static_pattern_match`], checking whether the value represented by the
+    /// `ValTree` matches the given pattern. This function does not recurse, meaning that it does
+    /// not handle or-patterns, or patterns for types with fields.
+    fn static_pattern_match_inner(
+        &self,
+        valtree: ty::ValTree<'tcx>,
+        pat: &DeconstructedPat<'_, 'tcx>,
+    ) -> bool {
+        use rustc_pattern_analysis::constructor::{IntRange, MaybeInfiniteInt};
+        use rustc_pattern_analysis::rustc::Constructor;
+
+        match pat.ctor() {
+            Constructor::Variant(variant_index) => {
+                let ValTreeKind::Branch(box [actual_variant_idx]) = *valtree else {
+                    bug!("malformed valtree for an enum")
+                };
+
+                let ValTreeKind::Leaf(actual_variant_idx) = ***actual_variant_idx else {
+                    bug!("malformed valtree for an enum")
+                };
+
+                *variant_index == VariantIdx::from_u32(actual_variant_idx.to_u32())
+            }
+            Constructor::IntRange(int_range) => {
+                let size = pat.ty().primitive_size(self.tcx);
+                let actual_int = valtree.unwrap_leaf().to_bits(size);
+                let actual_int = if pat.ty().is_signed() {
+                    MaybeInfiniteInt::new_finite_int(actual_int, size.bits())
+                } else {
+                    MaybeInfiniteInt::new_finite_uint(actual_int)
+                };
+                IntRange::from_singleton(actual_int).is_subrange(int_range)
+            }
+            Constructor::Bool(pattern_value) => match valtree.unwrap_leaf().try_to_bool() {
+                Ok(actual_value) => *pattern_value == actual_value,
+                Err(()) => bug!("bool value with invalid bits"),
+            },
+            Constructor::F16Range(l, h, end) => {
+                let actual = valtree.unwrap_leaf().to_f16();
+                match end {
+                    RangeEnd::Included => (*l..=*h).contains(&actual),
+                    RangeEnd::Excluded => (*l..*h).contains(&actual),
+                }
+            }
+            Constructor::F32Range(l, h, end) => {
+                let actual = valtree.unwrap_leaf().to_f32();
+                match end {
+                    RangeEnd::Included => (*l..=*h).contains(&actual),
+                    RangeEnd::Excluded => (*l..*h).contains(&actual),
+                }
+            }
+            Constructor::F64Range(l, h, end) => {
+                let actual = valtree.unwrap_leaf().to_f64();
+                match end {
+                    RangeEnd::Included => (*l..=*h).contains(&actual),
+                    RangeEnd::Excluded => (*l..*h).contains(&actual),
+                }
+            }
+            Constructor::F128Range(l, h, end) => {
+                let actual = valtree.unwrap_leaf().to_f128();
+                match end {
+                    RangeEnd::Included => (*l..=*h).contains(&actual),
+                    RangeEnd::Excluded => (*l..*h).contains(&actual),
+                }
+            }
+            Constructor::Wildcard => true,
+
+            // These we may eventually support:
+            Constructor::Struct
+            | Constructor::Ref
+            | Constructor::DerefPattern(_)
+            | Constructor::Slice(_)
+            | Constructor::UnionField
+            | Constructor::Or
+            | Constructor::Str(_) => bug!("unsupported pattern constructor {:?}", pat.ctor()),
+
+            // These should never occur here:
+            Constructor::Opaque(_)
+            | Constructor::Never
+            | Constructor::NonExhaustive
+            | Constructor::Hidden
+            | Constructor::Missing
+            | Constructor::PrivateUninhabited => {
+                bug!("unsupported pattern constructor {:?}", pat.ctor())
+            }
+        }
+    }
 }
diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs
index 67988f1fcbc..1d15e7e126f 100644
--- a/compiler/rustc_mir_build/src/builder/scope.rs
+++ b/compiler/rustc_mir_build/src/builder/scope.rs
@@ -83,20 +83,24 @@ that contains only loops and breakable blocks. It tracks where a `break`,
 
 use std::mem;
 
+use interpret::ErrorHandled;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::HirId;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::middle::region;
-use rustc_middle::mir::*;
-use rustc_middle::thir::{ExprId, LintLevel};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::mir::{self, *};
+use rustc_middle::thir::{AdtExpr, AdtExprBase, ArmId, ExprId, ExprKind, LintLevel};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree};
 use rustc_middle::{bug, span_bug};
+use rustc_pattern_analysis::rustc::RustcPatCtxt;
 use rustc_session::lint::Level;
 use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument};
 
+use super::matches::BuiltMatchTree;
 use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
+use crate::errors::{ConstContinueBadConst, ConstContinueUnknownJumpTarget};
 
 #[derive(Debug)]
 pub(crate) struct Scopes<'tcx> {
@@ -105,6 +109,8 @@ pub(crate) struct Scopes<'tcx> {
     /// The current set of breakable scopes. See module comment for more details.
     breakable_scopes: Vec<BreakableScope<'tcx>>,
 
+    const_continuable_scopes: Vec<ConstContinuableScope<'tcx>>,
+
     /// The scope of the innermost if-then currently being lowered.
     if_then_scope: Option<IfThenScope>,
 
@@ -175,6 +181,20 @@ struct BreakableScope<'tcx> {
 }
 
 #[derive(Debug)]
+struct ConstContinuableScope<'tcx> {
+    /// The scope for the `#[loop_match]` which its `#[const_continue]`s will jump to.
+    region_scope: region::Scope,
+    /// The place of the state of a `#[loop_match]`, which a `#[const_continue]` must update.
+    state_place: Place<'tcx>,
+
+    arms: Box<[ArmId]>,
+    built_match_tree: BuiltMatchTree<'tcx>,
+
+    /// Drops that happen on a `#[const_continue]`
+    const_continue_drops: DropTree,
+}
+
+#[derive(Debug)]
 struct IfThenScope {
     /// The if-then scope or arm scope
     region_scope: region::Scope,
@@ -461,6 +481,7 @@ impl<'tcx> Scopes<'tcx> {
         Self {
             scopes: Vec::new(),
             breakable_scopes: Vec::new(),
+            const_continuable_scopes: Vec::new(),
             if_then_scope: None,
             unwind_drops: DropTree::new(),
             coroutine_drops: DropTree::new(),
@@ -552,6 +573,59 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
+    /// Start a const-continuable scope, which tracks where `#[const_continue] break` should
+    /// branch to.
+    pub(crate) fn in_const_continuable_scope<F>(
+        &mut self,
+        arms: Box<[ArmId]>,
+        built_match_tree: BuiltMatchTree<'tcx>,
+        state_place: Place<'tcx>,
+        span: Span,
+        f: F,
+    ) -> BlockAnd<()>
+    where
+        F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<()>,
+    {
+        let region_scope = self.scopes.topmost();
+        let scope = ConstContinuableScope {
+            region_scope,
+            state_place,
+            const_continue_drops: DropTree::new(),
+            arms,
+            built_match_tree,
+        };
+        self.scopes.const_continuable_scopes.push(scope);
+        let normal_exit_block = f(self);
+        let const_continue_scope = self.scopes.const_continuable_scopes.pop().unwrap();
+        assert!(const_continue_scope.region_scope == region_scope);
+
+        let break_block = self.build_exit_tree(
+            const_continue_scope.const_continue_drops,
+            region_scope,
+            span,
+            None,
+        );
+
+        match (normal_exit_block, break_block) {
+            (block, None) => block,
+            (normal_block, Some(exit_block)) => {
+                let target = self.cfg.start_new_block();
+                let source_info = self.source_info(span);
+                self.cfg.terminate(
+                    normal_block.into_block(),
+                    source_info,
+                    TerminatorKind::Goto { target },
+                );
+                self.cfg.terminate(
+                    exit_block.into_block(),
+                    source_info,
+                    TerminatorKind::Goto { target },
+                );
+                target.unit()
+            }
+        }
+    }
+
     /// Start an if-then scope which tracks drop for `if` expressions and `if`
     /// guards.
     ///
@@ -742,6 +816,190 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         self.cfg.start_new_block().unit()
     }
 
+    /// Based on `FunctionCx::eval_unevaluated_mir_constant_to_valtree`.
+    fn eval_unevaluated_mir_constant_to_valtree(
+        &self,
+        constant: ConstOperand<'tcx>,
+    ) -> Result<(ty::ValTree<'tcx>, Ty<'tcx>), interpret::ErrorHandled> {
+        assert!(!constant.const_.ty().has_param());
+        let (uv, ty) = match constant.const_ {
+            mir::Const::Unevaluated(uv, ty) => (uv.shrink(), ty),
+            mir::Const::Ty(_, c) => match c.kind() {
+                // A constant that came from a const generic but was then used as an argument to
+                // old-style simd_shuffle (passing as argument instead of as a generic param).
+                ty::ConstKind::Value(cv) => return Ok((cv.valtree, cv.ty)),
+                other => span_bug!(constant.span, "{other:#?}"),
+            },
+            mir::Const::Val(mir::ConstValue::Scalar(mir::interpret::Scalar::Int(val)), ty) => {
+                return Ok((ValTree::from_scalar_int(self.tcx, val), ty));
+            }
+            // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
+            // a constant and write that value back into `Operand`s. This could happen, but is
+            // unlikely. Also: all users of `simd_shuffle` are on unstable and already need to take
+            // a lot of care around intrinsics. For an issue to happen here, it would require a
+            // macro expanding to a `simd_shuffle` call without wrapping the constant argument in a
+            // `const {}` block, but the user pass through arbitrary expressions.
+
+            // FIXME(oli-obk): Replace the magic const generic argument of `simd_shuffle` with a
+            // real const generic, and get rid of this entire function.
+            other => span_bug!(constant.span, "{other:#?}"),
+        };
+
+        match self.tcx.const_eval_resolve_for_typeck(self.typing_env(), uv, constant.span) {
+            Ok(Ok(valtree)) => Ok((valtree, ty)),
+            Ok(Err(ty)) => span_bug!(constant.span, "could not convert {ty:?} to a valtree"),
+            Err(e) => Err(e),
+        }
+    }
+
+    /// Sets up the drops for jumping from `block` to `scope`.
+    pub(crate) fn break_const_continuable_scope(
+        &mut self,
+        mut block: BasicBlock,
+        value: ExprId,
+        scope: region::Scope,
+        source_info: SourceInfo,
+    ) -> BlockAnd<()> {
+        let span = source_info.span;
+
+        // A break can only break out of a scope, so the value should be a scope.
+        let rustc_middle::thir::ExprKind::Scope { value, .. } = self.thir[value].kind else {
+            span_bug!(span, "break value must be a scope")
+        };
+
+        let constant = match &self.thir[value].kind {
+            ExprKind::Adt(box AdtExpr { variant_index, fields, base, .. }) => {
+                assert!(matches!(base, AdtExprBase::None));
+                assert!(fields.is_empty());
+                ConstOperand {
+                    span: self.thir[value].span,
+                    user_ty: None,
+                    const_: Const::Ty(
+                        self.thir[value].ty,
+                        ty::Const::new_value(
+                            self.tcx,
+                            ValTree::from_branches(
+                                self.tcx,
+                                [ValTree::from_scalar_int(self.tcx, variant_index.as_u32().into())],
+                            ),
+                            self.thir[value].ty,
+                        ),
+                    ),
+                }
+            }
+            _ => self.as_constant(&self.thir[value]),
+        };
+
+        let break_index = self
+            .scopes
+            .const_continuable_scopes
+            .iter()
+            .rposition(|const_continuable_scope| const_continuable_scope.region_scope == scope)
+            .unwrap_or_else(|| span_bug!(span, "no enclosing const-continuable scope found"));
+
+        let scope = &self.scopes.const_continuable_scopes[break_index];
+
+        let state_decl = &self.local_decls[scope.state_place.as_local().unwrap()];
+        let state_ty = state_decl.ty;
+        let (discriminant_ty, rvalue) = match state_ty.kind() {
+            ty::Adt(adt_def, _) if adt_def.is_enum() => {
+                (state_ty.discriminant_ty(self.tcx), Rvalue::Discriminant(scope.state_place))
+            }
+            ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => {
+                (state_ty, Rvalue::Use(Operand::Copy(scope.state_place)))
+            }
+            _ => span_bug!(state_decl.source_info.span, "unsupported #[loop_match] state"),
+        };
+
+        // The `PatCtxt` is normally used in pattern exhaustiveness checking, but reused
+        // here because it performs normalization and const evaluation.
+        let dropless_arena = rustc_arena::DroplessArena::default();
+        let typeck_results = self.tcx.typeck(self.def_id);
+        let cx = RustcPatCtxt {
+            tcx: self.tcx,
+            typeck_results,
+            module: self.tcx.parent_module(self.hir_id).to_def_id(),
+            // FIXME(#132279): We're in a body, should handle opaques.
+            typing_env: rustc_middle::ty::TypingEnv::non_body_analysis(self.tcx, self.def_id),
+            dropless_arena: &dropless_arena,
+            match_lint_level: self.hir_id,
+            whole_match_span: Some(rustc_span::Span::default()),
+            scrut_span: rustc_span::Span::default(),
+            refutable: true,
+            known_valid_scrutinee: true,
+        };
+
+        let valtree = match self.eval_unevaluated_mir_constant_to_valtree(constant) {
+            Ok((valtree, ty)) => {
+                // Defensively check that the type is monomorphic.
+                assert!(!ty.has_param());
+
+                valtree
+            }
+            Err(ErrorHandled::Reported(..)) => return self.cfg.start_new_block().unit(),
+            Err(ErrorHandled::TooGeneric(_)) => {
+                self.tcx.dcx().emit_fatal(ConstContinueBadConst { span: constant.span });
+            }
+        };
+
+        let Some(real_target) =
+            self.static_pattern_match(&cx, valtree, &*scope.arms, &scope.built_match_tree)
+        else {
+            self.tcx.dcx().emit_fatal(ConstContinueUnknownJumpTarget { span })
+        };
+
+        self.block_context.push(BlockFrame::SubExpr);
+        let state_place = scope.state_place;
+        block = self.expr_into_dest(state_place, block, value).into_block();
+        self.block_context.pop();
+
+        let discr = self.temp(discriminant_ty, source_info.span);
+        let scope_index = self
+            .scopes
+            .scope_index(self.scopes.const_continuable_scopes[break_index].region_scope, span);
+        let scope = &mut self.scopes.const_continuable_scopes[break_index];
+        self.cfg.push_assign(block, source_info, discr, rvalue);
+        let drop_and_continue_block = self.cfg.start_new_block();
+        let imaginary_target = self.cfg.start_new_block();
+        self.cfg.terminate(
+            block,
+            source_info,
+            TerminatorKind::FalseEdge { real_target: drop_and_continue_block, imaginary_target },
+        );
+
+        let drops = &mut scope.const_continue_drops;
+
+        let drop_idx = self.scopes.scopes[scope_index + 1..]
+            .iter()
+            .flat_map(|scope| &scope.drops)
+            .fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx));
+
+        drops.add_entry_point(imaginary_target, drop_idx);
+
+        self.cfg.terminate(imaginary_target, source_info, TerminatorKind::UnwindResume);
+
+        let region_scope = scope.region_scope;
+        let scope_index = self.scopes.scope_index(region_scope, span);
+        let mut drops = DropTree::new();
+
+        let drop_idx = self.scopes.scopes[scope_index + 1..]
+            .iter()
+            .flat_map(|scope| &scope.drops)
+            .fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx));
+
+        drops.add_entry_point(drop_and_continue_block, drop_idx);
+
+        // `build_drop_trees` doesn't have access to our source_info, so we
+        // create a dummy terminator now. `TerminatorKind::UnwindResume` is used
+        // because MIR type checking will panic if it hasn't been overwritten.
+        // (See `<ExitScopes as DropTreeBuilder>::link_entry_point`.)
+        self.cfg.terminate(drop_and_continue_block, source_info, TerminatorKind::UnwindResume);
+
+        self.build_exit_tree(drops, region_scope, span, Some(real_target));
+
+        return self.cfg.start_new_block().unit();
+    }
+
     /// Sets up the drops for breaking from `block` due to an `if` condition
     /// that turned out to be false.
     ///
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index d5061b71699..0b6b36640e9 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -465,10 +465,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
             | ExprKind::Break { .. }
             | ExprKind::Closure { .. }
             | ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. }
             | ExprKind::Yield { .. }
             | ExprKind::Loop { .. }
+            | ExprKind::LoopMatch { .. }
             | ExprKind::Let { .. }
             | ExprKind::Match { .. }
             | ExprKind::Box { .. }
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index ae09db50235..20e836f6bf2 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -994,14 +994,15 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> {
     pub(crate) uncovered: Uncovered,
     #[subdiagnostic]
     pub(crate) inform: Option<Inform>,
-    #[label(mir_build_confused)]
-    pub(crate) interpreted_as_const: Option<Span>,
     #[subdiagnostic]
-    pub(crate) interpreted_as_const_sugg: Option<InterpretedAsConst>,
+    pub(crate) interpreted_as_const: Option<InterpretedAsConst>,
+    #[subdiagnostic]
+    pub(crate) interpreted_as_const_sugg: Option<InterpretedAsConstSugg>,
     #[subdiagnostic]
     pub(crate) adt_defined_here: Option<AdtDefinedHere<'tcx>>,
     #[note(mir_build_privately_uninhabited)]
     pub(crate) witness_1_is_privately_uninhabited: bool,
+    pub(crate) witness_1: String,
     #[note(mir_build_pattern_ty)]
     pub(crate) _p: (),
     pub(crate) pattern_ty: Ty<'tcx>,
@@ -1016,6 +1017,14 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> {
 #[note(mir_build_more_information)]
 pub(crate) struct Inform;
 
+#[derive(Subdiagnostic)]
+#[label(mir_build_confused)]
+pub(crate) struct InterpretedAsConst {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) variable: String,
+}
+
 pub(crate) struct AdtDefinedHere<'tcx> {
     pub(crate) adt_def_span: Span,
     pub(crate) ty: Ty<'tcx>,
@@ -1046,7 +1055,7 @@ impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> {
     applicability = "maybe-incorrect",
     style = "verbose"
 )]
-pub(crate) struct InterpretedAsConst {
+pub(crate) struct InterpretedAsConstSugg {
     #[primary_span]
     pub(crate) span: Span,
     pub(crate) variable: String,
@@ -1149,3 +1158,80 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
         }
     }
 }
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_invalid_update)]
+pub(crate) struct LoopMatchInvalidUpdate {
+    #[primary_span]
+    pub lhs: Span,
+    #[label]
+    pub scrutinee: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_invalid_match)]
+#[note]
+pub(crate) struct LoopMatchInvalidMatch {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_unsupported_type)]
+#[note]
+pub(crate) struct LoopMatchUnsupportedType<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_bad_statements)]
+pub(crate) struct LoopMatchBadStatements {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_bad_rhs)]
+pub(crate) struct LoopMatchBadRhs {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_missing_assignment)]
+pub(crate) struct LoopMatchMissingAssignment {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_arm_with_guard)]
+pub(crate) struct LoopMatchArmWithGuard {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_const_continue_bad_const)]
+pub(crate) struct ConstContinueBadConst {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_const_continue_missing_value)]
+pub(crate) struct ConstContinueMissingValue {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_const_continue_unknown_jump_target)]
+#[note]
+pub(crate) struct ConstContinueUnknownJumpTarget {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 3baeccf6409..5197e93fda7 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -1,6 +1,7 @@
 use itertools::Itertools;
 use rustc_abi::{FIRST_VARIANT, FieldIdx};
 use rustc_ast::UnsafeBinderCastKind;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@@ -21,6 +22,7 @@ use rustc_middle::{bug, span_bug};
 use rustc_span::{Span, sym};
 use tracing::{debug, info, instrument, trace};
 
+use crate::errors::*;
 use crate::thir::cx::ThirBuildCx;
 
 impl<'tcx> ThirBuildCx<'tcx> {
@@ -479,6 +481,55 @@ impl<'tcx> ThirBuildCx<'tcx> {
                 ExprKind::RawBorrow { mutability, arg: self.mirror_expr(arg) }
             }
 
+            // Make `&pin mut $expr` and `&pin const $expr` into
+            // `Pin { __pointer: &mut { $expr } }` and `Pin { __pointer: &$expr }`.
+            hir::ExprKind::AddrOf(hir::BorrowKind::Pin, mutbl, arg_expr) => match expr_ty.kind() {
+                &ty::Adt(adt_def, args) if tcx.is_lang_item(adt_def.did(), hir::LangItem::Pin) => {
+                    let ty = args.type_at(0);
+                    let arg_ty = self.typeck_results.expr_ty(arg_expr);
+                    let mut arg = self.mirror_expr(arg_expr);
+                    // For `&pin mut $place` where `$place` is not `Unpin`, move the place
+                    // `$place` to ensure it will not be used afterwards.
+                    if mutbl.is_mut() && !arg_ty.is_unpin(self.tcx, self.typing_env) {
+                        let block = self.thir.blocks.push(Block {
+                            targeted_by_break: false,
+                            region_scope: region::Scope {
+                                local_id: arg_expr.hir_id.local_id,
+                                data: region::ScopeData::Node,
+                            },
+                            span: arg_expr.span,
+                            stmts: Box::new([]),
+                            expr: Some(arg),
+                            safety_mode: BlockSafety::Safe,
+                        });
+                        let (temp_lifetime, backwards_incompatible) = self
+                            .rvalue_scopes
+                            .temporary_scope(self.region_scope_tree, arg_expr.hir_id.local_id);
+                        arg = self.thir.exprs.push(Expr {
+                            temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
+                            ty: arg_ty,
+                            span: arg_expr.span,
+                            kind: ExprKind::Block { block },
+                        });
+                    }
+                    let expr = self.thir.exprs.push(Expr {
+                        temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
+                        ty,
+                        span: expr.span,
+                        kind: ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg },
+                    });
+                    ExprKind::Adt(Box::new(AdtExpr {
+                        adt_def,
+                        variant_index: FIRST_VARIANT,
+                        args,
+                        fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]),
+                        user_ty: None,
+                        base: AdtExprBase::None,
+                    }))
+                }
+                _ => span_bug!(expr.span, "unexpected type for pinned borrow: {:?}", expr_ty),
+            },
+
             hir::ExprKind::Block(blk, _) => ExprKind::Block { block: self.mirror_block(blk) },
 
             hir::ExprKind::Assign(lhs, rhs, _) => {
@@ -796,16 +847,38 @@ impl<'tcx> ThirBuildCx<'tcx> {
             }
             hir::ExprKind::Ret(v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) },
             hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) },
-            hir::ExprKind::Break(dest, ref value) => match dest.target_id {
-                Ok(target_id) => ExprKind::Break {
-                    label: region::Scope {
-                        local_id: target_id.local_id,
-                        data: region::ScopeData::Node,
-                    },
-                    value: value.map(|value| self.mirror_expr(value)),
-                },
-                Err(err) => bug!("invalid loop id for break: {}", err),
-            },
+            hir::ExprKind::Break(dest, ref value) => {
+                if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::ConstContinue(_)) {
+                    match dest.target_id {
+                        Ok(target_id) => {
+                            let Some(value) = value else {
+                                let span = expr.span;
+                                self.tcx.dcx().emit_fatal(ConstContinueMissingValue { span })
+                            };
+
+                            ExprKind::ConstContinue {
+                                label: region::Scope {
+                                    local_id: target_id.local_id,
+                                    data: region::ScopeData::Node,
+                                },
+                                value: self.mirror_expr(value),
+                            }
+                        }
+                        Err(err) => bug!("invalid loop id for break: {}", err),
+                    }
+                } else {
+                    match dest.target_id {
+                        Ok(target_id) => ExprKind::Break {
+                            label: region::Scope {
+                                local_id: target_id.local_id,
+                                data: region::ScopeData::Node,
+                            },
+                            value: value.map(|value| self.mirror_expr(value)),
+                        },
+                        Err(err) => bug!("invalid loop id for break: {}", err),
+                    }
+                }
+            }
             hir::ExprKind::Continue(dest) => match dest.target_id {
                 Ok(loop_id) => ExprKind::Continue {
                     label: region::Scope {
@@ -840,18 +913,93 @@ impl<'tcx> ThirBuildCx<'tcx> {
                 match_source,
             },
             hir::ExprKind::Loop(body, ..) => {
-                let block_ty = self.typeck_results.node_type(body.hir_id);
-                let (temp_lifetime, backwards_incompatible) = self
-                    .rvalue_scopes
-                    .temporary_scope(self.region_scope_tree, body.hir_id.local_id);
-                let block = self.mirror_block(body);
-                let body = self.thir.exprs.push(Expr {
-                    ty: block_ty,
-                    temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
-                    span: self.thir[block].span,
-                    kind: ExprKind::Block { block },
-                });
-                ExprKind::Loop { body }
+                if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::LoopMatch(_)) {
+                    let dcx = self.tcx.dcx();
+
+                    // Accept either `state = expr` or `state = expr;`.
+                    let loop_body_expr = match body.stmts {
+                        [] => match body.expr {
+                            Some(expr) => expr,
+                            None => dcx.emit_fatal(LoopMatchMissingAssignment { span: body.span }),
+                        },
+                        [single] if body.expr.is_none() => match single.kind {
+                            hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => expr,
+                            _ => dcx.emit_fatal(LoopMatchMissingAssignment { span: body.span }),
+                        },
+                        [first @ last] | [first, .., last] => dcx
+                            .emit_fatal(LoopMatchBadStatements { span: first.span.to(last.span) }),
+                    };
+
+                    let hir::ExprKind::Assign(state, rhs_expr, _) = loop_body_expr.kind else {
+                        dcx.emit_fatal(LoopMatchMissingAssignment { span: loop_body_expr.span })
+                    };
+
+                    let hir::ExprKind::Block(block_body, _) = rhs_expr.kind else {
+                        dcx.emit_fatal(LoopMatchBadRhs { span: rhs_expr.span })
+                    };
+
+                    // The labeled block should contain one match expression, but defining items is
+                    // allowed.
+                    for stmt in block_body.stmts {
+                        if !matches!(stmt.kind, rustc_hir::StmtKind::Item(_)) {
+                            dcx.emit_fatal(LoopMatchBadStatements { span: stmt.span })
+                        }
+                    }
+
+                    let Some(block_body_expr) = block_body.expr else {
+                        dcx.emit_fatal(LoopMatchBadRhs { span: block_body.span })
+                    };
+
+                    let hir::ExprKind::Match(scrutinee, arms, _match_source) = block_body_expr.kind
+                    else {
+                        dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span })
+                    };
+
+                    fn local(expr: &rustc_hir::Expr<'_>) -> Option<hir::HirId> {
+                        if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind {
+                            if let Res::Local(hir_id) = path.res {
+                                return Some(hir_id);
+                            }
+                        }
+
+                        None
+                    }
+
+                    let Some(scrutinee_hir_id) = local(scrutinee) else {
+                        dcx.emit_fatal(LoopMatchInvalidMatch { span: scrutinee.span })
+                    };
+
+                    if local(state) != Some(scrutinee_hir_id) {
+                        dcx.emit_fatal(LoopMatchInvalidUpdate {
+                            scrutinee: scrutinee.span,
+                            lhs: state.span,
+                        })
+                    }
+
+                    ExprKind::LoopMatch {
+                        state: self.mirror_expr(state),
+                        region_scope: region::Scope {
+                            local_id: block_body.hir_id.local_id,
+                            data: region::ScopeData::Node,
+                        },
+
+                        arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
+                        match_span: block_body_expr.span,
+                    }
+                } else {
+                    let block_ty = self.typeck_results.node_type(body.hir_id);
+                    let (temp_lifetime, backwards_incompatible) = self
+                        .rvalue_scopes
+                        .temporary_scope(self.region_scope_tree, body.hir_id.local_id);
+                    let block = self.mirror_block(body);
+                    let body = self.thir.exprs.push(Expr {
+                        ty: block_ty,
+                        temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
+                        span: self.thir[block].span,
+                        kind: ExprKind::Block { block },
+                    });
+                    ExprKind::Loop { body }
+                }
             }
             hir::ExprKind::Field(source, ..) => ExprKind::Field {
                 lhs: self.mirror_expr(source),
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 245bd866030..41fbabc2539 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -331,7 +331,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
             | WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]),
 
             // These diverge.
-            Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,
+            Become { .. }
+            | Break { .. }
+            | Continue { .. }
+            | ConstContinue { .. }
+            | Return { .. } => true,
 
             // These are statements that evaluate to `()`.
             Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
@@ -353,6 +357,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
             | Literal { .. }
             | LogicalOp { .. }
             | Loop { .. }
+            | LoopMatch { .. }
             | Match { .. }
             | NamedConst { .. }
             | NonHirLiteral { .. }
@@ -685,8 +690,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
             let span = self.tcx.def_span(def_id);
             let variable = self.tcx.item_name(def_id).to_string();
             // When we encounter a constant as the binding name, point at the `const` definition.
-            interpreted_as_const = Some(span);
-            interpreted_as_const_sugg = Some(InterpretedAsConst { span: pat.span, variable });
+            interpreted_as_const = Some(InterpretedAsConst { span, variable: variable.clone() });
+            interpreted_as_const_sugg = Some(InterpretedAsConstSugg { span: pat.span, variable });
         } else if let PatKind::Constant { .. } = unpeeled_pat.kind
             && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
         {
@@ -738,6 +743,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
             false
         };
 
+        let witness_1 = cx.print_witness_pat(witnesses.get(0).unwrap());
+
         self.error = Err(self.tcx.dcx().emit_err(PatternNotCovered {
             span: pat.span,
             origin,
@@ -746,6 +753,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
             interpreted_as_const,
             interpreted_as_const_sugg,
             witness_1_is_privately_uninhabited,
+            witness_1,
             _p: (),
             pattern_ty,
             let_suggestion,
diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs
index db9547a481f..1507b6b8c06 100644
--- a/compiler/rustc_mir_build/src/thir/print.rs
+++ b/compiler/rustc_mir_build/src/thir/print.rs
@@ -318,6 +318,20 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
                 self.print_expr(*body, depth_lvl + 2);
                 print_indented!(self, ")", depth_lvl);
             }
+            LoopMatch { state, region_scope, match_span, arms } => {
+                print_indented!(self, "LoopMatch {", depth_lvl);
+                print_indented!(self, "state:", depth_lvl + 1);
+                self.print_expr(*state, depth_lvl + 2);
+                print_indented!(self, format!("region_scope: {:?}", region_scope), depth_lvl + 1);
+                print_indented!(self, format!("match_span: {:?}", match_span), depth_lvl + 1);
+
+                print_indented!(self, "arms: [", depth_lvl + 1);
+                for arm_id in arms.iter() {
+                    self.print_arm(*arm_id, depth_lvl + 2);
+                }
+                print_indented!(self, "]", depth_lvl + 1);
+                print_indented!(self, "}", depth_lvl);
+            }
             Let { expr, pat } => {
                 print_indented!(self, "Let {", depth_lvl);
                 print_indented!(self, "expr:", depth_lvl + 1);
@@ -415,6 +429,13 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
                 print_indented!(self, format!("label: {:?}", label), depth_lvl + 1);
                 print_indented!(self, "}", depth_lvl);
             }
+            ConstContinue { label, value } => {
+                print_indented!(self, "ConstContinue (", depth_lvl);
+                print_indented!(self, format!("label: {:?}", label), depth_lvl + 1);
+                print_indented!(self, "value:", depth_lvl + 1);
+                self.print_expr(*value, depth_lvl + 2);
+                print_indented!(self, ")", depth_lvl);
+            }
             Return { value } => {
                 print_indented!(self, "Return {", depth_lvl);
                 print_indented!(self, "value:", depth_lvl + 1);
diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs
index 27af5818982..cddeefca681 100644
--- a/compiler/rustc_mir_transform/src/copy_prop.rs
+++ b/compiler/rustc_mir_transform/src/copy_prop.rs
@@ -30,31 +30,29 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp {
 
         let typing_env = body.typing_env(tcx);
         let ssa = SsaLocals::new(tcx, body, typing_env);
+        debug!(borrowed_locals = ?ssa.borrowed_locals());
+        debug!(copy_classes = ?ssa.copy_classes());
 
-        let fully_moved = fully_moved_locals(&ssa, body);
-        debug!(?fully_moved);
-
-        let mut storage_to_remove = DenseBitSet::new_empty(fully_moved.domain_size());
+        let mut any_replacement = false;
+        let mut storage_to_remove = DenseBitSet::new_empty(body.local_decls.len());
         for (local, &head) in ssa.copy_classes().iter_enumerated() {
             if local != head {
+                any_replacement = true;
                 storage_to_remove.insert(head);
             }
         }
 
-        let any_replacement = ssa.copy_classes().iter_enumerated().any(|(l, &h)| l != h);
-
-        Replacer {
-            tcx,
-            copy_classes: ssa.copy_classes(),
-            fully_moved,
-            borrowed_locals: ssa.borrowed_locals(),
-            storage_to_remove,
+        if !any_replacement {
+            return;
         }
-        .visit_body_preserves_cfg(body);
 
-        if any_replacement {
-            crate::simplify::remove_unused_definitions(body);
-        }
+        let fully_moved = fully_moved_locals(&ssa, body);
+        debug!(?fully_moved);
+
+        Replacer { tcx, copy_classes: ssa.copy_classes(), fully_moved, storage_to_remove }
+            .visit_body_preserves_cfg(body);
+
+        crate::simplify::remove_unused_definitions(body);
     }
 
     fn is_required(&self) -> bool {
@@ -102,7 +100,6 @@ struct Replacer<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     fully_moved: DenseBitSet<Local>,
     storage_to_remove: DenseBitSet<Local>,
-    borrowed_locals: &'a DenseBitSet<Local>,
     copy_classes: &'a IndexSlice<Local, Local>,
 }
 
@@ -111,34 +108,18 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
         self.tcx
     }
 
+    #[tracing::instrument(level = "trace", skip(self))]
     fn visit_local(&mut self, local: &mut Local, ctxt: PlaceContext, _: Location) {
         let new_local = self.copy_classes[*local];
-        // We must not unify two locals that are borrowed. But this is fine if one is borrowed and
-        // the other is not. We chose to check the original local, and not the target. That way, if
-        // the original local is borrowed and the target is not, we do not pessimize the whole class.
-        if self.borrowed_locals.contains(*local) {
-            return;
-        }
         match ctxt {
             // Do not modify the local in storage statements.
             PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) => {}
-            // The local should have been marked as non-SSA.
-            PlaceContext::MutatingUse(_) => assert_eq!(*local, new_local),
             // We access the value.
             _ => *local = new_local,
         }
     }
 
-    fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, loc: Location) {
-        if let Some(new_projection) = self.process_projection(place.projection, loc) {
-            place.projection = self.tcx().mk_place_elems(&new_projection);
-        }
-
-        // Any non-mutating use context is ok.
-        let ctxt = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
-        self.visit_local(&mut place.local, ctxt, loc)
-    }
-
+    #[tracing::instrument(level = "trace", skip(self))]
     fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) {
         if let Operand::Move(place) = *operand
             // A move out of a projection of a copy is equivalent to a copy of the original
@@ -151,6 +132,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
         self.super_operand(operand, loc);
     }
 
+    #[tracing::instrument(level = "trace", skip(self))]
     fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, loc: Location) {
         // When removing storage statements, we need to remove both (#107511).
         if let StatementKind::StorageLive(l) | StatementKind::StorageDead(l) = stmt.kind
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index 48db536c122..b45bff2af44 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -89,7 +89,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
             opportunities: Vec::new(),
         };
 
-        for bb in body.basic_blocks.indices() {
+        for (bb, _) in traversal::preorder(body) {
             finder.start_from_switch(bb);
         }
 
diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
index 75f351f05c3..1bd770a8526 100644
--- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
+++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
@@ -516,8 +516,12 @@ struct LocalLabel<'a> {
 /// A custom `Subdiagnostic` implementation so that the notes are delivered in a specific order
 impl Subdiagnostic for LocalLabel<'_> {
     fn add_to_diag<G: rustc_errors::EmissionGuarantee>(self, diag: &mut rustc_errors::Diag<'_, G>) {
+        // Becuase parent uses this field , we need to remove it delay before adding it.
+        diag.remove_arg("name");
         diag.arg("name", self.name);
+        diag.remove_arg("is_generated_name");
         diag.arg("is_generated_name", self.is_generated_name);
+        diag.remove_arg("is_dropped_first_edition_2024");
         diag.arg("is_dropped_first_edition_2024", self.is_dropped_first_edition_2024);
         let msg = diag.eagerly_translate(crate::fluent_generated::mir_transform_tail_expr_local);
         diag.span_label(self.span, msg);
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index a54e548ad70..db933da6413 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -82,7 +82,7 @@ pub(super) fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
     remove_dead_blocks(body);
 
     // FIXME: Should probably be moved into some kind of pass manager
-    body.basic_blocks_mut().raw.shrink_to_fit();
+    body.basic_blocks.as_mut_preserves_cfg().shrink_to_fit();
 }
 
 impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg {
diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs
index edd0cabca49..03b6f9b7ff3 100644
--- a/compiler/rustc_mir_transform/src/ssa.rs
+++ b/compiler/rustc_mir_transform/src/ssa.rs
@@ -293,6 +293,10 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'_, 'tcx> {
 fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
     let mut direct_uses = std::mem::take(&mut ssa.direct_uses);
     let mut copies = IndexVec::from_fn_n(|l| l, body.local_decls.len());
+    // We must not unify two locals that are borrowed. But this is fine if one is borrowed and
+    // the other is not. This bitset is keyed by *class head* and contains whether any member of
+    // the class is borrowed.
+    let mut borrowed_classes = ssa.borrowed_locals().clone();
 
     for (local, rvalue, _) in ssa.assignments(body) {
         let (Rvalue::Use(Operand::Copy(place) | Operand::Move(place))
@@ -318,6 +322,11 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
         // visited before `local`, and we just have to copy the representing local.
         let head = copies[rhs];
 
+        // Do not unify two borrowed locals.
+        if borrowed_classes.contains(local) && borrowed_classes.contains(head) {
+            continue;
+        }
+
         if local == RETURN_PLACE {
             // `_0` is special, we cannot rename it. Instead, rename the class of `rhs` to
             // `RETURN_PLACE`. This is only possible if the class head is a temporary, not an
@@ -330,14 +339,21 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
                     *h = RETURN_PLACE;
                 }
             }
+            if borrowed_classes.contains(head) {
+                borrowed_classes.insert(RETURN_PLACE);
+            }
         } else {
             copies[local] = head;
+            if borrowed_classes.contains(local) {
+                borrowed_classes.insert(head);
+            }
         }
         direct_uses[rhs] -= 1;
     }
 
     debug!(?copies);
     debug!(?direct_uses);
+    debug!(?borrowed_classes);
 
     // Invariant: `copies` must point to the head of an equivalence class.
     #[cfg(debug_assertions)]
@@ -346,6 +362,13 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
     }
     debug_assert_eq!(copies[RETURN_PLACE], RETURN_PLACE);
 
+    // Invariant: `borrowed_classes` must be true if any member of the class is borrowed.
+    #[cfg(debug_assertions)]
+    for &head in copies.iter() {
+        let any_borrowed = ssa.borrowed_locals.iter().any(|l| copies[l] == head);
+        assert_eq!(borrowed_classes.contains(head), any_borrowed);
+    }
+
     ssa.direct_uses = direct_uses;
     ssa.copy_classes = copies;
 }
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 173030e0326..e90e32ebebb 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -949,6 +949,9 @@ fn visit_instance_use<'tcx>(
         }
         ty::InstanceKind::DropGlue(_, None) => {
             // Don't need to emit noop drop glue if we are calling directly.
+            //
+            // Note that we also optimize away the call to visit_instance_use in vtable construction
+            // (see create_mono_items_for_vtable_methods).
             if !is_direct_call {
                 output.push(create_fn_mono_item(tcx, instance, source));
             }
@@ -1177,8 +1180,13 @@ fn create_mono_items_for_vtable_methods<'tcx>(
         output.extend(methods);
     }
 
-    // Also add the destructor.
-    visit_drop_use(tcx, impl_ty, false, source, output);
+    // Also add the destructor, if it's necessary.
+    //
+    // This matches the check in vtable_allocation_provider in middle/ty/vtable.rs,
+    // if we don't need drop we're not adding an actual pointer to the vtable.
+    if impl_ty.needs_drop(tcx, ty::TypingEnv::fully_monomorphized()) {
+        visit_drop_use(tcx, impl_ty, false, source, output);
+    }
 }
 
 /// Scans the CTFE alloc in order to find function pointers and statics that must be monomorphized.
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 7ead0a6d6b7..780feb9b827 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -147,13 +147,9 @@ pub trait SolverDelegateEvalExt: SolverDelegate {
     fn evaluate_root_goal(
         &self,
         goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
-        generate_proof_tree: GenerateProofTree,
         span: <Self::Interner as Interner>::Span,
         stalled_on: Option<GoalStalledOn<Self::Interner>>,
-    ) -> (
-        Result<GoalEvaluation<Self::Interner>, NoSolution>,
-        Option<inspect::GoalEvaluation<Self::Interner>>,
-    );
+    ) -> Result<GoalEvaluation<Self::Interner>, NoSolution>;
 
     /// Check whether evaluating `goal` with a depth of `root_depth` may
     /// succeed. This only returns `false` if the goal is guaranteed to
@@ -170,17 +166,16 @@ pub trait SolverDelegateEvalExt: SolverDelegate {
 
     // FIXME: This is only exposed because we need to use it in `analyse.rs`
     // which is not yet uplifted. Once that's done, we should remove this.
-    fn evaluate_root_goal_raw(
+    fn evaluate_root_goal_for_proof_tree(
         &self,
         goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
-        generate_proof_tree: GenerateProofTree,
-        stalled_on: Option<GoalStalledOn<Self::Interner>>,
+        span: <Self::Interner as Interner>::Span,
     ) -> (
         Result<
             (NestedNormalizationGoals<Self::Interner>, GoalEvaluation<Self::Interner>),
             NoSolution,
         >,
-        Option<inspect::GoalEvaluation<Self::Interner>>,
+        inspect::GoalEvaluation<Self::Interner>,
     );
 }
 
@@ -193,13 +188,17 @@ where
     fn evaluate_root_goal(
         &self,
         goal: Goal<I, I::Predicate>,
-        generate_proof_tree: GenerateProofTree,
         span: I::Span,
         stalled_on: Option<GoalStalledOn<I>>,
-    ) -> (Result<GoalEvaluation<I>, NoSolution>, Option<inspect::GoalEvaluation<I>>) {
-        EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, span, |ecx| {
-            ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal, stalled_on)
-        })
+    ) -> Result<GoalEvaluation<I>, NoSolution> {
+        EvalCtxt::enter_root(
+            self,
+            self.cx().recursion_limit(),
+            GenerateProofTree::No,
+            span,
+            |ecx| ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal, stalled_on),
+        )
+        .0
     }
 
     fn root_goal_may_hold_with_depth(
@@ -217,24 +216,22 @@ where
     }
 
     #[instrument(level = "debug", skip(self))]
-    fn evaluate_root_goal_raw(
+    fn evaluate_root_goal_for_proof_tree(
         &self,
         goal: Goal<I, I::Predicate>,
-        generate_proof_tree: GenerateProofTree,
-        stalled_on: Option<GoalStalledOn<I>>,
+        span: I::Span,
     ) -> (
         Result<(NestedNormalizationGoals<I>, GoalEvaluation<I>), NoSolution>,
-        Option<inspect::GoalEvaluation<I>>,
+        inspect::GoalEvaluation<I>,
     ) {
-        EvalCtxt::enter_root(
+        let (result, proof_tree) = EvalCtxt::enter_root(
             self,
             self.cx().recursion_limit(),
-            generate_proof_tree,
-            I::Span::dummy(),
-            |ecx| {
-                ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal, stalled_on)
-            },
-        )
+            GenerateProofTree::Yes,
+            span,
+            |ecx| ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal, None),
+        );
+        (result, proof_tree.unwrap())
     }
 }
 
@@ -430,7 +427,7 @@ where
                 canonical_input,
                 step_kind_from_parent,
                 &mut canonical_goal_evaluation,
-                |search_graph, canonical_goal_evaluation| {
+                |search_graph, cx, canonical_input, canonical_goal_evaluation| {
                     EvalCtxt::enter_canonical(
                         cx,
                         search_graph,
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 8ee116b090d..8aaa8e9ca87 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -103,15 +103,12 @@ where
             // We currently elaborate all supertrait outlives obligations from impls.
             // This can be removed when we actually do coinduction correctly, and prove
             // all supertrait obligations unconditionally.
-            let goal_clause: I::Clause = goal.predicate.upcast(cx);
-            for clause in elaborate::elaborate(cx, [goal_clause]) {
-                if matches!(
-                    clause.kind().skip_binder(),
-                    ty::ClauseKind::TypeOutlives(..) | ty::ClauseKind::RegionOutlives(..)
-                ) {
-                    ecx.add_goal(GoalSource::Misc, goal.with(cx, clause));
-                }
-            }
+            ecx.add_goals(
+                GoalSource::Misc,
+                cx.impl_super_outlives(impl_def_id)
+                    .iter_instantiated(cx, impl_args)
+                    .map(|pred| goal.with(cx, pred)),
+            );
 
             ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
         })
diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml
index 6504081f0b9..c4a0ae2ce9d 100644
--- a/compiler/rustc_parse/Cargo.toml
+++ b/compiler/rustc_parse/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2024"
 [dependencies]
 # tidy-alphabetical-start
 bitflags = "2.4.1"
-rustc-literal-escaper = "0.0.2"
+rustc-literal-escaper = "0.0.4"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 2845bbed1c0..60d275bf2b4 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -1,5 +1,3 @@
-use std::ops::Range;
-
 use diagnostics::make_unclosed_delims_error;
 use rustc_ast::ast::{self, AttrStyle};
 use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
@@ -10,7 +8,7 @@ use rustc_errors::{Applicability, Diag, DiagCtxtHandle, StashKey};
 use rustc_lexer::{
     Base, Cursor, DocStyle, FrontmatterAllowed, LiteralKind, RawStrError, is_whitespace,
 };
-use rustc_literal_escaper::{EscapeError, Mode, unescape_mixed, unescape_unicode};
+use rustc_literal_escaper::{EscapeError, Mode, check_for_errors};
 use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::{
     RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX,
@@ -702,7 +700,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
                     }
                     err.emit()
                 }
-                self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) // ' '
+                self.cook_quoted(token::Char, Mode::Char, start, end, 1, 1) // ' '
             }
             rustc_lexer::LiteralKind::Byte { terminated } => {
                 if !terminated {
@@ -714,7 +712,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
                         .with_code(E0763)
                         .emit()
                 }
-                self.cook_unicode(token::Byte, Mode::Byte, start, end, 2, 1) // b' '
+                self.cook_quoted(token::Byte, Mode::Byte, start, end, 2, 1) // b' '
             }
             rustc_lexer::LiteralKind::Str { terminated } => {
                 if !terminated {
@@ -726,7 +724,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
                         .with_code(E0765)
                         .emit()
                 }
-                self.cook_unicode(token::Str, Mode::Str, start, end, 1, 1) // " "
+                self.cook_quoted(token::Str, Mode::Str, start, end, 1, 1) // " "
             }
             rustc_lexer::LiteralKind::ByteStr { terminated } => {
                 if !terminated {
@@ -738,7 +736,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
                         .with_code(E0766)
                         .emit()
                 }
-                self.cook_unicode(token::ByteStr, Mode::ByteStr, start, end, 2, 1) // b" "
+                self.cook_quoted(token::ByteStr, Mode::ByteStr, start, end, 2, 1)
+                // b" "
             }
             rustc_lexer::LiteralKind::CStr { terminated } => {
                 if !terminated {
@@ -750,13 +749,14 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
                         .with_code(E0767)
                         .emit()
                 }
-                self.cook_mixed(token::CStr, Mode::CStr, start, end, 2, 1) // c" "
+                self.cook_quoted(token::CStr, Mode::CStr, start, end, 2, 1) // c" "
             }
             rustc_lexer::LiteralKind::RawStr { n_hashes } => {
                 if let Some(n_hashes) = n_hashes {
                     let n = u32::from(n_hashes);
                     let kind = token::StrRaw(n_hashes);
-                    self.cook_unicode(kind, Mode::RawStr, start, end, 2 + n, 1 + n) // r##" "##
+                    self.cook_quoted(kind, Mode::RawStr, start, end, 2 + n, 1 + n)
+                // r##" "##
                 } else {
                     self.report_raw_str_error(start, 1);
                 }
@@ -765,7 +765,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
                 if let Some(n_hashes) = n_hashes {
                     let n = u32::from(n_hashes);
                     let kind = token::ByteStrRaw(n_hashes);
-                    self.cook_unicode(kind, Mode::RawByteStr, start, end, 3 + n, 1 + n) // br##" "##
+                    self.cook_quoted(kind, Mode::RawByteStr, start, end, 3 + n, 1 + n)
+                // br##" "##
                 } else {
                     self.report_raw_str_error(start, 2);
                 }
@@ -774,7 +775,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
                 if let Some(n_hashes) = n_hashes {
                     let n = u32::from(n_hashes);
                     let kind = token::CStrRaw(n_hashes);
-                    self.cook_unicode(kind, Mode::RawCStr, start, end, 3 + n, 1 + n) // cr##" "##
+                    self.cook_quoted(kind, Mode::RawCStr, start, end, 3 + n, 1 + n)
+                // cr##" "##
                 } else {
                     self.report_raw_str_error(start, 2);
                 }
@@ -1091,7 +1093,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
         self.dcx().emit_fatal(errors::TooManyHashes { span: self.mk_sp(start, self.pos), num });
     }
 
-    fn cook_common(
+    fn cook_quoted(
         &self,
         mut kind: token::LitKind,
         mode: Mode,
@@ -1099,32 +1101,28 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
         end: BytePos,
         prefix_len: u32,
         postfix_len: u32,
-        unescape: fn(&str, Mode, &mut dyn FnMut(Range<usize>, Result<(), EscapeError>)),
     ) -> (token::LitKind, Symbol) {
         let content_start = start + BytePos(prefix_len);
         let content_end = end - BytePos(postfix_len);
         let lit_content = self.str_from_to(content_start, content_end);
-        unescape(lit_content, mode, &mut |range, result| {
-            // Here we only check for errors. The actual unescaping is done later.
-            if let Err(err) = result {
-                let span_with_quotes = self.mk_sp(start, end);
-                let (start, end) = (range.start as u32, range.end as u32);
-                let lo = content_start + BytePos(start);
-                let hi = lo + BytePos(end - start);
-                let span = self.mk_sp(lo, hi);
-                let is_fatal = err.is_fatal();
-                if let Some(guar) = emit_unescape_error(
-                    self.dcx(),
-                    lit_content,
-                    span_with_quotes,
-                    span,
-                    mode,
-                    range,
-                    err,
-                ) {
-                    assert!(is_fatal);
-                    kind = token::Err(guar);
-                }
+        check_for_errors(lit_content, mode, |range, err| {
+            let span_with_quotes = self.mk_sp(start, end);
+            let (start, end) = (range.start as u32, range.end as u32);
+            let lo = content_start + BytePos(start);
+            let hi = lo + BytePos(end - start);
+            let span = self.mk_sp(lo, hi);
+            let is_fatal = err.is_fatal();
+            if let Some(guar) = emit_unescape_error(
+                self.dcx(),
+                lit_content,
+                span_with_quotes,
+                span,
+                mode,
+                range,
+                err,
+            ) {
+                assert!(is_fatal);
+                kind = token::Err(guar);
             }
         });
 
@@ -1137,34 +1135,6 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
         };
         (kind, sym)
     }
-
-    fn cook_unicode(
-        &self,
-        kind: token::LitKind,
-        mode: Mode,
-        start: BytePos,
-        end: BytePos,
-        prefix_len: u32,
-        postfix_len: u32,
-    ) -> (token::LitKind, Symbol) {
-        self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
-            unescape_unicode(src, mode, &mut |span, result| callback(span, result.map(drop)))
-        })
-    }
-
-    fn cook_mixed(
-        &self,
-        kind: token::LitKind,
-        mode: Mode,
-        start: BytePos,
-        end: BytePos,
-        prefix_len: u32,
-        postfix_len: u32,
-    ) -> (token::LitKind, Symbol) {
-        self.cook_common(kind, mode, start, end, prefix_len, postfix_len, |src, mode, callback| {
-            unescape_mixed(src, mode, &mut |span, result| callback(span, result.map(drop)))
-        })
-    }
 }
 
 pub fn nfc_normalize(string: &str) -> Symbol {
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 93c76c47f06..4e312aab497 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -847,7 +847,7 @@ impl<'a> Parser<'a> {
         self.dcx().emit_err(errors::LifetimeInBorrowExpression { span, lifetime_span: lt_span });
     }
 
-    /// Parse `mut?` or `raw [ const | mut ]`.
+    /// Parse `mut?` or `[ raw | pin ] [ const | mut ]`.
     fn parse_borrow_modifiers(&mut self) -> (ast::BorrowKind, ast::Mutability) {
         if self.check_keyword(exp!(Raw)) && self.look_ahead(1, Token::is_mutability) {
             // `raw [ const | mut ]`.
@@ -855,6 +855,11 @@ impl<'a> Parser<'a> {
             assert!(found_raw);
             let mutability = self.parse_const_or_mut().unwrap();
             (ast::BorrowKind::Raw, mutability)
+        } else if let Some((ast::Pinnedness::Pinned, mutbl)) = self.parse_pin_and_mut() {
+            // `pin [ const | mut ]`.
+            // `pin` has been gated in `self.parse_pin_and_mut()` so we don't
+            // need to gate it here.
+            (ast::BorrowKind::Pin, mutbl)
         } else {
             // `mut?`
             (ast::BorrowKind::Ref, self.parse_mutability())
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 658ed4bd41c..5088caa80f8 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1781,7 +1781,7 @@ impl<'a> Parser<'a> {
         let mut recovered = Recovered::No;
         if self.eat(exp!(OpenBrace)) {
             while self.token != token::CloseBrace {
-                match self.parse_field_def(adt_ty) {
+                match self.parse_field_def(adt_ty, ident_span) {
                     Ok(field) => {
                         fields.push(field);
                     }
@@ -1894,7 +1894,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses an element of a struct declaration.
-    fn parse_field_def(&mut self, adt_ty: &str) -> PResult<'a, FieldDef> {
+    fn parse_field_def(&mut self, adt_ty: &str, ident_span: Span) -> PResult<'a, FieldDef> {
         self.recover_vcs_conflict_marker();
         let attrs = self.parse_outer_attributes()?;
         self.recover_vcs_conflict_marker();
@@ -1902,7 +1902,7 @@ impl<'a> Parser<'a> {
             let lo = this.token.span;
             let vis = this.parse_visibility(FollowedByType::No)?;
             let safety = this.parse_unsafe_field();
-            this.parse_single_struct_field(adt_ty, lo, vis, safety, attrs)
+            this.parse_single_struct_field(adt_ty, lo, vis, safety, attrs, ident_span)
                 .map(|field| (field, Trailing::No, UsePreAttrPos::No))
         })
     }
@@ -1915,28 +1915,27 @@ impl<'a> Parser<'a> {
         vis: Visibility,
         safety: Safety,
         attrs: AttrVec,
+        ident_span: Span,
     ) -> PResult<'a, FieldDef> {
-        let mut seen_comma: bool = false;
         let a_var = self.parse_name_and_ty(adt_ty, lo, vis, safety, attrs)?;
-        if self.token == token::Comma {
-            seen_comma = true;
-        }
-        if self.eat(exp!(Semi)) {
-            let sp = self.prev_token.span;
-            let mut err =
-                self.dcx().struct_span_err(sp, format!("{adt_ty} fields are separated by `,`"));
-            err.span_suggestion_short(
-                sp,
-                "replace `;` with `,`",
-                ",",
-                Applicability::MachineApplicable,
-            );
-            return Err(err);
-        }
         match self.token.kind {
             token::Comma => {
                 self.bump();
             }
+            token::Semi => {
+                self.bump();
+                let sp = self.prev_token.span;
+                let mut err =
+                    self.dcx().struct_span_err(sp, format!("{adt_ty} fields are separated by `,`"));
+                err.span_suggestion_short(
+                    sp,
+                    "replace `;` with `,`",
+                    ",",
+                    Applicability::MachineApplicable,
+                );
+                err.span_label(ident_span, format!("while parsing this {adt_ty}"));
+                err.emit();
+            }
             token::CloseBrace => {}
             token::DocComment(..) => {
                 let previous_span = self.prev_token.span;
@@ -1945,19 +1944,11 @@ impl<'a> Parser<'a> {
                     missing_comma: None,
                 };
                 self.bump(); // consume the doc comment
-                let comma_after_doc_seen = self.eat(exp!(Comma));
-                // `seen_comma` is always false, because we are inside doc block
-                // condition is here to make code more readable
-                if !seen_comma && comma_after_doc_seen {
-                    seen_comma = true;
-                }
-                if comma_after_doc_seen || self.token == token::CloseBrace {
+                if self.eat(exp!(Comma)) || self.token == token::CloseBrace {
                     self.dcx().emit_err(err);
                 } else {
-                    if !seen_comma {
-                        let sp = previous_span.shrink_to_hi();
-                        err.missing_comma = Some(sp);
-                    }
+                    let sp = previous_span.shrink_to_hi();
+                    err.missing_comma = Some(sp);
                     return Err(self.dcx().create_err(err));
                 }
             }
diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs
index 2a44c90abc1..15679d23bc5 100644
--- a/compiler/rustc_parse/src/parser/tests.rs
+++ b/compiler/rustc_parse/src/parser/tests.rs
@@ -14,6 +14,7 @@ use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, Toke
 use rustc_ast::{self as ast, PatKind, visit};
 use rustc_ast_pretty::pprust::item_to_string;
 use rustc_errors::emitter::{HumanEmitter, OutputTheme};
+use rustc_errors::translation::Translator;
 use rustc_errors::{DiagCtxt, MultiSpan, PResult};
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::{FilePathMapping, SourceMap};
@@ -41,9 +42,8 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> {
 fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
     let output = Arc::new(Mutex::new(Vec::new()));
     let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
-    let fallback_bundle =
-        rustc_errors::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
-    let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle)
+    let translator = Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
+    let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), translator)
         .sm(Some(source_map.clone()))
         .diagnostic_width(Some(140));
     emitter = emitter.theme(theme);
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index b3096e46b09..68d78af5943 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -286,10 +286,22 @@ fn emit_malformed_attribute(
     if matches!(
         name,
         sym::inline
+            | sym::may_dangle
+            | sym::rustc_as_ptr
+            | sym::rustc_pub_transparent
+            | sym::rustc_const_stable_indirect
             | sym::rustc_force_inline
             | sym::rustc_confusables
+            | sym::rustc_skip_during_method_dispatch
             | sym::repr
+            | sym::align
             | sym::deprecated
+            | sym::optimize
+            | sym::cold
+            | sym::naked
+            | sym::no_mangle
+            | sym::must_use
+            | sym::track_caller
     ) {
         return;
     }
diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml
index 52f23c00d4b..0666ae29409 100644
--- a/compiler/rustc_parse_format/Cargo.toml
+++ b/compiler/rustc_parse_format/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-rustc-literal-escaper = "0.0.2"
+rustc-literal-escaper = "0.0.4"
 rustc_lexer = { path = "../rustc_lexer" }
 # tidy-alphabetical-end
 
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 42bd0f5d847..8e4da7923fc 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -20,7 +20,6 @@ use std::ops::Range;
 pub use Alignment::*;
 pub use Count::*;
 pub use Position::*;
-use rustc_literal_escaper::{Mode, unescape_unicode};
 
 /// The type of format string that we are parsing.
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -320,7 +319,7 @@ impl<'input> Parser<'input> {
                     let without_quotes = &snippet[1..snippet.len() - 1];
                     let (mut ok, mut vec) = (true, vec![]);
                     let mut chars = input.chars();
-                    unescape_unicode(without_quotes, Mode::Str, &mut |range, res| match res {
+                    rustc_literal_escaper::unescape_str(without_quotes, |range, res| match res {
                         Ok(ch) if ok && chars.next().is_some_and(|c| ch == c) => {
                             vec.push((range, ch));
                         }
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 7c237d708c0..f601d058b33 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -13,6 +13,10 @@ passes_abi_ne =
 passes_abi_of =
     fn_abi_of({$fn_name}) = {$fn_abi}
 
+passes_align_should_be_repr_align =
+    `#[align(...)]` is not supported on {$item} items
+    .suggestion = use `#[repr(align(...))]` instead
+
 passes_allow_incoherent_impl =
     `rustc_allow_incoherent_impl` attribute should be applied to impl items
     .label = the only currently supported targets are inherent methods
@@ -29,10 +33,6 @@ passes_attr_application_struct =
     attribute should be applied to a struct
     .label = not a struct
 
-passes_attr_application_struct_enum_function_method_union =
-    attribute should be applied to a struct, enum, function, associated function, or union
-    .label = not a struct, enum, function, associated function, or union
-
 passes_attr_application_struct_enum_union =
     attribute should be applied to a struct, enum, or union
     .label = not a struct, enum, or union
@@ -82,11 +82,14 @@ passes_collapse_debuginfo =
 passes_confusables = attribute should be applied to an inherent method
     .label = not an inherent method
 
+passes_const_continue_attr =
+    `#[const_continue]` should be applied to a break expression
+    .label = not a break expression
+
 passes_const_stable_not_stable =
     attribute `#[rustc_const_stable]` can only be applied to functions that are declared `#[stable]`
     .label = attribute specified here
 
-
 passes_coroutine_on_non_closure =
     attribute should be applied to closures
     .label = not a closure
@@ -291,7 +294,7 @@ passes_duplicate_lang_item_crate_depends =
     .second_definition_path = second definition in `{$crate_name}` loaded from {$path}
 
 passes_enum_variant_same_name =
-    it is impossible to refer to the {$descr} `{$dead_name}` because it is shadowed by this enum variant with the same name
+    it is impossible to refer to the {$dead_descr} `{$dead_name}` because it is shadowed by this enum variant with the same name
 
 passes_export_name =
     attribute should be applied to a free function, impl method or static
@@ -446,6 +449,10 @@ passes_linkage =
     attribute should be applied to a function or static
     .label = not a function definition or static
 
+passes_loop_match_attr =
+    `#[loop_match]` should be applied to a loop
+    .label = not a loop
+
 passes_macro_export =
     `#[macro_export]` only has an effect on macro definitions
 
@@ -491,11 +498,6 @@ passes_must_not_suspend =
 passes_must_use_no_effect =
     `#[must_use]` has no effect when applied to {$article} {$target}
 
-passes_naked_functions_incompatible_attribute =
-    attribute incompatible with `#[unsafe(naked)]`
-    .label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`
-    .naked_attribute = function marked with `#[unsafe(naked)]` here
-
 passes_no_link =
     attribute should be applied to an `extern crate` item
     .label = not an `extern crate` item
@@ -583,13 +585,14 @@ passes_remove_fields =
      *[other] fields
     }
 
-passes_repr_align_function =
-    `repr(align)` attributes on functions are unstable
-
 passes_repr_align_greater_than_target_max =
     alignment must not be greater than `isize::MAX` bytes
     .note = `isize::MAX` is {$size} for the current target
 
+passes_repr_align_should_be_align =
+    `#[repr(align(...))]` is not supported on {$item} items
+    .help = use `#[align(...)]` instead
+
 passes_repr_conflicting =
     conflicting representation hints
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 5ce803aa1f8..99c220d946e 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -116,7 +116,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         let mut seen = FxHashMap::default();
         let attrs = self.tcx.hir_attrs(hir_id);
         for attr in attrs {
+            let mut style = None;
             match attr {
+                Attribute::Parsed(AttributeKind::SkipDuringMethodDispatch {
+                    span: attr_span,
+                    ..
+                }) => {
+                    self.check_must_be_applied_to_trait(*attr_span, span, target);
+                }
                 Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => {
                     self.check_confusables(*first_span, target);
                 }
@@ -128,6 +135,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => {
                     self.check_inline(hir_id, *attr_span, span, kind, target)
                 }
+                Attribute::Parsed(AttributeKind::Optimize(_, attr_span)) => {
+                    self.check_optimize(hir_id, *attr_span, span, target)
+                }
+                Attribute::Parsed(AttributeKind::LoopMatch(attr_span)) => {
+                    self.check_loop_match(hir_id, *attr_span, target)
+                }
+                Attribute::Parsed(AttributeKind::ConstContinue(attr_span)) => {
+                    self.check_const_continue(hir_id, *attr_span, target)
+                }
                 Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
                     .check_allow_internal_unstable(
                         hir_id,
@@ -146,6 +162,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 }
                 Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */
                 }
+
+                &Attribute::Parsed(AttributeKind::PubTransparent(attr_span)) => {
+                    self.check_rustc_pub_transparent(attr_span, span, attrs)
+                }
+                Attribute::Parsed(AttributeKind::Cold(attr_span)) => {
+                    self.check_cold(hir_id, *attr_span, span, target)
+                }
+                Attribute::Parsed(AttributeKind::Align { align, span: repr_span }) => {
+                    self.check_align(span, target, *align, *repr_span)
+                }
+                Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
+                    self.check_naked(hir_id, *attr_span, span, target)
+                }
+                Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => {
+                    self.check_track_caller(hir_id, *attr_span, attrs, span, target)
+                }
                 Attribute::Parsed(
                     AttributeKind::BodyStability { .. }
                     | AttributeKind::ConstStabilityIndirect
@@ -154,7 +186,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => {
                     self.check_applied_to_fn_or_method(hir_id, *attr_span, span, target)
                 }
-                Attribute::Unparsed(_) => {
+                Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
+                    self.check_may_dangle(hir_id, *attr_span)
+                }
+                Attribute::Parsed(AttributeKind::MustUse { span, .. }) => {
+                    self.check_must_use(hir_id, *span, target)
+                }
+                Attribute::Parsed(AttributeKind::NoMangle(attr_span)) => {
+                    self.check_no_mangle(hir_id, *attr_span, span, target)
+                }
+                Attribute::Unparsed(attr_item) => {
+                    style = Some(attr_item.style);
                     match attr.path().as_slice() {
                         [sym::diagnostic, sym::do_not_recommend, ..] => {
                             self.check_do_not_recommend(attr.span(), hir_id, target, attr, item)
@@ -163,7 +205,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
                         }
                         [sym::coverage, ..] => self.check_coverage(attr, span, target),
-                        [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target),
                         [sym::no_sanitize, ..] => {
                             self.check_no_sanitize(attr, span, target)
                         }
@@ -173,11 +214,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             self.check_target_feature(hir_id, attr, span, target, attrs)
                         }
                         [sym::thread_local, ..] => self.check_thread_local(attr, span, target),
-                        [sym::track_caller, ..] => {
-                            self.check_track_caller(hir_id, attr.span(), attrs, span, target)
-                        }
                         [sym::doc, ..] => self.check_doc_attrs(
                             attr,
+                            attr_item.style,
                             hir_id,
                             target,
                             &mut specified_inline,
@@ -193,7 +232,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         [sym::rustc_std_internal_symbol, ..] => {
                             self.check_rustc_std_internal_symbol(attr, span, target)
                         }
-                        [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs),
                         [sym::rustc_no_implicit_autorefs, ..] => {
                             self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target)
                         }
@@ -224,11 +262,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         | [sym::rustc_must_implement_one_of, ..]
                         | [sym::rustc_deny_explicit_impl, ..]
                         | [sym::rustc_do_not_implement_via_object, ..]
-                        | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target),
+                        | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr.span(), span, target),
                         [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target),
                         [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
-                        [sym::must_use, ..] => self.check_must_use(hir_id, attr, target),
-                        [sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr),
                         [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target),
                         [sym::rustc_allow_incoherent_impl, ..] => {
                             self.check_allow_incoherent_impl(attr, span, target)
@@ -239,11 +275,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target),
                         [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target),
                         [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target),
-                        [sym::cold, ..] => self.check_cold(hir_id, attr, span, target),
                         [sym::link, ..] => self.check_link(hir_id, attr, span, target),
                         [sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
                         [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target),
-                        [sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target),
                         [sym::macro_use, ..] | [sym::macro_escape, ..] => {
                             self.check_macro_use(hir_id, attr, target)
                         }
@@ -279,7 +313,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             self.check_type_const(hir_id,attr, target);
                         }
                         [sym::linkage, ..] => self.check_linkage(attr, span, target),
-                        [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs),
                         [
                             // ok
                             sym::allow
@@ -341,14 +374,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
                     attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
                 {
-                    match attr.style() {
-                        ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
+                    match style {
+                        Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint(
                             UNUSED_ATTRIBUTES,
                             hir_id,
                             attr.span(),
                             errors::OuterCrateLevelAttr,
                         ),
-                        ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
+                        Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
                             UNUSED_ATTRIBUTES,
                             hir_id,
                             attr.span(),
@@ -362,7 +395,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen);
             }
 
-            self.check_unused_attribute(hir_id, attr)
+            self.check_unused_attribute(hir_id, attr, style)
         }
 
         self.check_repr(attrs, span, target, item, hir_id);
@@ -525,7 +558,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
 
     /// Checks that `#[optimize(..)]` is applied to a function/closure/method,
     /// or to an impl block or module.
-    fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
+    fn check_optimize(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
         let is_valid = matches!(
             target,
             Target::Fn
@@ -534,7 +567,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         );
         if !is_valid {
             self.dcx().emit_err(errors::OptimizeInvalidTarget {
-                attr_span: attr.span(),
+                attr_span,
                 defn_span: span,
                 on_crate: hir_id == CRATE_HIR_ID,
             });
@@ -601,55 +634,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Checks if `#[naked]` is applied to a function definition.
-    fn check_naked(
-        &self,
-        hir_id: HirId,
-        attr: &Attribute,
-        span: Span,
-        target: Target,
-        attrs: &[Attribute],
-    ) {
-        // many attributes don't make sense in combination with #[naked].
-        // Notable attributes that are incompatible with `#[naked]` are:
-        //
-        // * `#[inline]`
-        // * `#[track_caller]`
-        // * `#[test]`, `#[ignore]`, `#[should_panic]`
-        //
-        // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
-        // accurate.
-        const ALLOW_LIST: &[rustc_span::Symbol] = &[
-            // conditional compilation
-            sym::cfg_trace,
-            sym::cfg_attr_trace,
-            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
-            sym::test,
-            sym::ignore,
-            sym::should_panic,
-            sym::bench,
-            // diagnostics
-            sym::allow,
-            sym::warn,
-            sym::deny,
-            sym::forbid,
-            // FIXME(jdonszelmann): not used, because already a new-style attr (ugh)
-            sym::deprecated,
-            sym::must_use,
-            // abi, linking and FFI
-            sym::export_name,
-            sym::link_section,
-            sym::linkage,
-            sym::no_mangle,
-            sym::naked,
-            sym::instruction_set,
-            sym::repr,
-            sym::rustc_std_internal_symbol,
-            // code generation
-            sym::cold,
-            // documentation
-            sym::doc,
-        ];
-
+    fn check_naked(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
         match target {
             Target::Fn
             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {
@@ -667,70 +652,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     )
                     .emit();
                 }
-
-                for other_attr in attrs {
-                    // this covers "sugared doc comments" of the form `/// ...`
-                    // it does not cover `#[doc = "..."]`, which is handled below
-                    if other_attr.is_doc_comment() {
-                        continue;
-                    }
-
-                    // FIXME(jdonszelmann): once naked uses new-style parsing,
-                    // this check can be part of the parser and be removed here
-                    match other_attr {
-                        Attribute::Parsed(
-                            AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. },
-                        ) => {
-                            continue;
-                        }
-                        Attribute::Parsed(AttributeKind::Inline(.., span)) => {
-                            self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute {
-                                span: *span,
-                                naked_span: attr.span(),
-                                attr: sym::inline.to_string(),
-                            });
-
-                            return;
-                        }
-                        // FIXME(jdonszelmann): make exhaustive
-                        _ => {}
-                    }
-
-                    if other_attr.has_name(sym::target_feature) {
-                        if !self.tcx.features().naked_functions_target_feature() {
-                            feature_err(
-                                &self.tcx.sess,
-                                sym::naked_functions_target_feature,
-                                other_attr.span(),
-                                "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
-                            ).emit();
-
-                            return;
-                        } else {
-                            continue;
-                        }
-                    }
-
-                    if !other_attr.has_any_name(ALLOW_LIST)
-                        && !matches!(other_attr.path().as_slice(), [sym::rustfmt, ..])
-                    {
-                        let path = other_attr.path();
-                        let path: Vec<_> = path.iter().map(|s| s.as_str()).collect();
-                        let other_attr_name = path.join("::");
-
-                        self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute {
-                            span: other_attr.span(),
-                            naked_span: attr.span(),
-                            attr: other_attr_name,
-                        });
-
-                        return;
-                    }
-                }
             }
             _ => {
                 self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
-                    attr_span: attr.span(),
+                    attr_span,
                     defn_span: span,
                     on_crate: hir_id == CRATE_HIR_ID,
                 });
@@ -803,9 +728,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             // erroneously allowed it and some crates used it accidentally, to be compatible
             // with crates depending on them, we can't throw an error here.
             Target::Field | Target::Arm | Target::MacroDef => {
-                for attr in attrs {
-                    self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "track_caller");
-                }
+                self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "track_caller");
             }
             _ => {
                 self.dcx().emit_err(errors::TrackedCallerWrongLocation {
@@ -1180,7 +1103,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     /// the first `inline`/`no_inline` attribute.
     fn check_doc_inline(
         &self,
-        attr: &Attribute,
+        style: AttrStyle,
         meta: &MetaItemInner,
         hir_id: HirId,
         target: Target,
@@ -1210,8 +1133,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     meta.span(),
                     errors::DocInlineOnlyUse {
                         attr_span: meta.span(),
-                        item_span: (attr.style() == AttrStyle::Outer)
-                            .then(|| self.tcx.hir_span(hir_id)),
+                        item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
                     },
                 );
             }
@@ -1220,7 +1142,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
 
     fn check_doc_masked(
         &self,
-        attr: &Attribute,
+        style: AttrStyle,
         meta: &MetaItemInner,
         hir_id: HirId,
         target: Target,
@@ -1232,8 +1154,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 meta.span(),
                 errors::DocMaskedOnlyExternCrate {
                     attr_span: meta.span(),
-                    item_span: (attr.style() == AttrStyle::Outer)
-                        .then(|| self.tcx.hir_span(hir_id)),
+                    item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
                 },
             );
             return;
@@ -1246,8 +1167,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 meta.span(),
                 errors::DocMaskedNotExternCrateSelf {
                     attr_span: meta.span(),
-                    item_span: (attr.style() == AttrStyle::Outer)
-                        .then(|| self.tcx.hir_span(hir_id)),
+                    item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
                 },
             );
         }
@@ -1271,13 +1191,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     fn check_attr_crate_level(
         &self,
         attr: &Attribute,
+        style: AttrStyle,
         meta: &MetaItemInner,
         hir_id: HirId,
     ) -> bool {
         if hir_id != CRATE_HIR_ID {
             // insert a bang between `#` and `[...`
             let bang_span = attr.span().lo() + BytePos(1);
-            let sugg = (attr.style() == AttrStyle::Outer
+            let sugg = (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),
@@ -1294,7 +1215,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Checks that `doc(test(...))` attribute contains only valid attributes and are at the right place.
-    fn check_test_attr(&self, attr: &Attribute, meta: &MetaItemInner, hir_id: HirId) {
+    fn check_test_attr(
+        &self,
+        attr: &Attribute,
+        style: AttrStyle,
+        meta: &MetaItemInner,
+        hir_id: HirId,
+    ) {
         if let Some(metas) = meta.meta_item_list() {
             for i_meta in metas {
                 match (i_meta.name(), i_meta.meta_item()) {
@@ -1302,7 +1229,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         // Allowed everywhere like `#[doc]`
                     }
                     (Some(sym::no_crate_inject), _) => {
-                        self.check_attr_crate_level(attr, meta, hir_id);
+                        self.check_attr_crate_level(attr, style, meta, hir_id);
                     }
                     (_, Some(m)) => {
                         self.tcx.emit_node_span_lint(
@@ -1356,6 +1283,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     fn check_doc_attrs(
         &self,
         attr: &Attribute,
+        style: AttrStyle,
         hir_id: HirId,
         target: Target,
         specified_inline: &mut Option<(bool, Span)>,
@@ -1390,7 +1318,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         }
 
                         Some(sym::test) => {
-                            self.check_test_attr(attr, meta, hir_id);
+                            self.check_test_attr(attr, style, meta, hir_id);
                         }
 
                         Some(
@@ -1401,25 +1329,25 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             | sym::html_root_url
                             | sym::html_no_source,
                         ) => {
-                            self.check_attr_crate_level(attr, meta, hir_id);
+                            self.check_attr_crate_level(attr, style, meta, hir_id);
                         }
 
                         Some(sym::cfg_hide) => {
-                            if self.check_attr_crate_level(attr, meta, hir_id) {
+                            if self.check_attr_crate_level(attr, style, meta, hir_id) {
                                 self.check_doc_cfg_hide(meta, hir_id);
                             }
                         }
 
                         Some(sym::inline | sym::no_inline) => {
-                            self.check_doc_inline(attr, meta, hir_id, target, specified_inline)
+                            self.check_doc_inline(style, meta, hir_id, target, specified_inline)
                         }
 
-                        Some(sym::masked) => self.check_doc_masked(attr, meta, hir_id, target),
+                        Some(sym::masked) => self.check_doc_masked(style, meta, hir_id, target),
 
                         Some(sym::cfg | sym::hidden | sym::notable_trait) => {}
 
                         Some(sym::rust_logo) => {
-                            if self.check_attr_crate_level(attr, meta, hir_id)
+                            if self.check_attr_crate_level(attr, style, meta, hir_id)
                                 && !self.tcx.features().rustdoc_internals()
                             {
                                 feature_err(
@@ -1458,7 +1386,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                                     errors::DocTestUnknownInclude {
                                         path,
                                         value: value.to_string(),
-                                        inner: match attr.style() {
+                                        inner: match style {
                                             AttrStyle::Inner => "!",
                                             AttrStyle::Outer => "",
                                         },
@@ -1551,7 +1479,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Warns against some misuses of `#[must_use]`
-    fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
+    fn check_must_use(&self, hir_id: HirId, attr_span: Span, target: Target) {
         if matches!(
             target,
             Target::Fn
@@ -1591,7 +1519,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         self.tcx.emit_node_span_lint(
             UNUSED_ATTRIBUTES,
             hir_id,
-            attr.span(),
+            attr_span,
             errors::MustUseNoEffect { article, target },
         );
     }
@@ -1607,7 +1535,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl.
-    fn check_may_dangle(&self, hir_id: HirId, attr: &Attribute) {
+    fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) {
         if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id)
             && matches!(
                 param.kind,
@@ -1624,11 +1552,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             return;
         }
 
-        self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span() });
+        self.dcx().emit_err(errors::InvalidMayDangle { attr_span });
     }
 
     /// Checks if `#[cold]` is applied to a non-function.
-    fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
+    fn check_cold(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
         match target {
             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
@@ -1636,7 +1564,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             // erroneously allowed it and some crates used it accidentally, to be compatible
             // with crates depending on them, we can't throw an error here.
             Target::Field | Target::Arm | Target::MacroDef => {
-                self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "cold");
+                self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "cold");
             }
             _ => {
                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
@@ -1644,7 +1572,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 self.tcx.emit_node_span_lint(
                     UNUSED_ATTRIBUTES,
                     hir_id,
-                    attr.span(),
+                    attr_span,
                     errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID },
                 );
             }
@@ -1887,14 +1815,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Checks if the attribute is applied to a trait.
-    fn check_must_be_applied_to_trait(&self, attr: &Attribute, span: Span, target: Target) {
+    fn check_must_be_applied_to_trait(&self, attr_span: Span, defn_span: Span, target: Target) {
         match target {
             Target::Trait => {}
             _ => {
-                self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait {
-                    attr_span: attr.span(),
-                    defn_span: span,
-                });
+                self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { attr_span, defn_span });
             }
         }
     }
@@ -1924,7 +1849,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Checks if `#[no_mangle]` is applied to a function or static.
-    fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
+    fn check_no_mangle(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
         match target {
             Target::Static | Target::Fn => {}
             Target::Method(..) if self.is_impl_item(hir_id) => {}
@@ -1933,7 +1858,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             // erroneously allowed it and some crates used it accidentally, to be compatible
             // with crates depending on them, we can't throw an error here.
             Target::Field | Target::Arm | Target::MacroDef => {
-                self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_mangle");
+                self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "no_mangle");
             }
             // FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error
             // The error should specify that the item that is wrong is specifically a *foreign* fn/static
@@ -1947,8 +1872,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 self.tcx.emit_node_span_lint(
                     UNUSED_ATTRIBUTES,
                     hir_id,
-                    attr.span(),
-                    errors::NoMangleForeign { span, attr_span: attr.span(), foreign_item_kind },
+                    attr_span,
+                    errors::NoMangleForeign { span, attr_span, foreign_item_kind },
                 );
             }
             _ => {
@@ -1957,13 +1882,35 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 self.tcx.emit_node_span_lint(
                     UNUSED_ATTRIBUTES,
                     hir_id,
-                    attr.span(),
+                    attr_span,
                     errors::NoMangle { span },
                 );
             }
         }
     }
 
+    /// Checks if the `#[align]` attributes on `item` are valid.
+    fn check_align(&self, span: Span, target: Target, align: Align, repr_span: Span) {
+        match target {
+            Target::Fn | Target::Method(_) => {}
+            Target::Struct | Target::Union | Target::Enum => {
+                self.dcx().emit_err(errors::AlignShouldBeReprAlign {
+                    span: repr_span,
+                    item: target.name(),
+                    align_bytes: align.bytes(),
+                });
+            }
+            _ => {
+                self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
+                    hint_span: repr_span,
+                    span,
+                });
+            }
+        }
+
+        self.check_align_value(align, repr_span);
+    }
+
     /// Checks if the `#[repr]` attributes on `item` are valid.
     fn check_repr(
         &self,
@@ -2016,23 +1963,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     match target {
                         Target::Struct | Target::Union | Target::Enum => {}
                         Target::Fn | Target::Method(_) => {
-                            if !self.tcx.features().fn_align() {
-                                feature_err(
-                                    &self.tcx.sess,
-                                    sym::fn_align,
-                                    *repr_span,
-                                    fluent::passes_repr_align_function,
-                                )
-                                .emit();
-                            }
+                            self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
+                                span: *repr_span,
+                                item: target.name(),
+                            });
                         }
                         _ => {
-                            self.dcx().emit_err(
-                                errors::AttrApplication::StructEnumFunctionMethodUnion {
-                                    hint_span: *repr_span,
-                                    span,
-                                },
-                            );
+                            self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
+                                hint_span: *repr_span,
+                                span,
+                            });
                         }
                     }
 
@@ -2090,21 +2030,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         match target {
                             Target::Struct | Target::Union | Target::Enum => continue,
                             Target::Fn | Target::Method(_) => {
-                                feature_err(
-                                    &self.tcx.sess,
-                                    sym::fn_align,
-                                    *repr_span,
-                                    fluent::passes_repr_align_function,
-                                )
-                                .emit();
+                                self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
+                                    span: *repr_span,
+                                    item: target.name(),
+                                });
                             }
                             _ => {
-                                self.dcx().emit_err(
-                                    errors::AttrApplication::StructEnumFunctionMethodUnion {
-                                        hint_span: *repr_span,
-                                        span,
-                                    },
-                                );
+                                self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
+                                    hint_span: *repr_span,
+                                    span,
+                                });
                             }
                         }
                     }
@@ -2402,7 +2337,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
-    fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
+    fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
         // FIXME(jdonszelmann): deduplicate these checks after more attrs are parsed. This is very
         // ugly now but can 100% be removed later.
         if let Attribute::Parsed(p) = attr {
@@ -2455,14 +2390,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             })
         {
             if hir_id != CRATE_HIR_ID {
-                match attr.style() {
-                    ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
+                match style {
+                    Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint(
                         UNUSED_ATTRIBUTES,
                         hir_id,
                         attr.span(),
                         errors::OuterCrateLevelAttr,
                     ),
-                    ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
+                    Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
                         UNUSED_ATTRIBUTES,
                         hir_id,
                         attr.span(),
@@ -2702,6 +2637,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             }
         }
     }
+
+    fn check_loop_match(&self, hir_id: HirId, attr_span: Span, target: Target) {
+        let node_span = self.tcx.hir_span(hir_id);
+
+        if !matches!(target, Target::Expression) {
+            self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span });
+            return;
+        }
+
+        if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Loop(..)) {
+            self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span });
+        };
+    }
+
+    fn check_const_continue(&self, hir_id: HirId, attr_span: Span, target: Target) {
+        let node_span = self.tcx.hir_span(hir_id);
+
+        if !matches!(target, Target::Expression) {
+            self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
+            return;
+        }
+
+        if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Break(..)) {
+            self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
+        };
+    }
 }
 
 impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 4257d8e8d16..ce43b2c281d 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -234,7 +234,14 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         pats: &[hir::PatField<'_>],
     ) {
         let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
-            ty::Adt(adt, _) => adt.variant_of_res(res),
+            ty::Adt(adt, _) => {
+                // Marks the ADT live if its variant appears as the pattern,
+                // considering cases when we have `let T(x) = foo()` and `fn foo<T>() -> T;`,
+                // we will lose the liveness info of `T` cause we cannot mark it live when visiting `foo`.
+                // Related issue: https://github.com/rust-lang/rust/issues/120770
+                self.check_def_id(adt.did());
+                adt.variant_of_res(res)
+            }
             _ => span_bug!(lhs.span, "non-ADT in struct pattern"),
         };
         for pat in pats {
@@ -254,7 +261,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         dotdot: hir::DotDotPos,
     ) {
         let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
-            ty::Adt(adt, _) => adt.variant_of_res(res),
+            ty::Adt(adt, _) => {
+                // Marks the ADT live if its variant appears as the pattern
+                self.check_def_id(adt.did());
+                adt.variant_of_res(res)
+            }
             _ => {
                 self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
                 return;
@@ -359,31 +370,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 return false;
             }
 
-            // don't ignore impls for Enums and pub Structs whose methods don't have self receiver,
-            // cause external crate may call such methods to construct values of these types
-            if let Some(local_impl_of) = impl_of.as_local()
-                && let Some(local_def_id) = def_id.as_local()
-                && let Some(fn_sig) =
-                    self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
-                && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
-                && let TyKind::Path(QPath::Resolved(_, path)) =
-                    self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind
-                && let Res::Def(def_kind, did) = path.res
-            {
-                match def_kind {
-                    // for example, #[derive(Default)] pub struct T(i32);
-                    // external crate can call T::default() to construct T,
-                    // so that don't ignore impl Default for pub Enum and Structs
-                    DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => {
-                        return false;
-                    }
-                    // don't ignore impl Default for Enums,
-                    // cause we don't know which variant is constructed
-                    DefKind::Enum => return false,
-                    _ => (),
-                };
-            }
-
             if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
                 && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
             {
@@ -494,38 +480,25 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         impl_id: hir::ItemId,
         local_def_id: LocalDefId,
     ) -> bool {
-        if self.should_ignore_item(local_def_id.to_def_id()) {
-            return false;
-        }
-
         let trait_def_id = match self.tcx.def_kind(local_def_id) {
             // assoc impl items of traits are live if the corresponding trait items are live
-            DefKind::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id,
+            DefKind::AssocFn => self
+                .tcx
+                .associated_item(local_def_id)
+                .trait_item_def_id
+                .and_then(|def_id| def_id.as_local()),
             // impl items are live if the corresponding traits are live
             DefKind::Impl { of_trait: true } => self
                 .tcx
                 .impl_trait_ref(impl_id.owner_id.def_id)
-                .and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)),
+                .and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()),
             _ => None,
         };
 
-        if let Some(trait_def_id) = trait_def_id {
-            if let Some(trait_def_id) = trait_def_id.as_local()
-                && !self.live_symbols.contains(&trait_def_id)
-            {
-                return false;
-            }
-
-            // FIXME: legacy logic to check whether the function may construct `Self`,
-            // this can be removed after supporting marking ADTs appearing in patterns
-            // as live, then we can check private impls of public traits directly
-            if let Some(fn_sig) =
-                self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
-                && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
-                && self.tcx.visibility(trait_def_id).is_public()
-            {
-                return true;
-            }
+        if let Some(trait_def_id) = trait_def_id
+            && !self.live_symbols.contains(&trait_def_id)
+        {
+            return false;
         }
 
         // The impl or impl item is used if the corresponding trait or trait item is used and the ty is used.
@@ -635,6 +608,11 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
     fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) {
         match &expr.kind {
             rustc_hir::PatExprKind::Path(qpath) => {
+                // mark the type of variant live when meeting E::V in expr
+                if let ty::Adt(adt, _) = self.typeck_results().node_type(expr.hir_id).kind() {
+                    self.check_def_id(adt.did());
+                }
+
                 let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
                 self.handle_res(res);
             }
@@ -1078,7 +1056,7 @@ impl<'tcx> DeadVisitor<'tcx> {
                         maybe_enum.variants().iter().find(|i| i.name == dead_item.name)
                 {
                     Some(crate::errors::EnumVariantSameName {
-                        descr: tcx.def_descr(dead_item.def_id.to_def_id()),
+                        dead_descr: tcx.def_descr(dead_item.def_id.to_def_id()),
                         dead_name: dead_item.name,
                         variant_span: tcx.def_span(variant.def_id),
                     })
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index f0d4b610f63..d9ec167aae3 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -31,6 +31,24 @@ pub(crate) struct AutoDiffAttr {
     pub attr_span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(passes_loop_match_attr)]
+pub(crate) struct LoopMatchAttr {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub node_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_const_continue_attr)]
+pub(crate) struct ConstContinueAttr {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub node_span: Span,
+}
+
 #[derive(LintDiagnostic)]
 #[diag(passes_outer_crate_level_attr)]
 pub(crate) struct OuterCrateLevelAttr;
@@ -1071,17 +1089,6 @@ pub(crate) struct FeaturePreviouslyDeclared<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_naked_functions_incompatible_attribute, code = E0736)]
-pub(crate) struct NakedFunctionIncompatibleAttribute {
-    #[primary_span]
-    #[label]
-    pub span: Span,
-    #[label(passes_naked_attribute)]
-    pub naked_span: Span,
-    pub attr: String,
-}
-
-#[derive(Diagnostic)]
 #[diag(passes_attr_only_in_functions)]
 pub(crate) struct AttrOnlyInFunctions {
     #[primary_span]
@@ -1308,13 +1315,6 @@ pub(crate) enum AttrApplication {
         #[label]
         span: Span,
     },
-    #[diag(passes_attr_application_struct_enum_function_method_union, code = E0517)]
-    StructEnumFunctionMethodUnion {
-        #[primary_span]
-        hint_span: Span,
-        #[label]
-        span: Span,
-    },
 }
 
 #[derive(Diagnostic)]
@@ -1516,7 +1516,7 @@ pub(crate) struct EnumVariantSameName<'tcx> {
     #[primary_span]
     pub variant_span: Span,
     pub dead_name: Symbol,
-    pub descr: &'tcx str,
+    pub dead_descr: &'tcx str,
 }
 
 #[derive(Subdiagnostic)]
@@ -1714,6 +1714,7 @@ impl Subdiagnostic for UnusedVariableStringInterp {
 #[derive(LintDiagnostic)]
 #[diag(passes_unused_variable_try_ignore)]
 pub(crate) struct UnusedVarTryIgnore {
+    pub name: String,
     #[subdiagnostic]
     pub sugg: UnusedVarTryIgnoreSugg,
 }
@@ -1816,3 +1817,26 @@ pub(crate) enum UnexportableItem<'a> {
         field_name: &'a str,
     },
 }
+
+#[derive(Diagnostic)]
+#[diag(passes_repr_align_should_be_align)]
+pub(crate) struct ReprAlignShouldBeAlign {
+    #[primary_span]
+    #[help]
+    pub span: Span,
+    pub item: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_align_should_be_repr_align)]
+pub(crate) struct AlignShouldBeReprAlign {
+    #[primary_span]
+    #[suggestion(
+        style = "verbose",
+        applicability = "machine-applicable",
+        code = "#[repr(align({align_bytes}))]"
+    )]
+    pub span: Span,
+    pub item: &'static str,
+    pub align_bytes: u64,
+}
diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs
index 46e6c0bf7da..40bc18939d6 100644
--- a/compiler/rustc_passes/src/input_stats.rs
+++ b/compiler/rustc_passes/src/input_stats.rs
@@ -65,16 +65,16 @@ pub fn print_hir_stats(tcx: TyCtxt<'_>) {
         StatCollector { tcx: Some(tcx), nodes: FxHashMap::default(), seen: FxHashSet::default() };
     tcx.hir_walk_toplevel_module(&mut collector);
     tcx.hir_walk_attributes(&mut collector);
-    collector.print("HIR STATS", "hir-stats");
+    collector.print(tcx, "HIR STATS", "hir-stats");
 }
 
-pub fn print_ast_stats(krate: &ast::Crate, title: &str, prefix: &str) {
+pub fn print_ast_stats(tcx: TyCtxt<'_>, krate: &ast::Crate) {
     use rustc_ast::visit::Visitor;
 
     let mut collector =
         StatCollector { tcx: None, nodes: FxHashMap::default(), seen: FxHashSet::default() };
     collector.visit_crate(krate);
-    collector.print(title, prefix);
+    collector.print(tcx, "POST EXPANSION AST STATS", "ast-stats");
 }
 
 impl<'k> StatCollector<'k> {
@@ -116,29 +116,48 @@ impl<'k> StatCollector<'k> {
         }
     }
 
-    fn print(&self, title: &str, prefix: &str) {
+    fn print(&self, tcx: TyCtxt<'_>, title: &str, prefix: &str) {
+        use std::fmt::Write;
+
         // We will soon sort, so the initial order does not matter.
         #[allow(rustc::potential_query_instability)]
         let mut nodes: Vec<_> = self.nodes.iter().collect();
         nodes.sort_by_cached_key(|(label, node)| (node.stats.accum_size(), label.to_owned()));
+        nodes.reverse(); // bigger items first
+
+        let name_w = 18;
+        let acc_size1_w = 10;
+        let acc_size2_w = 8; // " (NN.N%)"
+        let acc_size_w = acc_size1_w + acc_size2_w;
+        let count_w = 14;
+        let item_size_w = 14;
+        let banner_w = name_w + acc_size_w + count_w + item_size_w;
 
         let total_size = nodes.iter().map(|(_, node)| node.stats.accum_size()).sum();
         let total_count = nodes.iter().map(|(_, node)| node.stats.count).sum();
 
-        eprintln!("{prefix} {title}");
-        eprintln!(
-            "{} {:<18}{:>18}{:>14}{:>14}",
-            prefix, "Name", "Accumulated Size", "Count", "Item Size"
+        // We write all the text into a string and print it with a single
+        // `eprint!`. This is an attempt to minimize interleaved text if multiple
+        // rustc processes are printing macro-stats at the same time (e.g. with
+        // `RUSTFLAGS='-Zinput-stats' cargo build`). It still doesn't guarantee
+        // non-interleaving, though.
+        let mut s = String::new();
+        _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w));
+        _ = writeln!(s, "{prefix} {title}: {}", tcx.crate_name(hir::def_id::LOCAL_CRATE));
+        _ = writeln!(
+            s,
+            "{prefix} {:<name_w$}{:>acc_size_w$}{:>count_w$}{:>item_size_w$}",
+            "Name", "Accumulated Size", "Count", "Item Size"
         );
-        eprintln!("{prefix} ----------------------------------------------------------------");
+        _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w));
 
         let percent = |m, n| (m * 100) as f64 / n as f64;
 
         for (label, node) in nodes {
             let size = node.stats.accum_size();
-            eprintln!(
-                "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}",
-                prefix,
+            _ = writeln!(
+                s,
+                "{prefix} {:<name_w$}{:>acc_size1_w$} ({:4.1}%){:>count_w$}{:>item_size_w$}",
                 label,
                 usize_with_underscores(size),
                 percent(size, total_size),
@@ -155,9 +174,9 @@ impl<'k> StatCollector<'k> {
 
                 for (label, subnode) in subnodes {
                     let size = subnode.accum_size();
-                    eprintln!(
-                        "{} - {:<18}{:>10} ({:4.1}%){:>14}",
-                        prefix,
+                    _ = writeln!(
+                        s,
+                        "{prefix} - {:<name_w$}{:>acc_size1_w$} ({:4.1}%){:>count_w$}",
                         label,
                         usize_with_underscores(size),
                         percent(size, total_size),
@@ -166,15 +185,17 @@ impl<'k> StatCollector<'k> {
                 }
             }
         }
-        eprintln!("{prefix} ----------------------------------------------------------------");
-        eprintln!(
-            "{} {:<18}{:>10}        {:>14}",
-            prefix,
+        _ = writeln!(s, "{prefix} {}", "-".repeat(banner_w));
+        _ = writeln!(
+            s,
+            "{prefix} {:<name_w$}{:>acc_size1_w$}{:>acc_size2_w$}{:>count_w$}",
             "Total",
             usize_with_underscores(total_size),
+            "",
             usize_with_underscores(total_count),
         );
-        eprintln!("{prefix}");
+        _ = writeln!(s, "{prefix} {}", "=".repeat(banner_w));
+        eprint!("{s}");
     }
 }
 
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 763d9fda804..3088e189c20 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -85,6 +85,7 @@ use std::io;
 use std::io::prelude::*;
 use std::rc::Rc;
 
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_hir as hir;
 use rustc_hir::def::*;
@@ -145,7 +146,7 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     }
 
     // Don't run unused pass for #[naked]
-    if tcx.has_attr(def_id.to_def_id(), sym::naked) {
+    if find_attr!(tcx.get_all_attrs(def_id.to_def_id()), AttributeKind::Naked(..)) {
         return;
     }
 
@@ -1743,6 +1744,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
                             .map(|(_, pat_span, _)| *pat_span)
                             .collect::<Vec<_>>(),
                         errors::UnusedVarTryIgnore {
+                            name: name.clone(),
                             sugg: errors::UnusedVarTryIgnoreSugg {
                                 shorthands,
                                 non_shorthands,
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index f7a4931c111..09685640e50 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -314,7 +314,8 @@ impl IntRange {
         IntRange { lo, hi }
     }
 
-    fn is_subrange(&self, other: &Self) -> bool {
+    #[inline]
+    pub fn is_subrange(&self, other: &Self) -> bool {
         other.lo <= self.lo && self.hi <= other.hi
     }
 
diff --git a/compiler/rustc_proc_macro/Cargo.toml b/compiler/rustc_proc_macro/Cargo.toml
index 4a7c0d78ede..748fa944e28 100644
--- a/compiler/rustc_proc_macro/Cargo.toml
+++ b/compiler/rustc_proc_macro/Cargo.toml
@@ -15,7 +15,7 @@ test = false
 doctest = false
 
 [dependencies]
-rustc-literal-escaper = "0.0.2"
+rustc-literal-escaper = "0.0.4"
 
 [features]
 rustc-dep-of-std = []
diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml
index 7db06953aeb..3d2d879a764 100644
--- a/compiler/rustc_query_system/Cargo.toml
+++ b/compiler/rustc_query_system/Cargo.toml
@@ -6,7 +6,6 @@ edition = "2024"
 [dependencies]
 # tidy-alphabetical-start
 parking_lot = "0.12"
-rustc-rayon-core = { version = "0.5.0" }
 rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
@@ -21,6 +20,7 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
+rustc_thread_pool = { path = "../rustc_thread_pool" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 1e79bd461d2..7e61f5026da 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -236,7 +236,7 @@ impl<I> QueryLatch<I> {
             // If this detects a deadlock and the deadlock handler wants to resume this thread
             // we have to be in the `wait` call. This is ensured by the deadlock handler
             // getting the self.info lock.
-            rayon_core::mark_blocked();
+            rustc_thread_pool::mark_blocked();
             let proxy = qcx.jobserver_proxy();
             proxy.release_thread();
             waiter.condvar.wait(&mut info);
@@ -251,9 +251,9 @@ impl<I> QueryLatch<I> {
         let mut info = self.info.lock();
         debug_assert!(!info.complete);
         info.complete = true;
-        let registry = rayon_core::Registry::current();
+        let registry = rustc_thread_pool::Registry::current();
         for waiter in info.waiters.drain(..) {
-            rayon_core::mark_unblocked(&registry);
+            rustc_thread_pool::mark_unblocked(&registry);
             waiter.condvar.notify_one();
         }
     }
@@ -507,7 +507,7 @@ fn remove_cycle<I: Clone>(
 /// all active queries for cycles before finally resuming all the waiters at once.
 pub fn break_query_cycles<I: Clone + Debug>(
     query_map: QueryMap<I>,
-    registry: &rayon_core::Registry,
+    registry: &rustc_thread_pool::Registry,
 ) {
     let mut wakelist = Vec::new();
     // It is OK per the comments:
@@ -543,7 +543,7 @@ pub fn break_query_cycles<I: Clone + Debug>(
     // we wake the threads up as otherwise Rayon could detect a deadlock if a thread we
     // resumed fell asleep and this thread had yet to mark the remaining threads as unblocked.
     for _ in 0..wakelist.len() {
-        rayon_core::mark_unblocked(registry);
+        rustc_thread_pool::mark_unblocked(registry);
     }
 
     for waiter in wakelist.into_iter() {
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index f8e0a6936a0..16852d1661e 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -3,7 +3,7 @@ use std::mem;
 use rustc_ast::visit::FnKind;
 use rustc_ast::*;
 use rustc_ast_pretty::pprust;
-use rustc_attr_parsing::{AttributeParser, OmitDoc};
+use rustc_attr_parsing::{AttributeParser, Early, OmitDoc};
 use rustc_expand::expand::AstFragment;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind};
@@ -128,7 +128,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
                 // FIXME(jdonszelmann) make one of these in the resolver?
                 // FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can.
                 // Does that prevents errors from happening? maybe
-                let mut parser = AttributeParser::new_early(
+                let mut parser = AttributeParser::<'_, Early>::new(
                     &self.resolver.tcx.sess,
                     self.resolver.tcx.features(),
                     Vec::new(),
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 9149974a617..8bca350c8ba 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -256,22 +256,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         };
 
         let label = match new_binding.is_import_user_facing() {
-            true => errors::NameDefinedMultipleTimeLabel::Reimported { span, name },
-            false => errors::NameDefinedMultipleTimeLabel::Redefined { span, name },
+            true => errors::NameDefinedMultipleTimeLabel::Reimported { span },
+            false => errors::NameDefinedMultipleTimeLabel::Redefined { span },
         };
 
         let old_binding_label =
             (!old_binding.span.is_dummy() && old_binding.span != span).then(|| {
                 let span = self.tcx.sess.source_map().guess_head_span(old_binding.span);
                 match old_binding.is_import_user_facing() {
-                    true => errors::NameDefinedMultipleTimeOldBindingLabel::Import {
-                        span,
-                        name,
-                        old_kind,
-                    },
+                    true => {
+                        errors::NameDefinedMultipleTimeOldBindingLabel::Import { span, old_kind }
+                    }
                     false => errors::NameDefinedMultipleTimeOldBindingLabel::Definition {
                         span,
-                        name,
                         old_kind,
                     },
                 }
@@ -281,6 +278,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             .dcx()
             .create_err(errors::NameDefinedMultipleTime {
                 span,
+                name,
                 descr: ns.descr(),
                 container,
                 label,
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index 7fe74378b67..6d3752c0c83 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -978,6 +978,7 @@ pub(crate) struct VariableNotInAllPatterns {
 pub(crate) struct NameDefinedMultipleTime {
     #[primary_span]
     pub(crate) span: Span,
+    pub(crate) name: Symbol,
     pub(crate) descr: &'static str,
     pub(crate) container: &'static str,
     #[subdiagnostic]
@@ -992,13 +993,11 @@ pub(crate) enum NameDefinedMultipleTimeLabel {
     Reimported {
         #[primary_span]
         span: Span,
-        name: Symbol,
     },
     #[label(resolve_name_defined_multiple_time_redefined)]
     Redefined {
         #[primary_span]
         span: Span,
-        name: Symbol,
     },
 }
 
@@ -1008,14 +1007,12 @@ pub(crate) enum NameDefinedMultipleTimeOldBindingLabel {
     Import {
         #[primary_span]
         span: Span,
-        name: Symbol,
         old_kind: &'static str,
     },
     #[label(resolve_name_defined_multiple_time_old_binding_definition)]
     Definition {
         #[primary_span]
         span: Span,
-        name: Symbol,
         old_kind: &'static str,
     },
 }
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index e989209e177..1b7a2c3bda0 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -608,7 +608,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             }
         }
 
-        self.throw_unresolved_import_error(errors, glob_error);
+        if !errors.is_empty() {
+            self.throw_unresolved_import_error(errors, glob_error);
+        }
     }
 
     pub(crate) fn check_hidden_glob_reexports(
@@ -688,14 +690,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false,
             _ => true,
         });
+        errors.retain(|(_import, err)| {
+            // If we've encountered something like `use _;`, we've already emitted an error stating
+            // that `_` is not a valid identifier, so we ignore that resolve error.
+            err.segment != Some(kw::Underscore)
+        });
+
         if errors.is_empty() {
+            self.tcx.dcx().delayed_bug("expected a parse or \"`_` can't be an identifier\" error");
             return;
         }
 
-        /// Upper limit on the number of `span_label` messages.
-        const MAX_LABEL_COUNT: usize = 10;
-
         let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect());
+
         let paths = errors
             .iter()
             .map(|(import, err)| {
@@ -715,6 +722,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             diag.note(note.clone());
         }
 
+        /// Upper limit on the number of `span_label` messages.
+        const MAX_LABEL_COUNT: usize = 10;
+
         for (import, err) in errors.into_iter().take(MAX_LABEL_COUNT) {
             if let Some(label) = err.label {
                 diag.span_label(err.span, label);
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 338d9edcd22..ac7bdda4195 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -869,11 +869,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
                     self.with_generic_param_rib(
                         &[],
                         RibKind::Normal,
-                        LifetimeRibKind::Generics {
-                            binder: ty.id,
-                            kind: LifetimeBinderKind::PolyTrait,
-                            span,
-                        },
+                        ty.id,
+                        LifetimeBinderKind::PolyTrait,
+                        span,
                         |this| this.visit_path(path),
                     );
                 } else {
@@ -907,11 +905,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
                 self.with_generic_param_rib(
                     &bare_fn.generic_params,
                     RibKind::Normal,
-                    LifetimeRibKind::Generics {
-                        binder: ty.id,
-                        kind: LifetimeBinderKind::BareFnType,
-                        span,
-                    },
+                    ty.id,
+                    LifetimeBinderKind::BareFnType,
+                    span,
                     |this| {
                         this.visit_generic_params(&bare_fn.generic_params, false);
                         this.with_lifetime_rib(
@@ -942,11 +938,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
                 self.with_generic_param_rib(
                     &unsafe_binder.generic_params,
                     RibKind::Normal,
-                    LifetimeRibKind::Generics {
-                        binder: ty.id,
-                        kind: LifetimeBinderKind::BareFnType,
-                        span,
-                    },
+                    ty.id,
+                    LifetimeBinderKind::BareFnType,
+                    span,
                     |this| {
                         this.visit_generic_params(&unsafe_binder.generic_params, false);
                         this.with_lifetime_rib(
@@ -995,11 +989,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
         self.with_generic_param_rib(
             &tref.bound_generic_params,
             RibKind::Normal,
-            LifetimeRibKind::Generics {
-                binder: tref.trait_ref.ref_id,
-                kind: LifetimeBinderKind::PolyTrait,
-                span,
-            },
+            tref.trait_ref.ref_id,
+            LifetimeBinderKind::PolyTrait,
+            span,
             |this| {
                 this.visit_generic_params(&tref.bound_generic_params, false);
                 this.smart_resolve_path(
@@ -1020,11 +1012,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::Item(HasGenericParams::Yes(generics.span), def_kind),
-                    LifetimeRibKind::Generics {
-                        binder: foreign_item.id,
-                        kind: LifetimeBinderKind::Item,
-                        span: generics.span,
-                    },
+                    foreign_item.id,
+                    LifetimeBinderKind::Item,
+                    generics.span,
                     |this| visit::walk_item(this, foreign_item),
                 );
             }
@@ -1032,11 +1022,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::Item(HasGenericParams::Yes(generics.span), def_kind),
-                    LifetimeRibKind::Generics {
-                        binder: foreign_item.id,
-                        kind: LifetimeBinderKind::Function,
-                        span: generics.span,
-                    },
+                    foreign_item.id,
+                    LifetimeBinderKind::Function,
+                    generics.span,
                     |this| visit::walk_item(this, foreign_item),
                 );
             }
@@ -1374,11 +1362,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
                 this.with_generic_param_rib(
                     bound_generic_params,
                     RibKind::Normal,
-                    LifetimeRibKind::Generics {
-                        binder: bounded_ty.id,
-                        kind: LifetimeBinderKind::WhereBound,
-                        span,
-                    },
+                    bounded_ty.id,
+                    LifetimeBinderKind::WhereBound,
+                    span,
                     |this| {
                         this.visit_generic_params(bound_generic_params, false);
                         this.visit_ty(bounded_ty);
@@ -1432,11 +1418,14 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
 
     fn visit_variant(&mut self, v: &'ast Variant) {
         self.resolve_doc_links(&v.attrs, MaybeExported::Ok(v.id));
-        visit::walk_variant(self, v)
-    }
-
-    fn visit_variant_discr(&mut self, discr: &'ast AnonConst) {
-        self.resolve_anon_const(discr, AnonConstKind::EnumDiscriminant);
+        self.visit_id(v.id);
+        walk_list!(self, visit_attribute, &v.attrs);
+        self.visit_vis(&v.vis);
+        self.visit_ident(&v.ident);
+        self.visit_variant_data(&v.data);
+        if let Some(discr) = &v.disr_expr {
+            self.resolve_anon_const(discr, AnonConstKind::EnumDiscriminant);
+        }
     }
 
     fn visit_field_def(&mut self, f: &'ast FieldDef) {
@@ -2555,11 +2544,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
             this.with_generic_param_rib(
                 &generics.params,
                 RibKind::Item(HasGenericParams::Yes(generics.span), kind),
-                LifetimeRibKind::Generics {
-                    binder: item.id,
-                    kind: LifetimeBinderKind::Item,
-                    span: generics.span,
-                },
+                item.id,
+                LifetimeBinderKind::Item,
+                generics.span,
                 |this| {
                     let item_def_id = this.r.local_def_id(item.id).to_def_id();
                     this.with_self_rib(
@@ -2632,11 +2619,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::Item(HasGenericParams::Yes(generics.span), def_kind),
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        kind: LifetimeBinderKind::Item,
-                        span: generics.span,
-                    },
+                    item.id,
+                    LifetimeBinderKind::Item,
+                    generics.span,
                     |this| visit::walk_item(this, item),
                 );
             }
@@ -2645,11 +2630,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::Item(HasGenericParams::Yes(generics.span), def_kind),
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        kind: LifetimeBinderKind::Function,
-                        span: generics.span,
-                    },
+                    item.id,
+                    LifetimeBinderKind::Function,
+                    generics.span,
                     |this| visit::walk_item(this, item),
                 );
                 self.resolve_define_opaques(define_opaque);
@@ -2685,11 +2668,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::Item(HasGenericParams::Yes(generics.span), def_kind),
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        kind: LifetimeBinderKind::Item,
-                        span: generics.span,
-                    },
+                    item.id,
+                    LifetimeBinderKind::Item,
+                    generics.span,
                     |this| {
                         let local_def_id = this.r.local_def_id(item.id).to_def_id();
                         this.with_self_rib(Res::SelfTyParam { trait_: local_def_id }, |this| {
@@ -2706,11 +2687,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::Item(HasGenericParams::Yes(generics.span), def_kind),
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        kind: LifetimeBinderKind::Item,
-                        span: generics.span,
-                    },
+                    item.id,
+                    LifetimeBinderKind::Item,
+                    generics.span,
                     |this| {
                         let local_def_id = this.r.local_def_id(item.id).to_def_id();
                         this.with_self_rib(Res::SelfTyParam { trait_: local_def_id }, |this| {
@@ -2776,11 +2755,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                         },
                         def_kind,
                     ),
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        kind: LifetimeBinderKind::ConstItem,
-                        span: generics.span,
-                    },
+                    item.id,
+                    LifetimeBinderKind::ConstItem,
+                    generics.span,
                     |this| {
                         this.visit_generics(generics);
 
@@ -2825,11 +2802,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &[],
                     RibKind::Item(HasGenericParams::Yes(span), def_kind),
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        kind: LifetimeBinderKind::Function,
-                        span,
-                    },
+                    item.id,
+                    LifetimeBinderKind::Function,
+                    span,
                     |this| this.resolve_delegation(delegation),
                 );
             }
@@ -2846,17 +2821,16 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         &'c mut self,
         params: &'c [GenericParam],
         kind: RibKind<'ra>,
-        lifetime_kind: LifetimeRibKind,
+        binder: NodeId,
+        generics_kind: LifetimeBinderKind,
+        generics_span: Span,
         f: F,
     ) where
         F: FnOnce(&mut Self),
     {
         debug!("with_generic_param_rib");
-        let LifetimeRibKind::Generics { binder, span: generics_span, kind: generics_kind, .. } =
-            lifetime_kind
-        else {
-            panic!()
-        };
+        let lifetime_kind =
+            LifetimeRibKind::Generics { binder, span: generics_span, kind: generics_kind };
 
         let mut function_type_rib = Rib::new(kind);
         let mut function_value_rib = Rib::new(kind);
@@ -3086,7 +3060,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 this.with_generic_param_rib(
                     &generics.params,
                     RibKind::AssocItem,
-                    LifetimeRibKind::Generics { binder: item.id, span: generics.span, kind },
+                    item.id,
+                    kind,
+                    generics.span,
                     |this| visit::walk_assoc_item(this, item, AssocCtxt::Trait),
                 );
             };
@@ -3104,11 +3080,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                     self.with_generic_param_rib(
                         &generics.params,
                         RibKind::AssocItem,
-                        LifetimeRibKind::Generics {
-                            binder: item.id,
-                            span: generics.span,
-                            kind: LifetimeBinderKind::ConstItem,
-                        },
+                        item.id,
+                        LifetimeBinderKind::ConstItem,
+                        generics.span,
                         |this| {
                             this.with_lifetime_rib(
                                 LifetimeRibKind::StaticIfNoLifetimeInScope {
@@ -3145,11 +3119,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                     self.with_generic_param_rib(
                         &[],
                         RibKind::AssocItem,
-                        LifetimeRibKind::Generics {
-                            binder: item.id,
-                            kind: LifetimeBinderKind::Function,
-                            span: delegation.path.segments.last().unwrap().ident.span,
-                        },
+                        item.id,
+                        LifetimeBinderKind::Function,
+                        delegation.path.segments.last().unwrap().ident.span,
                         |this| this.resolve_delegation(delegation),
                     );
                 }
@@ -3227,11 +3199,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         self.with_generic_param_rib(
             &generics.params,
             RibKind::Item(HasGenericParams::Yes(generics.span), self.r.local_def_kind(item_id)),
-            LifetimeRibKind::Generics {
-                span: generics.span,
-                binder: item_id,
-                kind: LifetimeBinderKind::ImplBlock,
-            },
+            item_id,
+            LifetimeBinderKind::ImplBlock,
+            generics.span,
             |this| {
                 // Dummy self type for better errors if `Self` is used in the trait path.
                 this.with_self_rib(Res::SelfTyParam { trait_: LOCAL_CRATE.as_def_id() }, |this| {
@@ -3316,15 +3286,14 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::AssocItem,
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        span: generics.span,
-                        kind: LifetimeBinderKind::ConstItem,
-                    },
+                    item.id,
+                    LifetimeBinderKind::ConstItem,
+                    generics.span,
                     |this| {
                         this.with_lifetime_rib(
-                            // Until these are a hard error, we need to create them within the correct binder,
-                            // Otherwise the lifetimes of this assoc const think they are lifetimes of the trait.
+                            // Until these are a hard error, we need to create them within the
+                            // correct binder, Otherwise the lifetimes of this assoc const think
+                            // they are lifetimes of the trait.
                             LifetimeRibKind::AnonymousCreateParameter {
                                 binder: item.id,
                                 report_in_path: true,
@@ -3373,11 +3342,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::AssocItem,
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        span: generics.span,
-                        kind: LifetimeBinderKind::Function,
-                    },
+                    item.id,
+                    LifetimeBinderKind::Function,
+                    generics.span,
                     |this| {
                         // If this is a trait impl, ensure the method
                         // exists in trait
@@ -3404,11 +3371,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::AssocItem,
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        span: generics.span,
-                        kind: LifetimeBinderKind::Item,
-                    },
+                    item.id,
+                    LifetimeBinderKind::Item,
+                    generics.span,
                     |this| {
                         this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
                             // If this is a trait impl, ensure the type
@@ -3434,11 +3399,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &[],
                     RibKind::AssocItem,
-                    LifetimeRibKind::Generics {
-                        binder: item.id,
-                        kind: LifetimeBinderKind::Function,
-                        span: delegation.path.segments.last().unwrap().ident.span,
-                    },
+                    item.id,
+                    LifetimeBinderKind::Function,
+                    delegation.path.segments.last().unwrap().ident.span,
                     |this| {
                         this.check_trait_item(
                             item.id,
@@ -4951,11 +4914,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     generic_params,
                     RibKind::Normal,
-                    LifetimeRibKind::Generics {
-                        binder: expr.id,
-                        kind: LifetimeBinderKind::Closure,
-                        span,
-                    },
+                    expr.id,
+                    LifetimeBinderKind::Closure,
+                    span,
                     |this| visit::walk_expr(this, expr),
                 );
             }
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index fa839d2748d..931c6241bf2 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -356,7 +356,12 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGen
 /// If there are no doc-comments, return true.
 /// FIXME(#78591): Support both inner and outer attributes on the same item.
 pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool {
-    attrs.iter().find(|a| a.doc_str().is_some()).is_none_or(|a| a.style() == ast::AttrStyle::Inner)
+    for attr in attrs {
+        if let Some(attr_style) = attr.doc_resolution_scope() {
+            return attr_style == ast::AttrStyle::Inner;
+        }
+    }
+    true
 }
 
 /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`.
diff --git a/compiler/rustc_serialize/src/int_overflow.rs b/compiler/rustc_serialize/src/int_overflow.rs
index f2aac2ef711..6782fbc33da 100644
--- a/compiler/rustc_serialize/src/int_overflow.rs
+++ b/compiler/rustc_serialize/src/int_overflow.rs
@@ -20,6 +20,7 @@ macro_rules! impl_debug_strict_add {
     ($( $ty:ty )*) => {
         $(
             impl DebugStrictAdd for $ty {
+                #[inline]
                 fn debug_strict_add(self, other: Self) -> Self {
                     if cfg!(debug_assertions) {
                         self + other
@@ -42,6 +43,7 @@ macro_rules! impl_debug_strict_sub {
     ($( $ty:ty )*) => {
         $(
             impl DebugStrictSub for $ty {
+                #[inline]
                 fn debug_strict_sub(self, other: Self) -> Self {
                     if cfg!(debug_assertions) {
                         self - other
diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs
index 00bad8e70cf..4242642c664 100644
--- a/compiler/rustc_serialize/src/opaque.rs
+++ b/compiler/rustc_serialize/src/opaque.rs
@@ -89,10 +89,12 @@ impl FileEncoder {
         self.buffered = 0;
     }
 
+    #[inline]
     pub fn file(&self) -> &File {
         &self.file
     }
 
+    #[inline]
     pub fn path(&self) -> &Path {
         &self.path
     }
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index f0ee19e3c67..5b88a7017c5 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -22,7 +22,6 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-smallvec = "1.8.1"
 termize = "0.1.1"
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 61953614c77..528c52eace7 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -40,11 +40,6 @@ session_file_is_not_writeable = output file {$file} is not writeable -- check it
 
 session_file_write_fail = failed to write `{$path}` due to error `{$err}`
 
-session_forbidden_ctarget_feature =
-    target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
-    .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-session_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
-
 session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64
 
 session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models
@@ -137,9 +132,6 @@ session_target_stack_protector_not_supported = `-Z stack-protector={$stack_prote
 session_unleashed_feature_help_named = skipping check for `{$gate}` feature
 session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate
 
-session_unstable_ctarget_feature =
-    unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = this feature is not stably supported; its behavior can change in the future
 session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto`
 
 session_unsupported_crate_type_for_target =
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 87e4b0a17aa..73bb0471c22 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -227,13 +227,15 @@ pub enum CoverageLevel {
 }
 
 /// The different settings that the `-Z autodiff` flag can have.
-#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+#[derive(Clone, PartialEq, Hash, Debug)]
 pub enum AutoDiff {
     /// Enable the autodiff opt pipeline
     Enable,
 
     /// Print TypeAnalysis information
     PrintTA,
+    /// Print TypeAnalysis information for a specific function
+    PrintTAFn(String),
     /// Print ActivityAnalysis Information
     PrintAA,
     /// Print Performance Warnings from Enzyme
@@ -1296,6 +1298,28 @@ bitflags::bitflags! {
     }
 }
 
+#[derive(Clone, Debug)]
+pub struct Sysroot {
+    pub explicit: Option<PathBuf>,
+    pub default: PathBuf,
+}
+
+impl Sysroot {
+    pub fn new(explicit: Option<PathBuf>) -> Sysroot {
+        Sysroot { explicit, default: filesearch::default_sysroot() }
+    }
+
+    /// Return explicit sysroot if it was passed with `--sysroot`, or default sysroot otherwise.
+    pub fn path(&self) -> &Path {
+        self.explicit.as_deref().unwrap_or(&self.default)
+    }
+
+    /// Returns both explicit sysroot if it was passed with `--sysroot` and the default sysroot.
+    pub fn all_paths(&self) -> impl Iterator<Item = &Path> {
+        self.explicit.as_deref().into_iter().chain(iter::once(&*self.default))
+    }
+}
+
 pub fn host_tuple() -> &'static str {
     // Get the host triple out of the build environment. This ensures that our
     // idea of the host triple is the same as for the set of libraries we've
@@ -1342,7 +1366,7 @@ impl Default for Options {
             describe_lints: false,
             output_types: OutputTypes(BTreeMap::new()),
             search_paths: vec![],
-            sysroot: filesearch::materialize_sysroot(None),
+            sysroot: Sysroot::new(None),
             target_triple: TargetTuple::from_tuple(host_tuple()),
             test: false,
             incremental: None,
@@ -2673,7 +2697,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
     let cg = cg;
 
-    let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
     let target_triple = parse_target_triple(early_dcx, matches);
     let opt_level = parse_opt_level(early_dcx, matches, &cg);
     // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
@@ -2712,10 +2735,10 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
     let logical_env = parse_logical_env(early_dcx, matches);
 
-    let sysroot = filesearch::materialize_sysroot(sysroot_opt);
+    let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from));
 
     let real_source_base_dir = |suffix: &str, confirm: &str| {
-        let mut candidate = sysroot.join(suffix);
+        let mut candidate = sysroot.path().join(suffix);
         if let Ok(metadata) = candidate.symlink_metadata() {
             // Replace the symlink bootstrap creates, with its destination.
             // We could try to use `fs::canonicalize` instead, but that might
@@ -2742,7 +2765,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     let mut search_paths = vec![];
     for s in &matches.opt_strs("L") {
         search_paths.push(SearchPath::from_cli_opt(
-            &sysroot,
+            sysroot.path(),
             &target_triple,
             early_dcx,
             s,
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 9c591dcf619..bf95014843d 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -501,20 +501,3 @@ pub(crate) struct SoftFloatIgnored;
 #[note]
 #[note(session_soft_float_deprecated_issue)]
 pub(crate) struct SoftFloatDeprecated;
-
-#[derive(Diagnostic)]
-#[diag(session_forbidden_ctarget_feature)]
-#[note]
-#[note(session_forbidden_ctarget_feature_issue)]
-pub(crate) struct ForbiddenCTargetFeature<'a> {
-    pub feature: &'a str,
-    pub enabled: &'a str,
-    pub reason: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(session_unstable_ctarget_feature)]
-#[note]
-pub(crate) struct UnstableCTargetFeature<'a> {
-    pub feature: &'a str,
-}
diff --git a/compiler/rustc_session/src/features.rs b/compiler/rustc_session/src/features.rs
deleted file mode 100644
index 70a088a236f..00000000000
--- a/compiler/rustc_session/src/features.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use rustc_target::target_features::Stability;
-
-use crate::Session;
-use crate::errors::{ForbiddenCTargetFeature, UnstableCTargetFeature};
-
-pub trait StabilityExt {
-    /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
-    /// Otherwise, some features also may only be enabled by flag (target modifier).
-    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
-    /// `requires_nightly`.)
-    fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str>;
-
-    /// Check that feature is correctly enabled/disabled by command line flag (emits warnings)
-    fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str);
-}
-
-impl StabilityExt for Stability {
-    fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str> {
-        match self {
-            Stability::Forbidden { reason } => Err(reason),
-            Stability::TargetModifierOnly { reason, flag } => {
-                if !sess.opts.target_feature_flag_enabled(*flag) { Err(reason) } else { Ok(()) }
-            }
-            _ => Ok(()),
-        }
-    }
-    fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str) {
-        if let Err(reason) = self.is_toggle_permitted(sess) {
-            sess.dcx().emit_warn(ForbiddenCTargetFeature {
-                feature,
-                enabled: if enable { "enabled" } else { "disabled" },
-                reason,
-            });
-        } else if self.requires_nightly().is_some() {
-            // An unstable feature. Warn about using it. It makes little sense
-            // to hard-error here since we just warn about fully unknown
-            // features above.
-            sess.dcx().emit_warn(UnstableCTargetFeature { feature });
-        }
-    }
-}
-
-pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<&str>) {
-    // -Zretpoline without -Zretpoline-external-thunk enables
-    // retpoline-indirect-branches and retpoline-indirect-calls target features
-    let unstable_opts = &sess.opts.unstable_opts;
-    if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
-        features.push("+retpoline-indirect-branches");
-        features.push("+retpoline-indirect-calls");
-    }
-    // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables
-    // retpoline-external-thunk, retpoline-indirect-branches and
-    // retpoline-indirect-calls target features
-    if unstable_opts.retpoline_external_thunk {
-        features.push("+retpoline-external-thunk");
-        features.push("+retpoline-indirect-branches");
-        features.push("+retpoline-indirect-calls");
-    }
-}
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
index 4f8c3926207..f64fa86948c 100644
--- a/compiler/rustc_session/src/filesearch.rs
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -5,7 +5,6 @@ use std::{env, fs};
 
 use rustc_fs_util::try_canonicalize;
 use rustc_target::spec::Target;
-use smallvec::{SmallVec, smallvec};
 
 use crate::search_paths::{PathKind, SearchPath};
 
@@ -182,24 +181,9 @@ fn current_dll_path() -> Result<PathBuf, String> {
     Err("current_dll_path is not supported on WASI".to_string())
 }
 
-pub fn sysroot_with_fallback(sysroot: &Path) -> SmallVec<[PathBuf; 2]> {
-    let mut candidates = smallvec![sysroot.to_owned()];
-    let default_sysroot = get_or_default_sysroot();
-    if default_sysroot != sysroot {
-        candidates.push(default_sysroot);
-    }
-    candidates
-}
-
-/// Returns the provided sysroot or calls [`get_or_default_sysroot`] if it's none.
-/// Panics if [`get_or_default_sysroot`]  returns an error.
-pub fn materialize_sysroot(maybe_sysroot: Option<PathBuf>) -> PathBuf {
-    maybe_sysroot.unwrap_or_else(|| get_or_default_sysroot())
-}
-
 /// This function checks if sysroot is found using env::args().next(), and if it
 /// is not found, finds sysroot from current rustc_driver dll.
-pub fn get_or_default_sysroot() -> PathBuf {
+pub(crate) fn default_sysroot() -> PathBuf {
     fn default_from_rustc_driver_dll() -> Result<PathBuf, String> {
         let dll = current_dll_path()?;
 
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 4added19e56..5e5872ee068 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -29,7 +29,6 @@ pub use session::*;
 pub mod output;
 
 pub use getopts;
-pub mod features;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 31b4237d3b2..ecd82c0cc01 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -290,14 +290,6 @@ macro_rules! top_level_options {
                 mods.sort_by(|a, b| a.opt.cmp(&b.opt));
                 mods
             }
-
-            pub fn target_feature_flag_enabled(&self, flag: &str) -> bool {
-                match flag {
-                    "retpoline" => self.unstable_opts.retpoline,
-                    "retpoline-external-thunk" => self.unstable_opts.retpoline_external_thunk,
-                    _ => false,
-                }
-            }
         }
     );
 }
@@ -345,7 +337,7 @@ top_level_options!(
         output_types: OutputTypes [TRACKED],
         search_paths: Vec<SearchPath> [UNTRACKED],
         libs: Vec<NativeLib> [TRACKED],
-        sysroot: PathBuf [UNTRACKED],
+        sysroot: Sysroot [UNTRACKED],
 
         target_triple: TargetTuple [TRACKED],
 
@@ -733,7 +725,7 @@ mod desc {
     pub(crate) const parse_list: &str = "a space-separated list of strings";
     pub(crate) const parse_list_with_polarity: &str =
         "a comma-separated list of strings, with elements beginning with + or -";
-    pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`";
+    pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintTAFn`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`";
     pub(crate) const parse_comma_list: &str = "a comma-separated list of strings";
     pub(crate) const parse_opt_comma_list: &str = parse_comma_list;
     pub(crate) const parse_number: &str = "a number";
@@ -1373,9 +1365,22 @@ pub mod parse {
         let mut v: Vec<&str> = v.split(",").collect();
         v.sort_unstable();
         for &val in v.iter() {
-            let variant = match val {
+            // Split each entry on '=' if it has an argument
+            let (key, arg) = match val.split_once('=') {
+                Some((k, a)) => (k, Some(a)),
+                None => (val, None),
+            };
+
+            let variant = match key {
                 "Enable" => AutoDiff::Enable,
                 "PrintTA" => AutoDiff::PrintTA,
+                "PrintTAFn" => {
+                    if let Some(fun) = arg {
+                        AutoDiff::PrintTAFn(fun.to_string())
+                    } else {
+                        return false;
+                    }
+                }
                 "PrintAA" => AutoDiff::PrintAA,
                 "PrintPerf" => AutoDiff::PrintPerf,
                 "PrintSteps" => AutoDiff::PrintSteps,
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 87c848cf857..0118cdb1fc2 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -8,10 +8,11 @@ use rustc_ast::attr::AttrIdGenerator;
 use rustc_ast::node_id::NodeId;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
 use rustc_data_structures::sync::{AppendOnlyVec, Lock};
-use rustc_errors::emitter::{HumanEmitter, SilentEmitter, stderr_destination};
+use rustc_errors::emitter::{FatalOnlyEmitter, HumanEmitter, stderr_destination};
+use rustc_errors::translation::Translator;
 use rustc_errors::{
     ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan,
-    StashKey, fallback_fluent_bundle,
+    StashKey,
 };
 use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue};
 use rustc_span::edition::Edition;
@@ -242,10 +243,10 @@ pub struct ParseSess {
 impl ParseSess {
     /// Used for testing.
     pub fn new(locale_resources: Vec<&'static str>) -> Self {
-        let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
+        let translator = Translator::with_fallback_bundle(locale_resources, false);
         let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
         let emitter = Box::new(
-            HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)
+            HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator)
                 .sm(Some(Arc::clone(&sm))),
         );
         let dcx = DiagCtxt::new(emitter);
@@ -274,19 +275,14 @@ impl ParseSess {
         }
     }
 
-    pub fn with_silent_emitter(
-        locale_resources: Vec<&'static str>,
-        fatal_note: String,
-        emit_fatal_diagnostic: bool,
-    ) -> Self {
-        let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
+    pub fn with_fatal_emitter(locale_resources: Vec<&'static str>, fatal_note: String) -> Self {
+        let translator = Translator::with_fallback_bundle(locale_resources, false);
         let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
         let fatal_emitter =
-            Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle));
-        let dcx = DiagCtxt::new(Box::new(SilentEmitter {
+            Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator));
+        let dcx = DiagCtxt::new(Box::new(FatalOnlyEmitter {
             fatal_emitter,
             fatal_note: Some(fatal_note),
-            emit_fatal_diagnostic,
         }))
         .disable_warnings();
         ParseSess::with_dcx(dcx, sm)
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index ca42c5a4256..bad2581ae31 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -19,9 +19,10 @@ use rustc_errors::emitter::{
 };
 use rustc_errors::json::JsonEmitter;
 use rustc_errors::timings::TimingSectionHandler;
+use rustc_errors::translation::Translator;
 use rustc_errors::{
     Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort,
-    FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle,
+    TerminalUrl, fallback_fluent_bundle,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -148,7 +149,6 @@ pub struct Session {
     pub opts: config::Options,
     pub target_tlib_path: Arc<SearchPath>,
     pub psess: ParseSess,
-    pub sysroot: PathBuf,
     /// Input, input file path and output file path to this compilation process.
     pub io: CompilerIO,
 
@@ -455,8 +455,10 @@ impl Session {
     /// directories are also returned, for example if `--sysroot` is used but tools are missing
     /// (#125246): we also add the bin directories to the sysroot where rustc is located.
     pub fn get_tools_search_paths(&self, self_contained: bool) -> Vec<PathBuf> {
-        let search_paths = filesearch::sysroot_with_fallback(&self.sysroot)
-            .into_iter()
+        let search_paths = self
+            .opts
+            .sysroot
+            .all_paths()
             .map(|sysroot| filesearch::make_target_bin_path(&sysroot, config::host_tuple()));
 
         if self_contained {
@@ -948,8 +950,7 @@ impl Session {
 fn default_emitter(
     sopts: &config::Options,
     source_map: Arc<SourceMap>,
-    bundle: Option<Arc<FluentBundle>>,
-    fallback_bundle: LazyFallbackBundle,
+    translator: Translator,
 ) -> Box<DynEmitter> {
     let macro_backtrace = sopts.unstable_opts.macro_backtrace;
     let track_diagnostics = sopts.unstable_opts.track_diagnostics;
@@ -974,17 +975,11 @@ fn default_emitter(
             let short = kind.short();
 
             if let HumanReadableErrorType::AnnotateSnippet = kind {
-                let emitter = AnnotateSnippetEmitter::new(
-                    source_map,
-                    bundle,
-                    fallback_bundle,
-                    short,
-                    macro_backtrace,
-                );
+                let emitter =
+                    AnnotateSnippetEmitter::new(source_map, translator, short, macro_backtrace);
                 Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
             } else {
-                let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
-                    .fluent_bundle(bundle)
+                let emitter = HumanEmitter::new(stderr_destination(color_config), translator)
                     .sm(source_map)
                     .short_message(short)
                     .diagnostic_width(sopts.diagnostic_width)
@@ -1006,12 +1001,11 @@ fn default_emitter(
             JsonEmitter::new(
                 Box::new(io::BufWriter::new(io::stderr())),
                 source_map,
-                fallback_bundle,
+                translator,
                 pretty,
                 json_rendered,
                 color_config,
             )
-            .fluent_bundle(bundle)
             .ui_testing(sopts.unstable_opts.ui_testing)
             .ignored_directories_in_source_blocks(
                 sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(),
@@ -1030,12 +1024,11 @@ fn default_emitter(
 pub fn build_session(
     sopts: config::Options,
     io: CompilerIO,
-    bundle: Option<Arc<rustc_errors::FluentBundle>>,
+    fluent_bundle: Option<Arc<rustc_errors::FluentBundle>>,
     registry: rustc_errors::registry::Registry,
     fluent_resources: Vec<&'static str>,
     driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
     target: Target,
-    sysroot: PathBuf,
     cfg_version: &'static str,
     ice_file: Option<PathBuf>,
     using_internal_features: &'static AtomicBool,
@@ -1052,12 +1045,15 @@ pub fn build_session(
     let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow);
     let can_emit_warnings = !(warnings_allow || cap_lints_allow);
 
-    let fallback_bundle = fallback_fluent_bundle(
-        fluent_resources,
-        sopts.unstable_opts.translate_directionality_markers,
-    );
+    let translator = Translator {
+        fluent_bundle,
+        fallback_fluent_bundle: fallback_fluent_bundle(
+            fluent_resources,
+            sopts.unstable_opts.translate_directionality_markers,
+        ),
+    };
     let source_map = rustc_span::source_map::get_source_map().unwrap();
-    let emitter = default_emitter(&sopts, Arc::clone(&source_map), bundle, fallback_bundle);
+    let emitter = default_emitter(&sopts, Arc::clone(&source_map), translator);
 
     let mut dcx = DiagCtxt::new(emitter)
         .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings))
@@ -1067,7 +1063,7 @@ pub fn build_session(
     }
 
     let host_triple = TargetTuple::from_tuple(config::host_tuple());
-    let (host, target_warnings) = Target::search(&host_triple, &sysroot)
+    let (host, target_warnings) = Target::search(&host_triple, sopts.sysroot.path())
         .unwrap_or_else(|e| dcx.handle().fatal(format!("Error loading host specification: {e}")));
     for warning in target_warnings.warning_messages() {
         dcx.handle().warn(warning)
@@ -1100,13 +1096,14 @@ pub fn build_session(
     let host_triple = config::host_tuple();
     let target_triple = sopts.target_triple.tuple();
     // FIXME use host sysroot?
-    let host_tlib_path = Arc::new(SearchPath::from_sysroot_and_triple(&sysroot, host_triple));
+    let host_tlib_path =
+        Arc::new(SearchPath::from_sysroot_and_triple(sopts.sysroot.path(), host_triple));
     let target_tlib_path = if host_triple == target_triple {
         // Use the same `SearchPath` if host and target triple are identical to avoid unnecessary
         // rescanning of the target lib path and an unnecessary allocation.
         Arc::clone(&host_tlib_path)
     } else {
-        Arc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple))
+        Arc::new(SearchPath::from_sysroot_and_triple(sopts.sysroot.path(), target_triple))
     };
 
     let prof = SelfProfilerRef::new(
@@ -1138,7 +1135,6 @@ pub fn build_session(
         opts: sopts,
         target_tlib_path,
         psess,
-        sysroot,
         io,
         incr_comp_session: RwLock::new(IncrCompSession::NotInitialized),
         prof,
@@ -1500,13 +1496,13 @@ impl EarlyDiagCtxt {
 fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
     // FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will
     // need to reference every crate that might emit an early error for translation to work.
-    let fallback_bundle =
-        fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false);
+    let translator =
+        Translator::with_fallback_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false);
     let emitter: Box<DynEmitter> = match output {
         config::ErrorOutputType::HumanReadable { kind, color_config } => {
             let short = kind.short();
             Box::new(
-                HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
+                HumanEmitter::new(stderr_destination(color_config), translator)
                     .theme(if let HumanReadableErrorType::Unicode = kind {
                         OutputTheme::Unicode
                     } else {
@@ -1519,7 +1515,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
             Box::new(JsonEmitter::new(
                 Box::new(io::BufWriter::new(io::stderr())),
                 Some(Arc::new(SourceMap::new(FilePathMapping::empty()))),
-                fallback_bundle,
+                translator,
                 pretty,
                 json_rendered,
                 color_config,
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index a4c6f186222..24351eee1c4 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -479,6 +479,8 @@ impl RustcInternal for Abi {
             Abi::Vectorcall { unwind } => rustc_abi::ExternAbi::Vectorcall { unwind },
             Abi::Thiscall { unwind } => rustc_abi::ExternAbi::Thiscall { unwind },
             Abi::Aapcs { unwind } => rustc_abi::ExternAbi::Aapcs { unwind },
+            Abi::CCmseNonSecureCall => rustc_abi::ExternAbi::CmseNonSecureCall,
+            Abi::CCmseNonSecureEntry => rustc_abi::ExternAbi::CmseNonSecureEntry,
             Abi::Win64 { unwind } => rustc_abi::ExternAbi::Win64 { unwind },
             Abi::SysV64 { unwind } => rustc_abi::ExternAbi::SysV64 { unwind },
             Abi::PtxKernel => rustc_abi::ExternAbi::PtxKernel,
@@ -488,12 +490,11 @@ impl RustcInternal for Abi {
             Abi::EfiApi => rustc_abi::ExternAbi::EfiApi,
             Abi::AvrInterrupt => rustc_abi::ExternAbi::AvrInterrupt,
             Abi::AvrNonBlockingInterrupt => rustc_abi::ExternAbi::AvrNonBlockingInterrupt,
-            Abi::CCmseNonSecureCall => rustc_abi::ExternAbi::CCmseNonSecureCall,
-            Abi::CCmseNonSecureEntry => rustc_abi::ExternAbi::CCmseNonSecureEntry,
             Abi::System { unwind } => rustc_abi::ExternAbi::System { unwind },
             Abi::RustCall => rustc_abi::ExternAbi::RustCall,
             Abi::Unadjusted => rustc_abi::ExternAbi::Unadjusted,
             Abi::RustCold => rustc_abi::ExternAbi::RustCold,
+            Abi::RustInvalid => rustc_abi::ExternAbi::RustInvalid,
             Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM,
             Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS,
             Abi::Custom => rustc_abi::ExternAbi::Custom,
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index baa4c0681e8..3fa83cfc6a0 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -130,7 +130,11 @@ impl<'tcx> SmirCtxt<'tcx> {
 
     pub fn all_trait_decls(&self) -> stable_mir::TraitDecls {
         let mut tables = self.0.borrow_mut();
-        tables.tcx.all_traits().map(|trait_def_id| tables.trait_def(trait_def_id)).collect()
+        tables
+            .tcx
+            .all_traits_including_private()
+            .map(|trait_def_id| tables.trait_def(trait_def_id))
+            .collect()
     }
 
     pub fn trait_decls(&self, crate_num: CrateNum) -> stable_mir::TraitDecls {
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index b4239ddd896..7abec488151 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -871,12 +871,13 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
             ExternAbi::EfiApi => Abi::EfiApi,
             ExternAbi::AvrInterrupt => Abi::AvrInterrupt,
             ExternAbi::AvrNonBlockingInterrupt => Abi::AvrNonBlockingInterrupt,
-            ExternAbi::CCmseNonSecureCall => Abi::CCmseNonSecureCall,
-            ExternAbi::CCmseNonSecureEntry => Abi::CCmseNonSecureEntry,
+            ExternAbi::CmseNonSecureCall => Abi::CCmseNonSecureCall,
+            ExternAbi::CmseNonSecureEntry => Abi::CCmseNonSecureEntry,
             ExternAbi::System { unwind } => Abi::System { unwind },
             ExternAbi::RustCall => Abi::RustCall,
             ExternAbi::Unadjusted => Abi::Unadjusted,
             ExternAbi::RustCold => Abi::RustCold,
+            ExternAbi::RustInvalid => Abi::RustInvalid,
             ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM,
             ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS,
             ExternAbi::Custom => Abi::Custom,
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index 4415cd6e2e3..1ae79491642 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -1126,6 +1126,7 @@ pub enum Abi {
     RustCold,
     RiscvInterruptM,
     RiscvInterruptS,
+    RustInvalid,
     Custom,
 }
 
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index 315dedec107..29be3b73ee9 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -1213,6 +1213,17 @@ pub enum DesugaringKind {
     Contract,
     /// A pattern type range start/end
     PatTyRange,
+    /// A format literal.
+    FormatLiteral {
+        /// Was this format literal written in the source?
+        /// - `format!("boo")` => Yes,
+        /// - `format!(concat!("b", "o", "o"))` => No,
+        /// - `format!(include_str!("boo.txt"))` => No,
+        ///
+        /// If it wasn't written in the source then we have to be careful with suggestions about
+        /// rewriting it.
+        source: bool,
+    },
 }
 
 impl DesugaringKind {
@@ -1231,6 +1242,10 @@ impl DesugaringKind {
             DesugaringKind::BoundModifier => "trait bound modifier",
             DesugaringKind::Contract => "contract check",
             DesugaringKind::PatTyRange => "pattern type",
+            DesugaringKind::FormatLiteral { source: true } => "format string literal",
+            DesugaringKind::FormatLiteral { source: false } => {
+                "expression that expanded into a format string literal"
+            }
         }
     }
 
@@ -1250,6 +1265,7 @@ impl DesugaringKind {
             DesugaringKind::BoundModifier => value == "BoundModifier",
             DesugaringKind::Contract => value == "Contract",
             DesugaringKind::PatTyRange => value == "PatTyRange",
+            DesugaringKind::FormatLiteral { .. } => value == "FormatLiteral",
         }
     }
 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 812a254990c..11463ad354a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -396,8 +396,7 @@ symbols! {
         __S,
         __awaitee,
         __try_var,
-        _d,
-        _e,
+        _t,
         _task_context,
         a32,
         aarch64_target_feature,
@@ -407,6 +406,7 @@ symbols! {
         abi_amdgpu_kernel,
         abi_avr_interrupt,
         abi_c_cmse_nonsecure_call,
+        abi_cmse_nonsecure_call,
         abi_custom,
         abi_efiapi,
         abi_gpu_kernel,
@@ -578,6 +578,7 @@ symbols! {
         box_new,
         box_patterns,
         box_syntax,
+        boxed_slice,
         bpf_target_feature,
         braced_empty_structs,
         branch,
@@ -642,6 +643,7 @@ symbols! {
         cfi_encoding,
         char,
         char_is_ascii,
+        char_to_digit,
         child_id,
         child_kill,
         client,
@@ -692,6 +694,7 @@ symbols! {
         const_closures,
         const_compare_raw_pointers,
         const_constructor,
+        const_continue,
         const_deallocate,
         const_destruct,
         const_eval_limit,
@@ -1080,7 +1083,6 @@ symbols! {
         fs_create_dir,
         fsub_algebraic,
         fsub_fast,
-        fsxr,
         full,
         fundamental,
         fused_iterator,
@@ -1088,6 +1090,7 @@ symbols! {
         future_drop_poll,
         future_output,
         future_trait,
+        fxsr,
         gdb_script_file,
         ge,
         gen_blocks,
@@ -1217,6 +1220,8 @@ symbols! {
         intrinsics,
         intrinsics_unaligned_volatile_load,
         intrinsics_unaligned_volatile_store,
+        io_error_new,
+        io_errorkind,
         io_stderr,
         io_stdout,
         irrefutable_let_patterns,
@@ -1302,10 +1307,12 @@ symbols! {
         logf64,
         loongarch_target_feature,
         loop_break_value,
+        loop_match,
         lt,
         m68k_target_feature,
         macro_at_most_once_rep,
         macro_attributes_in_derive_output,
+        macro_concat,
         macro_escape,
         macro_export,
         macro_lifetime_matcher,
@@ -1340,6 +1347,7 @@ symbols! {
         maybe_uninit,
         maybe_uninit_uninit,
         maybe_uninit_zeroed,
+        mem_align_of,
         mem_discriminant,
         mem_drop,
         mem_forget,
@@ -1707,6 +1715,7 @@ symbols! {
         question_mark,
         quote,
         range_inclusive_new,
+        range_step,
         raw_dylib,
         raw_dylib_elf,
         raw_eq,
@@ -2023,6 +2032,7 @@ symbols! {
         slice,
         slice_from_raw_parts,
         slice_from_raw_parts_mut,
+        slice_from_ref,
         slice_get_unchecked,
         slice_into_vec,
         slice_iter,
@@ -2052,6 +2062,7 @@ symbols! {
         static_recursion,
         staticlib,
         std,
+        std_lib_injection,
         std_panic,
         std_panic_2015_macro,
         std_panic_macro,
diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs
index 4659bbdb890..b13e2112478 100644
--- a/compiler/rustc_target/src/spec/abi_map.rs
+++ b/compiler/rustc_target/src/spec/abi_map.rs
@@ -12,16 +12,19 @@ pub struct AbiMap {
     os: OsKind,
 }
 
+/// result from trying to map an ABI
 #[derive(Copy, Clone, Debug)]
 pub enum AbiMapping {
     /// this ABI is exactly mapped for this platform
     Direct(CanonAbi),
     /// we don't yet warn on this, but we will
     Deprecated(CanonAbi),
+    /// ABI we do not map for this platform: it must not reach codegen
     Invalid,
 }
 
 impl AbiMapping {
+    /// optionally get a [CanonAbi], even if Deprecated
     pub fn into_option(self) -> Option<CanonAbi> {
         match self {
             Self::Direct(abi) | Self::Deprecated(abi) => Some(abi),
@@ -29,6 +32,7 @@ impl AbiMapping {
         }
     }
 
+    /// get a [CanonAbi] even if Deprecated, panicking if Invalid
     #[track_caller]
     pub fn unwrap(self) -> CanonAbi {
         self.into_option().unwrap()
@@ -40,6 +44,7 @@ impl AbiMapping {
 }
 
 impl AbiMap {
+    /// create an AbiMap according to arbitrary fields on the [Target]
     pub fn from_target(target: &Target) -> Self {
         // the purpose of this little exercise is to force listing what affects these mappings
         let arch = match &*target.arch {
@@ -59,6 +64,7 @@ impl AbiMap {
         AbiMap { arch, os }
     }
 
+    /// lower an [ExternAbi] to a [CanonAbi] if this AbiMap allows
     pub fn canonize_abi(&self, extern_abi: ExternAbi, has_c_varargs: bool) -> AbiMapping {
         let AbiMap { os, arch } = *self;
 
@@ -79,24 +85,35 @@ impl AbiMap {
             (ExternAbi::System { .. }, _) => CanonAbi::C,
 
             // fallible lowerings
+            /* multi-platform */
+            // always and forever
+            (ExternAbi::RustInvalid, _) => return AbiMapping::Invalid,
+
             (ExternAbi::EfiApi, Arch::Arm(..)) => CanonAbi::Arm(ArmCall::Aapcs),
             (ExternAbi::EfiApi, Arch::X86_64) => CanonAbi::X86(X86Call::Win64),
             (ExternAbi::EfiApi, Arch::Aarch64 | Arch::Riscv | Arch::X86) => CanonAbi::C,
             (ExternAbi::EfiApi, _) => return AbiMapping::Invalid,
 
+            /* arm */
             (ExternAbi::Aapcs { .. }, Arch::Arm(..)) => CanonAbi::Arm(ArmCall::Aapcs),
             (ExternAbi::Aapcs { .. }, _) => return AbiMapping::Invalid,
 
-            (ExternAbi::CCmseNonSecureCall, Arch::Arm(ArmVer::ThumbV8M)) => {
+            (ExternAbi::CmseNonSecureCall, Arch::Arm(ArmVer::ThumbV8M)) => {
                 CanonAbi::Arm(ArmCall::CCmseNonSecureCall)
             }
-            (ExternAbi::CCmseNonSecureEntry, Arch::Arm(ArmVer::ThumbV8M)) => {
+            (ExternAbi::CmseNonSecureEntry, Arch::Arm(ArmVer::ThumbV8M)) => {
                 CanonAbi::Arm(ArmCall::CCmseNonSecureEntry)
             }
-            (ExternAbi::CCmseNonSecureCall | ExternAbi::CCmseNonSecureEntry, ..) => {
+            (ExternAbi::CmseNonSecureCall | ExternAbi::CmseNonSecureEntry, ..) => {
                 return AbiMapping::Invalid;
             }
 
+            /* gpu */
+            (ExternAbi::PtxKernel, Arch::Nvptx) => CanonAbi::GpuKernel,
+            (ExternAbi::GpuKernel, Arch::Amdgpu | Arch::Nvptx) => CanonAbi::GpuKernel,
+            (ExternAbi::PtxKernel | ExternAbi::GpuKernel, _) => return AbiMapping::Invalid,
+
+            /* x86 */
             (ExternAbi::Cdecl { .. }, Arch::X86) => CanonAbi::C,
             (ExternAbi::Cdecl { .. }, _) => return AbiMapping::Deprecated(CanonAbi::C),
 
@@ -124,10 +141,7 @@ impl AbiMap {
             (ExternAbi::Win64 { .. }, Arch::X86_64) => CanonAbi::X86(X86Call::Win64),
             (ExternAbi::SysV64 { .. } | ExternAbi::Win64 { .. }, _) => return AbiMapping::Invalid,
 
-            (ExternAbi::PtxKernel, Arch::Nvptx) => CanonAbi::GpuKernel,
-            (ExternAbi::GpuKernel, Arch::Amdgpu | Arch::Nvptx) => CanonAbi::GpuKernel,
-            (ExternAbi::PtxKernel | ExternAbi::GpuKernel, _) => return AbiMapping::Invalid,
-
+            /* interrupts */
             (ExternAbi::AvrInterrupt, Arch::Avr) => CanonAbi::Interrupt(InterruptKind::Avr),
             (ExternAbi::AvrNonBlockingInterrupt, Arch::Avr) => {
                 CanonAbi::Interrupt(InterruptKind::AvrNonBlocking)
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index a1eac1fba25..3eea1e070a6 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -11,11 +11,6 @@ use crate::spec::{FloatAbi, RustcAbi, Target};
 /// These exist globally and are not in the target-specific lists below.
 pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
 
-/// Features that require special handling when passing to LLVM:
-/// these are target-specific (i.e., must also be listed in the target-specific list below)
-/// but do not correspond to an LLVM target feature.
-pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"];
-
 /// Stability information for target features.
 #[derive(Debug, Copy, Clone)]
 pub enum Stability {
@@ -34,9 +29,6 @@ pub enum Stability {
     /// particular for features are actually ABI configuration flags (not all targets are as nice as
     /// RISC-V and have an explicit way to set the ABI separate from target features).
     Forbidden { reason: &'static str },
-    /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set
-    /// by target modifier flag. Target modifier flags are tracked to be consistent in linked modules.
-    TargetModifierOnly { reason: &'static str, flag: &'static str },
 }
 use Stability::*;
 
@@ -52,7 +44,6 @@ impl<CTX> HashStable<CTX> for Stability {
             Stability::Forbidden { reason } => {
                 reason.hash_stable(hcx, hasher);
             }
-            Stability::TargetModifierOnly { .. } => {}
         }
     }
 }
@@ -62,7 +53,7 @@ impl Stability {
     /// (It might still be nightly-only even if this returns `true`, so make sure to also check
     /// `requires_nightly`.)
     pub fn in_cfg(&self) -> bool {
-        !matches!(self, Stability::Forbidden { .. })
+        matches!(self, Stability::Stable | Stability::Unstable { .. })
     }
 
     /// Returns the nightly feature that is required to toggle this target feature via
@@ -78,7 +69,16 @@ impl Stability {
             Stability::Unstable(nightly_feature) => Some(nightly_feature),
             Stability::Stable { .. } => None,
             Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"),
-            Stability::TargetModifierOnly { .. } => None,
+        }
+    }
+
+    /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
+    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
+    /// `requires_nightly`.)
+    pub fn toggle_allowed(&self) -> Result<(), &'static str> {
+        match self {
+            Stability::Unstable(_) | Stability::Stable { .. } => Ok(()),
+            Stability::Forbidden { reason } => Err(reason),
         }
     }
 }
@@ -270,12 +270,7 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]),
     // FEAT_RDM
     ("rdm", Stable, &["neon"]),
-    // This is needed for inline assembly, but shouldn't be stabilized as-is
-    // since it should be enabled globally using -Zfixed-x18, not
-    // #[target_feature].
-    // Note that cfg(target_feature = "reserve-x18") is currently not set for
-    // targets that reserve x18 by default.
-    ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]),
+    ("reserve-x18", Forbidden { reason: "use `-Zfixed-x18` compiler flag instead" }, &[]),
     // FEAT_SB
     ("sb", Stable, &[]),
     // FEAT_SHA1 & FEAT_SHA256
@@ -450,26 +445,17 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("rdseed", Stable, &[]),
     (
         "retpoline-external-thunk",
-        Stability::TargetModifierOnly {
-            reason: "use `retpoline-external-thunk` target modifier flag instead",
-            flag: "retpoline-external-thunk",
-        },
+        Stability::Forbidden { reason: "use `-Zretpoline-external-thunk` compiler flag instead" },
         &[],
     ),
     (
         "retpoline-indirect-branches",
-        Stability::TargetModifierOnly {
-            reason: "use `retpoline` target modifier flag instead",
-            flag: "retpoline",
-        },
+        Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" },
         &[],
     ),
     (
         "retpoline-indirect-calls",
-        Stability::TargetModifierOnly {
-            reason: "use `retpoline` target modifier flag instead",
-            flag: "retpoline",
-        },
+        Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" },
         &[],
     ),
     ("rtm", Unstable(sym::rtm_target_feature), &[]),
@@ -732,6 +718,7 @@ static LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
 #[rustfmt::skip]
 const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     // tidy-alphabetical-start
+    // For "backchain", https://github.com/rust-lang/rust/issues/142412 is a stabilization blocker
     ("backchain", Unstable(sym::s390x_target_feature), &[]),
     ("concurrent-functions", Unstable(sym::s390x_target_feature), &[]),
     ("deflate-conversion", Unstable(sym::s390x_target_feature), &[]),
diff --git a/compiler/rustc_thread_pool/Cargo.toml b/compiler/rustc_thread_pool/Cargo.toml
new file mode 100644
index 00000000000..d0bd065c457
--- /dev/null
+++ b/compiler/rustc_thread_pool/Cargo.toml
@@ -0,0 +1,50 @@
+[package]
+name = "rustc_thread_pool"
+version = "0.0.0"
+authors = ["Niko Matsakis <niko@alum.mit.edu>",
+           "Josh Stone <cuviper@gmail.com>"]
+description = "Core APIs for Rayon - fork for rustc"
+license = "MIT OR Apache-2.0"
+rust-version = "1.63"
+edition = "2021"
+readme = "README.md"
+keywords = ["parallel", "thread", "concurrency", "join", "performance"]
+categories = ["concurrency"]
+
+[dependencies]
+crossbeam-deque = "0.8"
+crossbeam-utils = "0.8"
+
+[dev-dependencies]
+rand = "0.9"
+rand_xorshift = "0.4"
+scoped-tls = "1.0"
+
+[target.'cfg(unix)'.dev-dependencies]
+libc = "0.2"
+
+[[test]]
+name = "stack_overflow_crash"
+path = "tests/stack_overflow_crash.rs"
+
+# NB: having one [[test]] manually defined means we need to declare them all
+
+[[test]]
+name = "double_init_fail"
+path = "tests/double_init_fail.rs"
+
+[[test]]
+name = "init_zero_threads"
+path = "tests/init_zero_threads.rs"
+
+[[test]]
+name = "scope_join"
+path = "tests/scope_join.rs"
+
+[[test]]
+name = "simple_panic"
+path = "tests/simple_panic.rs"
+
+[[test]]
+name = "scoped_threadpool"
+path = "tests/scoped_threadpool.rs"
diff --git a/compiler/rustc_thread_pool/README.md b/compiler/rustc_thread_pool/README.md
new file mode 100644
index 00000000000..a50cc1165b8
--- /dev/null
+++ b/compiler/rustc_thread_pool/README.md
@@ -0,0 +1,10 @@
+Note: This is an unstable fork made for use in rustc
+
+Rayon-core represents the "core, stable" APIs of Rayon: join, scope, and so forth, as well as the ability to create custom thread-pools with ThreadPool.
+
+Maybe worth mentioning: users are not necessarily intended to directly access rustc_thread_pool; all its APIs are mirrored in the rayon crate. To that end, the examples in the docs use rayon::join and so forth rather than rayon_core::join.
+
+
+Please see [Rayon Docs] for details about using Rayon.
+
+[Rayon Docs]: https://docs.rs/rayon/
diff --git a/compiler/rustc_thread_pool/src/broadcast/mod.rs b/compiler/rustc_thread_pool/src/broadcast/mod.rs
new file mode 100644
index 00000000000..9545c4b15d8
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/broadcast/mod.rs
@@ -0,0 +1,148 @@
+use std::fmt;
+use std::marker::PhantomData;
+use std::sync::Arc;
+
+use crate::job::{ArcJob, StackJob};
+use crate::latch::{CountLatch, LatchRef};
+use crate::registry::{Registry, WorkerThread};
+
+mod tests;
+
+/// Executes `op` within every thread in the current threadpool. If this is
+/// called from a non-Rayon thread, it will execute in the global threadpool.
+/// Any attempts to use `join`, `scope`, or parallel iterators will then operate
+/// within that threadpool. When the call has completed on each thread, returns
+/// a vector containing all of their return values.
+///
+/// For more information, see the [`ThreadPool::broadcast()`][m] method.
+///
+/// [m]: struct.ThreadPool.html#method.broadcast
+pub fn broadcast<OP, R>(op: OP) -> Vec<R>
+where
+    OP: Fn(BroadcastContext<'_>) -> R + Sync,
+    R: Send,
+{
+    // We assert that current registry has not terminated.
+    unsafe { broadcast_in(op, &Registry::current()) }
+}
+
+/// Spawns an asynchronous task on every thread in this thread-pool. This task
+/// will run in the implicit, global scope, which means that it may outlast the
+/// current stack frame -- therefore, it cannot capture any references onto the
+/// stack (you will likely need a `move` closure).
+///
+/// For more information, see the [`ThreadPool::spawn_broadcast()`][m] method.
+///
+/// [m]: struct.ThreadPool.html#method.spawn_broadcast
+pub fn spawn_broadcast<OP>(op: OP)
+where
+    OP: Fn(BroadcastContext<'_>) + Send + Sync + 'static,
+{
+    // We assert that current registry has not terminated.
+    unsafe { spawn_broadcast_in(op, &Registry::current()) }
+}
+
+/// Provides context to a closure called by `broadcast`.
+pub struct BroadcastContext<'a> {
+    worker: &'a WorkerThread,
+
+    /// Make sure to prevent auto-traits like `Send` and `Sync`.
+    _marker: PhantomData<&'a mut dyn Fn()>,
+}
+
+impl<'a> BroadcastContext<'a> {
+    pub(super) fn with<R>(f: impl FnOnce(BroadcastContext<'_>) -> R) -> R {
+        let worker_thread = WorkerThread::current();
+        assert!(!worker_thread.is_null());
+        f(BroadcastContext { worker: unsafe { &*worker_thread }, _marker: PhantomData })
+    }
+
+    /// Our index amongst the broadcast threads (ranges from `0..self.num_threads()`).
+    #[inline]
+    pub fn index(&self) -> usize {
+        self.worker.index()
+    }
+
+    /// The number of threads receiving the broadcast in the thread pool.
+    ///
+    /// # Future compatibility note
+    ///
+    /// Future versions of Rayon might vary the number of threads over time, but
+    /// this method will always return the number of threads which are actually
+    /// receiving your particular `broadcast` call.
+    #[inline]
+    pub fn num_threads(&self) -> usize {
+        self.worker.registry().num_threads()
+    }
+}
+
+impl<'a> fmt::Debug for BroadcastContext<'a> {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt.debug_struct("BroadcastContext")
+            .field("index", &self.index())
+            .field("num_threads", &self.num_threads())
+            .field("pool_id", &self.worker.registry().id())
+            .finish()
+    }
+}
+
+/// Execute `op` on every thread in the pool. It will be executed on each
+/// thread when they have nothing else to do locally, before they try to
+/// steal work from other threads. This function will not return until all
+/// threads have completed the `op`.
+///
+/// Unsafe because `registry` must not yet have terminated.
+pub(super) unsafe fn broadcast_in<OP, R>(op: OP, registry: &Arc<Registry>) -> Vec<R>
+where
+    OP: Fn(BroadcastContext<'_>) -> R + Sync,
+    R: Send,
+{
+    let f = move |injected: bool| {
+        debug_assert!(injected);
+        BroadcastContext::with(&op)
+    };
+
+    let n_threads = registry.num_threads();
+    let current_thread = unsafe { WorkerThread::current().as_ref() };
+    let tlv = crate::tlv::get();
+    let latch = CountLatch::with_count(n_threads, current_thread);
+    let jobs: Vec<_> =
+        (0..n_threads).map(|_| StackJob::new(tlv, &f, LatchRef::new(&latch))).collect();
+    let job_refs = jobs.iter().map(|job| unsafe { job.as_job_ref() });
+
+    registry.inject_broadcast(job_refs);
+
+    // Wait for all jobs to complete, then collect the results, maybe propagating a panic.
+    latch.wait(current_thread);
+    jobs.into_iter().map(|job| unsafe { job.into_result() }).collect()
+}
+
+/// Execute `op` on every thread in the pool. It will be executed on each
+/// thread when they have nothing else to do locally, before they try to
+/// steal work from other threads. This function returns immediately after
+/// injecting the jobs.
+///
+/// Unsafe because `registry` must not yet have terminated.
+pub(super) unsafe fn spawn_broadcast_in<OP>(op: OP, registry: &Arc<Registry>)
+where
+    OP: Fn(BroadcastContext<'_>) + Send + Sync + 'static,
+{
+    let job = ArcJob::new({
+        let registry = Arc::clone(registry);
+        move || {
+            registry.catch_unwind(|| BroadcastContext::with(&op));
+            registry.terminate(); // (*) permit registry to terminate now
+        }
+    });
+
+    let n_threads = registry.num_threads();
+    let job_refs = (0..n_threads).map(|_| {
+        // Ensure that registry cannot terminate until this job has executed
+        // on each thread. This ref is decremented at the (*) above.
+        registry.increment_terminate_count();
+
+        ArcJob::as_static_job_ref(&job)
+    });
+
+    registry.inject_broadcast(job_refs);
+}
diff --git a/compiler/rustc_thread_pool/src/broadcast/tests.rs b/compiler/rustc_thread_pool/src/broadcast/tests.rs
new file mode 100644
index 00000000000..fac8b8ad466
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/broadcast/tests.rs
@@ -0,0 +1,264 @@
+#![cfg(test)]
+
+use std::sync::Arc;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::mpsc::channel;
+use std::{thread, time};
+
+use crate::ThreadPoolBuilder;
+
+#[test]
+fn broadcast_global() {
+    let v = crate::broadcast(|ctx| ctx.index());
+    assert!(v.into_iter().eq(0..crate::current_num_threads()));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_broadcast_global() {
+    let (tx, rx) = channel();
+    crate::spawn_broadcast(move |ctx| tx.send(ctx.index()).unwrap());
+
+    let mut v: Vec<_> = rx.into_iter().collect();
+    v.sort_unstable();
+    assert!(v.into_iter().eq(0..crate::current_num_threads()));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn broadcast_pool() {
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    let v = pool.broadcast(|ctx| ctx.index());
+    assert!(v.into_iter().eq(0..7));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_broadcast_pool() {
+    let (tx, rx) = channel();
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    pool.spawn_broadcast(move |ctx| tx.send(ctx.index()).unwrap());
+
+    let mut v: Vec<_> = rx.into_iter().collect();
+    v.sort_unstable();
+    assert!(v.into_iter().eq(0..7));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn broadcast_self() {
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    let v = pool.install(|| crate::broadcast(|ctx| ctx.index()));
+    assert!(v.into_iter().eq(0..7));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_broadcast_self() {
+    let (tx, rx) = channel();
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    pool.spawn(|| crate::spawn_broadcast(move |ctx| tx.send(ctx.index()).unwrap()));
+
+    let mut v: Vec<_> = rx.into_iter().collect();
+    v.sort_unstable();
+    assert!(v.into_iter().eq(0..7));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn broadcast_mutual() {
+    let count = AtomicUsize::new(0);
+    let pool1 = ThreadPoolBuilder::new().num_threads(3).build().unwrap();
+    let pool2 = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    pool1.install(|| {
+        pool2.broadcast(|_| {
+            pool1.broadcast(|_| {
+                count.fetch_add(1, Ordering::Relaxed);
+            })
+        })
+    });
+    assert_eq!(count.into_inner(), 3 * 7);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_broadcast_mutual() {
+    let (tx, rx) = channel();
+    let pool1 = Arc::new(ThreadPoolBuilder::new().num_threads(3).build().unwrap());
+    let pool2 = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    pool1.spawn({
+        let pool1 = Arc::clone(&pool1);
+        move || {
+            pool2.spawn_broadcast(move |_| {
+                let tx = tx.clone();
+                pool1.spawn_broadcast(move |_| tx.send(()).unwrap())
+            })
+        }
+    });
+    assert_eq!(rx.into_iter().count(), 3 * 7);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn broadcast_mutual_sleepy() {
+    let count = AtomicUsize::new(0);
+    let pool1 = ThreadPoolBuilder::new().num_threads(3).build().unwrap();
+    let pool2 = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    pool1.install(|| {
+        thread::sleep(time::Duration::from_secs(1));
+        pool2.broadcast(|_| {
+            thread::sleep(time::Duration::from_secs(1));
+            pool1.broadcast(|_| {
+                thread::sleep(time::Duration::from_millis(100));
+                count.fetch_add(1, Ordering::Relaxed);
+            })
+        })
+    });
+    assert_eq!(count.into_inner(), 3 * 7);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_broadcast_mutual_sleepy() {
+    let (tx, rx) = channel();
+    let pool1 = Arc::new(ThreadPoolBuilder::new().num_threads(3).build().unwrap());
+    let pool2 = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    pool1.spawn({
+        let pool1 = Arc::clone(&pool1);
+        move || {
+            thread::sleep(time::Duration::from_secs(1));
+            pool2.spawn_broadcast(move |_| {
+                let tx = tx.clone();
+                thread::sleep(time::Duration::from_secs(1));
+                pool1.spawn_broadcast(move |_| {
+                    thread::sleep(time::Duration::from_millis(100));
+                    tx.send(()).unwrap();
+                })
+            })
+        }
+    });
+    assert_eq!(rx.into_iter().count(), 3 * 7);
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn broadcast_panic_one() {
+    let count = AtomicUsize::new(0);
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    let result = crate::unwind::halt_unwinding(|| {
+        pool.broadcast(|ctx| {
+            count.fetch_add(1, Ordering::Relaxed);
+            if ctx.index() == 3 {
+                panic!("Hello, world!");
+            }
+        })
+    });
+    assert_eq!(count.into_inner(), 7);
+    assert!(result.is_err(), "broadcast panic should propagate!");
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn spawn_broadcast_panic_one() {
+    let (tx, rx) = channel();
+    let (panic_tx, panic_rx) = channel();
+    let pool = ThreadPoolBuilder::new()
+        .num_threads(7)
+        .panic_handler(move |e| panic_tx.send(e).unwrap())
+        .build()
+        .unwrap();
+    pool.spawn_broadcast(move |ctx| {
+        tx.send(()).unwrap();
+        if ctx.index() == 3 {
+            panic!("Hello, world!");
+        }
+    });
+    drop(pool); // including panic_tx
+    assert_eq!(rx.into_iter().count(), 7);
+    assert_eq!(panic_rx.into_iter().count(), 1);
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn broadcast_panic_many() {
+    let count = AtomicUsize::new(0);
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    let result = crate::unwind::halt_unwinding(|| {
+        pool.broadcast(|ctx| {
+            count.fetch_add(1, Ordering::Relaxed);
+            if ctx.index() % 2 == 0 {
+                panic!("Hello, world!");
+            }
+        })
+    });
+    assert_eq!(count.into_inner(), 7);
+    assert!(result.is_err(), "broadcast panic should propagate!");
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn spawn_broadcast_panic_many() {
+    let (tx, rx) = channel();
+    let (panic_tx, panic_rx) = channel();
+    let pool = ThreadPoolBuilder::new()
+        .num_threads(7)
+        .panic_handler(move |e| panic_tx.send(e).unwrap())
+        .build()
+        .unwrap();
+    pool.spawn_broadcast(move |ctx| {
+        tx.send(()).unwrap();
+        if ctx.index() % 2 == 0 {
+            panic!("Hello, world!");
+        }
+    });
+    drop(pool); // including panic_tx
+    assert_eq!(rx.into_iter().count(), 7);
+    assert_eq!(panic_rx.into_iter().count(), 4);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn broadcast_sleep_race() {
+    let test_duration = time::Duration::from_secs(1);
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    let start = time::Instant::now();
+    while start.elapsed() < test_duration {
+        pool.broadcast(|ctx| {
+            // A slight spread of sleep duration increases the chance that one
+            // of the threads will race in the pool's idle sleep afterward.
+            thread::sleep(time::Duration::from_micros(ctx.index() as u64));
+        });
+    }
+}
+
+#[test]
+fn broadcast_after_spawn_broadcast() {
+    let (tx, rx) = channel();
+
+    // Queue a non-blocking spawn_broadcast.
+    crate::spawn_broadcast(move |ctx| tx.send(ctx.index()).unwrap());
+
+    // This blocking broadcast runs after all prior broadcasts.
+    crate::broadcast(|_| {});
+
+    // The spawn_broadcast **must** have run by now on all threads.
+    let mut v: Vec<_> = rx.try_iter().collect();
+    v.sort_unstable();
+    assert!(v.into_iter().eq(0..crate::current_num_threads()));
+}
+
+#[test]
+fn broadcast_after_spawn() {
+    let (tx, rx) = channel();
+
+    // Queue a regular spawn on a thread-local deque.
+    crate::registry::in_worker(move |_, _| {
+        crate::spawn(move || tx.send(22).unwrap());
+    });
+
+    // Broadcast runs after the local deque is empty.
+    crate::broadcast(|_| {});
+
+    // The spawn **must** have run by now.
+    assert_eq!(22, rx.try_recv().unwrap());
+}
diff --git a/compiler/rustc_thread_pool/src/compile_fail/mod.rs b/compiler/rustc_thread_pool/src/compile_fail/mod.rs
new file mode 100644
index 00000000000..f2ec646a4d3
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/compile_fail/mod.rs
@@ -0,0 +1,7 @@
+// These modules contain `compile_fail` doc tests.
+mod quicksort_race1;
+mod quicksort_race2;
+mod quicksort_race3;
+mod rc_return;
+mod rc_upvar;
+mod scope_join_bad;
diff --git a/compiler/rustc_thread_pool/src/compile_fail/quicksort_race1.rs b/compiler/rustc_thread_pool/src/compile_fail/quicksort_race1.rs
new file mode 100644
index 00000000000..f6dbc769699
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/compile_fail/quicksort_race1.rs
@@ -0,0 +1,28 @@
+/*! ```compile_fail,E0524
+
+fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
+    if v.len() <= 1 {
+        return;
+    }
+
+    let mid = partition(v);
+    let (lo, _hi) = v.split_at_mut(mid);
+    rustc_thread_pool::join(|| quick_sort(lo), || quick_sort(lo)); //~ ERROR
+}
+
+fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
+    let pivot = v.len() - 1;
+    let mut i = 0;
+    for j in 0..pivot {
+        if v[j] <= v[pivot] {
+            v.swap(i, j);
+            i += 1;
+        }
+    }
+    v.swap(i, pivot);
+    i
+}
+
+fn main() { }
+
+``` */
diff --git a/compiler/rustc_thread_pool/src/compile_fail/quicksort_race2.rs b/compiler/rustc_thread_pool/src/compile_fail/quicksort_race2.rs
new file mode 100644
index 00000000000..ccd737a700d
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/compile_fail/quicksort_race2.rs
@@ -0,0 +1,28 @@
+/*! ```compile_fail,E0500
+
+fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
+    if v.len() <= 1 {
+        return;
+    }
+
+    let mid = partition(v);
+    let (lo, _hi) = v.split_at_mut(mid);
+    rustc_thread_pool::join(|| quick_sort(lo), || quick_sort(v)); //~ ERROR
+}
+
+fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
+    let pivot = v.len() - 1;
+    let mut i = 0;
+    for j in 0..pivot {
+        if v[j] <= v[pivot] {
+            v.swap(i, j);
+            i += 1;
+        }
+    }
+    v.swap(i, pivot);
+    i
+}
+
+fn main() { }
+
+``` */
diff --git a/compiler/rustc_thread_pool/src/compile_fail/quicksort_race3.rs b/compiler/rustc_thread_pool/src/compile_fail/quicksort_race3.rs
new file mode 100644
index 00000000000..6acdf084433
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/compile_fail/quicksort_race3.rs
@@ -0,0 +1,28 @@
+/*! ```compile_fail,E0524
+
+fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
+    if v.len() <= 1 {
+        return;
+    }
+
+    let mid = partition(v);
+    let (_lo, hi) = v.split_at_mut(mid);
+    rustc_thread_pool::join(|| quick_sort(hi), || quick_sort(hi)); //~ ERROR
+}
+
+fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
+    let pivot = v.len() - 1;
+    let mut i = 0;
+    for j in 0..pivot {
+        if v[j] <= v[pivot] {
+            v.swap(i, j);
+            i += 1;
+        }
+    }
+    v.swap(i, pivot);
+    i
+}
+
+fn main() { }
+
+``` */
diff --git a/compiler/rustc_thread_pool/src/compile_fail/rc_return.rs b/compiler/rustc_thread_pool/src/compile_fail/rc_return.rs
new file mode 100644
index 00000000000..165c685aba1
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/compile_fail/rc_return.rs
@@ -0,0 +1,17 @@
+/** ```compile_fail,E0277
+
+use std::rc::Rc;
+
+rustc_thread_pool::join(|| Rc::new(22), || ()); //~ ERROR
+
+``` */
+mod left {}
+
+/** ```compile_fail,E0277
+
+use std::rc::Rc;
+
+rustc_thread_pool::join(|| (), || Rc::new(23)); //~ ERROR
+
+``` */
+mod right {}
diff --git a/compiler/rustc_thread_pool/src/compile_fail/rc_upvar.rs b/compiler/rustc_thread_pool/src/compile_fail/rc_upvar.rs
new file mode 100644
index 00000000000..6dc9ead48a0
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/compile_fail/rc_upvar.rs
@@ -0,0 +1,9 @@
+/*! ```compile_fail,E0277
+
+use std::rc::Rc;
+
+let r = Rc::new(22);
+rustc_thread_pool::join(|| r.clone(), || r.clone());
+//~^ ERROR
+
+``` */
diff --git a/compiler/rustc_thread_pool/src/compile_fail/scope_join_bad.rs b/compiler/rustc_thread_pool/src/compile_fail/scope_join_bad.rs
new file mode 100644
index 00000000000..e65abfc3c1e
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/compile_fail/scope_join_bad.rs
@@ -0,0 +1,24 @@
+/*! ```compile_fail,E0373
+
+fn bad_scope<F>(f: F)
+    where F: FnOnce(&i32) + Send,
+{
+    rustc_thread_pool::scope(|s| {
+        let x = 22;
+        s.spawn(|_| f(&x)); //~ ERROR `x` does not live long enough
+    });
+}
+
+fn good_scope<F>(f: F)
+    where F: FnOnce(&i32) + Send,
+{
+    let x = 22;
+    rustc_thread_pool::scope(|s| {
+        s.spawn(|_| f(&x));
+    });
+}
+
+fn main() {
+}
+
+``` */
diff --git a/compiler/rustc_thread_pool/src/job.rs b/compiler/rustc_thread_pool/src/job.rs
new file mode 100644
index 00000000000..e6e84ac2320
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/job.rs
@@ -0,0 +1,277 @@
+use std::any::Any;
+use std::cell::UnsafeCell;
+use std::mem;
+use std::sync::Arc;
+
+use crossbeam_deque::{Injector, Steal};
+
+use crate::latch::Latch;
+use crate::tlv::Tlv;
+use crate::{tlv, unwind};
+
+pub(super) enum JobResult<T> {
+    None,
+    Ok(T),
+    Panic(Box<dyn Any + Send>),
+}
+
+/// A `Job` is used to advertise work for other threads that they may
+/// want to steal. In accordance with time honored tradition, jobs are
+/// arranged in a deque, so that thieves can take from the top of the
+/// deque while the main worker manages the bottom of the deque. This
+/// deque is managed by the `thread_pool` module.
+pub(super) trait Job {
+    /// Unsafe: this may be called from a different thread than the one
+    /// which scheduled the job, so the implementer must ensure the
+    /// appropriate traits are met, whether `Send`, `Sync`, or both.
+    unsafe fn execute(this: *const ());
+}
+
+/// Effectively a Job trait object. Each JobRef **must** be executed
+/// exactly once, or else data may leak.
+///
+/// Internally, we store the job's data in a `*const ()` pointer. The
+/// true type is something like `*const StackJob<...>`, but we hide
+/// it. We also carry the "execute fn" from the `Job` trait.
+pub(super) struct JobRef {
+    pointer: *const (),
+    execute_fn: unsafe fn(*const ()),
+}
+
+unsafe impl Send for JobRef {}
+unsafe impl Sync for JobRef {}
+
+impl JobRef {
+    /// Unsafe: caller asserts that `data` will remain valid until the
+    /// job is executed.
+    pub(super) unsafe fn new<T>(data: *const T) -> JobRef
+    where
+        T: Job,
+    {
+        // erase types:
+        JobRef { pointer: data as *const (), execute_fn: <T as Job>::execute }
+    }
+
+    /// Returns an opaque handle that can be saved and compared,
+    /// without making `JobRef` itself `Copy + Eq`.
+    #[inline]
+    pub(super) fn id(&self) -> impl Eq {
+        (self.pointer, self.execute_fn)
+    }
+
+    #[inline]
+    pub(super) unsafe fn execute(self) {
+        unsafe { (self.execute_fn)(self.pointer) }
+    }
+}
+
+/// A job that will be owned by a stack slot. This means that when it
+/// executes it need not free any heap data, the cleanup occurs when
+/// the stack frame is later popped. The function parameter indicates
+/// `true` if the job was stolen -- executed on a different thread.
+pub(super) struct StackJob<L, F, R>
+where
+    L: Latch + Sync,
+    F: FnOnce(bool) -> R + Send,
+    R: Send,
+{
+    pub(super) latch: L,
+    func: UnsafeCell<Option<F>>,
+    result: UnsafeCell<JobResult<R>>,
+    tlv: Tlv,
+}
+
+impl<L, F, R> StackJob<L, F, R>
+where
+    L: Latch + Sync,
+    F: FnOnce(bool) -> R + Send,
+    R: Send,
+{
+    pub(super) fn new(tlv: Tlv, func: F, latch: L) -> StackJob<L, F, R> {
+        StackJob {
+            latch,
+            func: UnsafeCell::new(Some(func)),
+            result: UnsafeCell::new(JobResult::None),
+            tlv,
+        }
+    }
+
+    pub(super) unsafe fn as_job_ref(&self) -> JobRef {
+        unsafe { JobRef::new(self) }
+    }
+
+    pub(super) unsafe fn run_inline(self, stolen: bool) -> R {
+        self.func.into_inner().unwrap()(stolen)
+    }
+
+    pub(super) unsafe fn into_result(self) -> R {
+        self.result.into_inner().into_return_value()
+    }
+}
+
+impl<L, F, R> Job for StackJob<L, F, R>
+where
+    L: Latch + Sync,
+    F: FnOnce(bool) -> R + Send,
+    R: Send,
+{
+    unsafe fn execute(this: *const ()) {
+        let this = unsafe { &*(this as *const Self) };
+        tlv::set(this.tlv);
+        let abort = unwind::AbortIfPanic;
+        let func = unsafe { (*this.func.get()).take().unwrap() };
+        unsafe {
+            (*this.result.get()) = JobResult::call(func);
+        }
+        unsafe {
+            Latch::set(&this.latch);
+        }
+        mem::forget(abort);
+    }
+}
+
+/// Represents a job stored in the heap. Used to implement
+/// `scope`. Unlike `StackJob`, when executed, `HeapJob` simply
+/// invokes a closure, which then triggers the appropriate logic to
+/// signal that the job executed.
+///
+/// (Probably `StackJob` should be refactored in a similar fashion.)
+pub(super) struct HeapJob<BODY>
+where
+    BODY: FnOnce() + Send,
+{
+    job: BODY,
+    tlv: Tlv,
+}
+
+impl<BODY> HeapJob<BODY>
+where
+    BODY: FnOnce() + Send,
+{
+    pub(super) fn new(tlv: Tlv, job: BODY) -> Box<Self> {
+        Box::new(HeapJob { job, tlv })
+    }
+
+    /// Creates a `JobRef` from this job -- note that this hides all
+    /// lifetimes, so it is up to you to ensure that this JobRef
+    /// doesn't outlive any data that it closes over.
+    pub(super) unsafe fn into_job_ref(self: Box<Self>) -> JobRef {
+        unsafe { JobRef::new(Box::into_raw(self)) }
+    }
+
+    /// Creates a static `JobRef` from this job.
+    pub(super) fn into_static_job_ref(self: Box<Self>) -> JobRef
+    where
+        BODY: 'static,
+    {
+        unsafe { self.into_job_ref() }
+    }
+}
+
+impl<BODY> Job for HeapJob<BODY>
+where
+    BODY: FnOnce() + Send,
+{
+    unsafe fn execute(this: *const ()) {
+        let this = unsafe { Box::from_raw(this as *mut Self) };
+        tlv::set(this.tlv);
+        (this.job)();
+    }
+}
+
+/// Represents a job stored in an `Arc` -- like `HeapJob`, but may
+/// be turned into multiple `JobRef`s and called multiple times.
+pub(super) struct ArcJob<BODY>
+where
+    BODY: Fn() + Send + Sync,
+{
+    job: BODY,
+}
+
+impl<BODY> ArcJob<BODY>
+where
+    BODY: Fn() + Send + Sync,
+{
+    pub(super) fn new(job: BODY) -> Arc<Self> {
+        Arc::new(ArcJob { job })
+    }
+
+    /// Creates a `JobRef` from this job -- note that this hides all
+    /// lifetimes, so it is up to you to ensure that this JobRef
+    /// doesn't outlive any data that it closes over.
+    pub(super) unsafe fn as_job_ref(this: &Arc<Self>) -> JobRef {
+        unsafe { JobRef::new(Arc::into_raw(Arc::clone(this))) }
+    }
+
+    /// Creates a static `JobRef` from this job.
+    pub(super) fn as_static_job_ref(this: &Arc<Self>) -> JobRef
+    where
+        BODY: 'static,
+    {
+        unsafe { Self::as_job_ref(this) }
+    }
+}
+
+impl<BODY> Job for ArcJob<BODY>
+where
+    BODY: Fn() + Send + Sync,
+{
+    unsafe fn execute(this: *const ()) {
+        let this = unsafe { Arc::from_raw(this as *mut Self) };
+        (this.job)();
+    }
+}
+
+impl<T> JobResult<T> {
+    fn call(func: impl FnOnce(bool) -> T) -> Self {
+        match unwind::halt_unwinding(|| func(true)) {
+            Ok(x) => JobResult::Ok(x),
+            Err(x) => JobResult::Panic(x),
+        }
+    }
+
+    /// Convert the `JobResult` for a job that has finished (and hence
+    /// its JobResult is populated) into its return value.
+    ///
+    /// NB. This will panic if the job panicked.
+    pub(super) fn into_return_value(self) -> T {
+        match self {
+            JobResult::None => unreachable!(),
+            JobResult::Ok(x) => x,
+            JobResult::Panic(x) => unwind::resume_unwinding(x),
+        }
+    }
+}
+
+/// Indirect queue to provide FIFO job priority.
+pub(super) struct JobFifo {
+    inner: Injector<JobRef>,
+}
+
+impl JobFifo {
+    pub(super) fn new() -> Self {
+        JobFifo { inner: Injector::new() }
+    }
+
+    pub(super) unsafe fn push(&self, job_ref: JobRef) -> JobRef {
+        // A little indirection ensures that spawns are always prioritized in FIFO order. The
+        // jobs in a thread's deque may be popped from the back (LIFO) or stolen from the front
+        // (FIFO), but either way they will end up popping from the front of this queue.
+        self.inner.push(job_ref);
+        unsafe { JobRef::new(self) }
+    }
+}
+
+impl Job for JobFifo {
+    unsafe fn execute(this: *const ()) {
+        // We "execute" a queue by executing its first job, FIFO.
+        let this = unsafe { &*(this as *const Self) };
+        loop {
+            match this.inner.steal() {
+                Steal::Success(job_ref) => break unsafe { job_ref.execute() },
+                Steal::Empty => panic!("FIFO is empty"),
+                Steal::Retry => {}
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_thread_pool/src/join/mod.rs b/compiler/rustc_thread_pool/src/join/mod.rs
new file mode 100644
index 00000000000..f285362c19b
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/join/mod.rs
@@ -0,0 +1,201 @@
+use std::any::Any;
+
+use crate::job::StackJob;
+use crate::latch::SpinLatch;
+use crate::registry::{self, WorkerThread};
+use crate::tlv::{self, Tlv};
+use crate::{FnContext, unwind};
+
+#[cfg(test)]
+mod tests;
+
+/// Takes two closures and *potentially* runs them in parallel. It
+/// returns a pair of the results from those closures.
+///
+/// Conceptually, calling `join()` is similar to spawning two threads,
+/// one executing each of the two closures. However, the
+/// implementation is quite different and incurs very low
+/// overhead. The underlying technique is called "work stealing": the
+/// Rayon runtime uses a fixed pool of worker threads and attempts to
+/// only execute code in parallel when there are idle CPUs to handle
+/// it.
+///
+/// When `join` is called from outside the thread pool, the calling
+/// thread will block while the closures execute in the pool. When
+/// `join` is called within the pool, the calling thread still actively
+/// participates in the thread pool. It will begin by executing closure
+/// A (on the current thread). While it is doing that, it will advertise
+/// closure B as being available for other threads to execute. Once closure A
+/// has completed, the current thread will try to execute closure B;
+/// if however closure B has been stolen, then it will look for other work
+/// while waiting for the thief to fully execute closure B. (This is the
+/// typical work-stealing strategy).
+///
+/// # Examples
+///
+/// This example uses join to perform a quick-sort (note this is not a
+/// particularly optimized implementation: if you **actually** want to
+/// sort for real, you should prefer [the `par_sort` method] offered
+/// by Rayon).
+///
+/// [the `par_sort` method]: ../rayon/slice/trait.ParallelSliceMut.html#method.par_sort
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// let mut v = vec![5, 1, 8, 22, 0, 44];
+/// quick_sort(&mut v);
+/// assert_eq!(v, vec![0, 1, 5, 8, 22, 44]);
+///
+/// fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
+///    if v.len() > 1 {
+///        let mid = partition(v);
+///        let (lo, hi) = v.split_at_mut(mid);
+///        rayon::join(|| quick_sort(lo),
+///                    || quick_sort(hi));
+///    }
+/// }
+///
+/// // Partition rearranges all items `<=` to the pivot
+/// // item (arbitrary selected to be the last item in the slice)
+/// // to the first half of the slice. It then returns the
+/// // "dividing point" where the pivot is placed.
+/// fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
+///     let pivot = v.len() - 1;
+///     let mut i = 0;
+///     for j in 0..pivot {
+///         if v[j] <= v[pivot] {
+///             v.swap(i, j);
+///             i += 1;
+///         }
+///     }
+///     v.swap(i, pivot);
+///     i
+/// }
+/// ```
+///
+/// # Warning about blocking I/O
+///
+/// The assumption is that the closures given to `join()` are
+/// CPU-bound tasks that do not perform I/O or other blocking
+/// operations. If you do perform I/O, and that I/O should block
+/// (e.g., waiting for a network request), the overall performance may
+/// be poor. Moreover, if you cause one closure to be blocked waiting
+/// on another (for example, using a channel), that could lead to a
+/// deadlock.
+///
+/// # Panics
+///
+/// No matter what happens, both closures will always be executed. If
+/// a single closure panics, whether it be the first or second
+/// closure, that panic will be propagated and hence `join()` will
+/// panic with the same panic value. If both closures panic, `join()`
+/// will panic with the panic value from the first closure.
+pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
+where
+    A: FnOnce() -> RA + Send,
+    B: FnOnce() -> RB + Send,
+    RA: Send,
+    RB: Send,
+{
+    #[inline]
+    fn call<R>(f: impl FnOnce() -> R) -> impl FnOnce(FnContext) -> R {
+        move |_| f()
+    }
+
+    join_context(call(oper_a), call(oper_b))
+}
+
+/// Identical to `join`, except that the closures have a parameter
+/// that provides context for the way the closure has been called,
+/// especially indicating whether they're executing on a different
+/// thread than where `join_context` was called. This will occur if
+/// the second job is stolen by a different thread, or if
+/// `join_context` was called from outside the thread pool to begin
+/// with.
+pub fn join_context<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
+where
+    A: FnOnce(FnContext) -> RA + Send,
+    B: FnOnce(FnContext) -> RB + Send,
+    RA: Send,
+    RB: Send,
+{
+    #[inline]
+    fn call_a<R>(f: impl FnOnce(FnContext) -> R, injected: bool) -> impl FnOnce() -> R {
+        move || f(FnContext::new(injected))
+    }
+
+    #[inline]
+    fn call_b<R>(f: impl FnOnce(FnContext) -> R) -> impl FnOnce(bool) -> R {
+        move |migrated| f(FnContext::new(migrated))
+    }
+
+    registry::in_worker(|worker_thread, injected| unsafe {
+        let tlv = tlv::get();
+        // Create virtual wrapper for task b; this all has to be
+        // done here so that the stack frame can keep it all live
+        // long enough.
+        let job_b = StackJob::new(tlv, call_b(oper_b), SpinLatch::new(worker_thread));
+        let job_b_ref = job_b.as_job_ref();
+        let job_b_id = job_b_ref.id();
+        worker_thread.push(job_b_ref);
+
+        // Execute task a; hopefully b gets stolen in the meantime.
+        let status_a = unwind::halt_unwinding(call_a(oper_a, injected));
+        let result_a = match status_a {
+            Ok(v) => v,
+            Err(err) => join_recover_from_panic(worker_thread, &job_b.latch, err, tlv),
+        };
+
+        // Now that task A has finished, try to pop job B from the
+        // local stack. It may already have been popped by job A; it
+        // may also have been stolen. There may also be some tasks
+        // pushed on top of it in the stack, and we will have to pop
+        // those off to get to it.
+        while !job_b.latch.probe() {
+            if let Some(job) = worker_thread.take_local_job() {
+                if job_b_id == job.id() {
+                    // Found it! Let's run it.
+                    //
+                    // Note that this could panic, but it's ok if we unwind here.
+
+                    // Restore the TLV since we might have run some jobs overwriting it when waiting for job b.
+                    tlv::set(tlv);
+
+                    let result_b = job_b.run_inline(injected);
+                    return (result_a, result_b);
+                } else {
+                    worker_thread.execute(job);
+                }
+            } else {
+                // Local deque is empty. Time to steal from other
+                // threads.
+                worker_thread.wait_until(&job_b.latch);
+                debug_assert!(job_b.latch.probe());
+                break;
+            }
+        }
+
+        // Restore the TLV since we might have run some jobs overwriting it when waiting for job b.
+        tlv::set(tlv);
+
+        (result_a, job_b.into_result())
+    })
+}
+
+/// If job A panics, we still cannot return until we are sure that job
+/// B is complete. This is because it may contain references into the
+/// enclosing stack frame(s).
+#[cold] // cold path
+unsafe fn join_recover_from_panic(
+    worker_thread: &WorkerThread,
+    job_b_latch: &SpinLatch<'_>,
+    err: Box<dyn Any + Send>,
+    tlv: Tlv,
+) -> ! {
+    unsafe { worker_thread.wait_until(job_b_latch) };
+
+    // Restore the TLV since we might have run some jobs overwriting it when waiting for job b.
+    tlv::set(tlv);
+
+    unwind::resume_unwinding(err)
+}
diff --git a/compiler/rustc_thread_pool/src/join/tests.rs b/compiler/rustc_thread_pool/src/join/tests.rs
new file mode 100644
index 00000000000..9df99072c3a
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/join/tests.rs
@@ -0,0 +1,151 @@
+//! Tests for the join code.
+
+use rand::distr::StandardUniform;
+use rand::{Rng, SeedableRng};
+use rand_xorshift::XorShiftRng;
+
+use super::*;
+use crate::ThreadPoolBuilder;
+
+fn quick_sort<T: PartialOrd + Send>(v: &mut [T]) {
+    if v.len() <= 1 {
+        return;
+    }
+
+    let mid = partition(v);
+    let (lo, hi) = v.split_at_mut(mid);
+    join(|| quick_sort(lo), || quick_sort(hi));
+}
+
+fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
+    let pivot = v.len() - 1;
+    let mut i = 0;
+    for j in 0..pivot {
+        if v[j] <= v[pivot] {
+            v.swap(i, j);
+            i += 1;
+        }
+    }
+    v.swap(i, pivot);
+    i
+}
+
+fn seeded_rng() -> XorShiftRng {
+    let mut seed = <XorShiftRng as SeedableRng>::Seed::default();
+    (0..).zip(seed.as_mut()).for_each(|(i, x)| *x = i);
+    XorShiftRng::from_seed(seed)
+}
+
+#[test]
+fn sort() {
+    let rng = seeded_rng();
+    let mut data: Vec<u32> = rng.sample_iter(&StandardUniform).take(6 * 1024).collect();
+    let mut sorted_data = data.clone();
+    sorted_data.sort();
+    quick_sort(&mut data);
+    assert_eq!(data, sorted_data);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn sort_in_pool() {
+    let rng = seeded_rng();
+    let mut data: Vec<u32> = rng.sample_iter(&StandardUniform).take(12 * 1024).collect();
+
+    let pool = ThreadPoolBuilder::new().build().unwrap();
+    let mut sorted_data = data.clone();
+    sorted_data.sort();
+    pool.install(|| quick_sort(&mut data));
+    assert_eq!(data, sorted_data);
+}
+
+#[test]
+#[should_panic(expected = "Hello, world!")]
+fn panic_propagate_a() {
+    join(|| panic!("Hello, world!"), || ());
+}
+
+#[test]
+#[should_panic(expected = "Hello, world!")]
+fn panic_propagate_b() {
+    join(|| (), || panic!("Hello, world!"));
+}
+
+#[test]
+#[should_panic(expected = "Hello, world!")]
+fn panic_propagate_both() {
+    join(|| panic!("Hello, world!"), || panic!("Goodbye, world!"));
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn panic_b_still_executes() {
+    let mut x = false;
+    match unwind::halt_unwinding(|| join(|| panic!("Hello, world!"), || x = true)) {
+        Ok(_) => panic!("failed to propagate panic from closure A,"),
+        Err(_) => assert!(x, "closure b failed to execute"),
+    }
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn join_context_both() {
+    // If we're not in a pool, both should be marked stolen as they're injected.
+    let (a_migrated, b_migrated) = join_context(|a| a.migrated(), |b| b.migrated());
+    assert!(a_migrated);
+    assert!(b_migrated);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn join_context_neither() {
+    // If we're already in a 1-thread pool, neither job should be stolen.
+    let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
+    let (a_migrated, b_migrated) =
+        pool.install(|| join_context(|a| a.migrated(), |b| b.migrated()));
+    assert!(!a_migrated);
+    assert!(!b_migrated);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn join_context_second() {
+    use std::sync::Barrier;
+
+    // If we're already in a 2-thread pool, the second job should be stolen.
+    let barrier = Barrier::new(2);
+    let pool = ThreadPoolBuilder::new().num_threads(2).build().unwrap();
+    let (a_migrated, b_migrated) = pool.install(|| {
+        join_context(
+            |a| {
+                barrier.wait();
+                a.migrated()
+            },
+            |b| {
+                barrier.wait();
+                b.migrated()
+            },
+        )
+    });
+    assert!(!a_migrated);
+    assert!(b_migrated);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn join_counter_overflow() {
+    const MAX: u32 = 500_000;
+
+    let mut i = 0;
+    let mut j = 0;
+    let pool = ThreadPoolBuilder::new().num_threads(2).build().unwrap();
+
+    // Hammer on join a bunch of times -- used to hit overflow debug-assertions
+    // in JEC on 32-bit targets: https://github.com/rayon-rs/rayon/issues/797
+    for _ in 0..MAX {
+        pool.join(|| i += 1, || j += 1);
+    }
+
+    assert_eq!(i, MAX);
+    assert_eq!(j, MAX);
+}
diff --git a/compiler/rustc_thread_pool/src/latch.rs b/compiler/rustc_thread_pool/src/latch.rs
new file mode 100644
index 00000000000..49ba62d3bea
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/latch.rs
@@ -0,0 +1,431 @@
+use std::marker::PhantomData;
+use std::ops::Deref;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::{Arc, Condvar, Mutex};
+
+use crate::registry::{Registry, WorkerThread};
+
+/// We define various kinds of latches, which are all a primitive signaling
+/// mechanism. A latch starts as false. Eventually someone calls `set()` and
+/// it becomes true. You can test if it has been set by calling `probe()`.
+///
+/// Some kinds of latches, but not all, support a `wait()` operation
+/// that will wait until the latch is set, blocking efficiently. That
+/// is not part of the trait since it is not possibly to do with all
+/// latches.
+///
+/// The intention is that `set()` is called once, but `probe()` may be
+/// called any number of times. Once `probe()` returns true, the memory
+/// effects that occurred before `set()` become visible.
+///
+/// It'd probably be better to refactor the API into two paired types,
+/// but that's a bit of work, and this is not a public API.
+///
+/// ## Memory ordering
+///
+/// Latches need to guarantee two things:
+///
+/// - Once `probe()` returns true, all memory effects from the `set()`
+///   are visible (in other words, the set should synchronize-with
+///   the probe).
+/// - Once `set()` occurs, the next `probe()` *will* observe it. This
+///   typically requires a seq-cst ordering. See [the "tickle-then-get-sleepy" scenario in the sleep
+///   README](/src/sleep/README.md#tickle-then-get-sleepy) for details.
+pub(super) trait Latch {
+    /// Set the latch, signalling others.
+    ///
+    /// # WARNING
+    ///
+    /// Setting a latch triggers other threads to wake up and (in some
+    /// cases) complete. This may, in turn, cause memory to be
+    /// deallocated and so forth. One must be very careful about this,
+    /// and it's typically better to read all the fields you will need
+    /// to access *before* a latch is set!
+    ///
+    /// This function operates on `*const Self` instead of `&self` to allow it
+    /// to become dangling during this call. The caller must ensure that the
+    /// pointer is valid upon entry, and not invalidated during the call by any
+    /// actions other than `set` itself.
+    unsafe fn set(this: *const Self);
+}
+
+pub(super) trait AsCoreLatch {
+    fn as_core_latch(&self) -> &CoreLatch;
+}
+
+/// Latch is not set, owning thread is awake
+const UNSET: usize = 0;
+
+/// Latch is not set, owning thread is going to sleep on this latch
+/// (but has not yet fallen asleep).
+const SLEEPY: usize = 1;
+
+/// Latch is not set, owning thread is asleep on this latch and
+/// must be awoken.
+const SLEEPING: usize = 2;
+
+/// Latch is set.
+const SET: usize = 3;
+
+/// Spin latches are the simplest, most efficient kind, but they do
+/// not support a `wait()` operation. They just have a boolean flag
+/// that becomes true when `set()` is called.
+#[derive(Debug)]
+pub(super) struct CoreLatch {
+    state: AtomicUsize,
+}
+
+impl CoreLatch {
+    #[inline]
+    fn new() -> Self {
+        Self { state: AtomicUsize::new(0) }
+    }
+
+    /// Invoked by owning thread as it prepares to sleep. Returns true
+    /// if the owning thread may proceed to fall asleep, false if the
+    /// latch was set in the meantime.
+    #[inline]
+    pub(super) fn get_sleepy(&self) -> bool {
+        self.state.compare_exchange(UNSET, SLEEPY, Ordering::SeqCst, Ordering::Relaxed).is_ok()
+    }
+
+    /// Invoked by owning thread as it falls asleep sleep. Returns
+    /// true if the owning thread should block, or false if the latch
+    /// was set in the meantime.
+    #[inline]
+    pub(super) fn fall_asleep(&self) -> bool {
+        self.state.compare_exchange(SLEEPY, SLEEPING, Ordering::SeqCst, Ordering::Relaxed).is_ok()
+    }
+
+    /// Invoked by owning thread as it falls asleep sleep. Returns
+    /// true if the owning thread should block, or false if the latch
+    /// was set in the meantime.
+    #[inline]
+    pub(super) fn wake_up(&self) {
+        if !self.probe() {
+            let _ =
+                self.state.compare_exchange(SLEEPING, UNSET, Ordering::SeqCst, Ordering::Relaxed);
+        }
+    }
+
+    /// Set the latch. If this returns true, the owning thread was sleeping
+    /// and must be awoken.
+    ///
+    /// This is private because, typically, setting a latch involves
+    /// doing some wakeups; those are encapsulated in the surrounding
+    /// latch code.
+    #[inline]
+    unsafe fn set(this: *const Self) -> bool {
+        let old_state = unsafe { (*this).state.swap(SET, Ordering::AcqRel) };
+        old_state == SLEEPING
+    }
+
+    /// Test if this latch has been set.
+    #[inline]
+    pub(super) fn probe(&self) -> bool {
+        self.state.load(Ordering::Acquire) == SET
+    }
+}
+
+impl AsCoreLatch for CoreLatch {
+    #[inline]
+    fn as_core_latch(&self) -> &CoreLatch {
+        self
+    }
+}
+
+/// Spin latches are the simplest, most efficient kind, but they do
+/// not support a `wait()` operation. They just have a boolean flag
+/// that becomes true when `set()` is called.
+pub(super) struct SpinLatch<'r> {
+    core_latch: CoreLatch,
+    registry: &'r Arc<Registry>,
+    target_worker_index: usize,
+    cross: bool,
+}
+
+impl<'r> SpinLatch<'r> {
+    /// Creates a new spin latch that is owned by `thread`. This means
+    /// that `thread` is the only thread that should be blocking on
+    /// this latch -- it also means that when the latch is set, we
+    /// will wake `thread` if it is sleeping.
+    #[inline]
+    pub(super) fn new(thread: &'r WorkerThread) -> SpinLatch<'r> {
+        SpinLatch {
+            core_latch: CoreLatch::new(),
+            registry: thread.registry(),
+            target_worker_index: thread.index(),
+            cross: false,
+        }
+    }
+
+    /// Creates a new spin latch for cross-threadpool blocking. Notably, we
+    /// need to make sure the registry is kept alive after setting, so we can
+    /// safely call the notification.
+    #[inline]
+    pub(super) fn cross(thread: &'r WorkerThread) -> SpinLatch<'r> {
+        SpinLatch { cross: true, ..SpinLatch::new(thread) }
+    }
+
+    #[inline]
+    pub(super) fn probe(&self) -> bool {
+        self.core_latch.probe()
+    }
+}
+
+impl<'r> AsCoreLatch for SpinLatch<'r> {
+    #[inline]
+    fn as_core_latch(&self) -> &CoreLatch {
+        &self.core_latch
+    }
+}
+
+impl<'r> Latch for SpinLatch<'r> {
+    #[inline]
+    unsafe fn set(this: *const Self) {
+        let cross_registry;
+
+        let registry: &Registry = if unsafe { (*this).cross } {
+            // Ensure the registry stays alive while we notify it.
+            // Otherwise, it would be possible that we set the spin
+            // latch and the other thread sees it and exits, causing
+            // the registry to be deallocated, all before we get a
+            // chance to invoke `registry.notify_worker_latch_is_set`.
+            cross_registry = Arc::clone(unsafe { (*this).registry });
+            &cross_registry
+        } else {
+            // If this is not a "cross-registry" spin-latch, then the
+            // thread which is performing `set` is itself ensuring
+            // that the registry stays alive. However, that doesn't
+            // include this *particular* `Arc` handle if the waiting
+            // thread then exits, so we must completely dereference it.
+            unsafe { (*this).registry }
+        };
+        let target_worker_index = unsafe { (*this).target_worker_index };
+
+        // NOTE: Once we `set`, the target may proceed and invalidate `this`!
+        if unsafe { CoreLatch::set(&(*this).core_latch) } {
+            // Subtle: at this point, we can no longer read from
+            // `self`, because the thread owning this spin latch may
+            // have awoken and deallocated the latch. Therefore, we
+            // only use fields whose values we already read.
+            registry.notify_worker_latch_is_set(target_worker_index);
+        }
+    }
+}
+
+/// A Latch starts as false and eventually becomes true. You can block
+/// until it becomes true.
+#[derive(Debug)]
+pub(super) struct LockLatch {
+    m: Mutex<bool>,
+    v: Condvar,
+}
+
+impl LockLatch {
+    #[inline]
+    pub(super) fn new() -> LockLatch {
+        LockLatch { m: Mutex::new(false), v: Condvar::new() }
+    }
+
+    /// Block until latch is set, then resets this lock latch so it can be reused again.
+    pub(super) fn wait_and_reset(&self) {
+        let mut guard = self.m.lock().unwrap();
+        while !*guard {
+            guard = self.v.wait(guard).unwrap();
+        }
+        *guard = false;
+    }
+
+    /// Block until latch is set.
+    pub(super) fn wait(&self) {
+        let mut guard = self.m.lock().unwrap();
+        while !*guard {
+            guard = self.v.wait(guard).unwrap();
+        }
+    }
+}
+
+impl Latch for LockLatch {
+    #[inline]
+    unsafe fn set(this: *const Self) {
+        let mut guard = unsafe { (*this).m.lock().unwrap() };
+        *guard = true;
+        unsafe { (*this).v.notify_all() };
+    }
+}
+
+/// Once latches are used to implement one-time blocking, primarily
+/// for the termination flag of the threads in the pool.
+///
+/// Note: like a `SpinLatch`, once-latches are always associated with
+/// some registry that is probing them, which must be tickled when
+/// they are set. *Unlike* a `SpinLatch`, they don't themselves hold a
+/// reference to that registry. This is because in some cases the
+/// registry owns the once-latch, and that would create a cycle. So a
+/// `OnceLatch` must be given a reference to its owning registry when
+/// it is set. For this reason, it does not implement the `Latch`
+/// trait (but it doesn't have to, as it is not used in those generic
+/// contexts).
+#[derive(Debug)]
+pub(super) struct OnceLatch {
+    core_latch: CoreLatch,
+}
+
+impl OnceLatch {
+    #[inline]
+    pub(super) fn new() -> OnceLatch {
+        Self { core_latch: CoreLatch::new() }
+    }
+
+    /// Set the latch, then tickle the specific worker thread,
+    /// which should be the one that owns this latch.
+    #[inline]
+    pub(super) unsafe fn set_and_tickle_one(
+        this: *const Self,
+        registry: &Registry,
+        target_worker_index: usize,
+    ) {
+        if unsafe { CoreLatch::set(&(*this).core_latch) } {
+            registry.notify_worker_latch_is_set(target_worker_index);
+        }
+    }
+}
+
+impl AsCoreLatch for OnceLatch {
+    #[inline]
+    fn as_core_latch(&self) -> &CoreLatch {
+        &self.core_latch
+    }
+}
+
+/// Counting latches are used to implement scopes. They track a
+/// counter. Unlike other latches, calling `set()` does not
+/// necessarily make the latch be considered `set()`; instead, it just
+/// decrements the counter. The latch is only "set" (in the sense that
+/// `probe()` returns true) once the counter reaches zero.
+#[derive(Debug)]
+pub(super) struct CountLatch {
+    counter: AtomicUsize,
+    kind: CountLatchKind,
+}
+
+enum CountLatchKind {
+    /// A latch for scopes created on a rayon thread which will participate in work-
+    /// stealing while it waits for completion. This thread is not necessarily part
+    /// of the same registry as the scope itself!
+    Stealing {
+        latch: CoreLatch,
+        /// If a worker thread in registry A calls `in_place_scope` on a ThreadPool
+        /// with registry B, when a job completes in a thread of registry B, we may
+        /// need to call `notify_worker_latch_is_set()` to wake the thread in registry A.
+        /// That means we need a reference to registry A (since at that point we will
+        /// only have a reference to registry B), so we stash it here.
+        registry: Arc<Registry>,
+        /// The index of the worker to wake in `registry`
+        worker_index: usize,
+    },
+
+    /// A latch for scopes created on a non-rayon thread which will block to wait.
+    Blocking { latch: LockLatch },
+}
+
+impl std::fmt::Debug for CountLatchKind {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            CountLatchKind::Stealing { latch, .. } => {
+                f.debug_tuple("Stealing").field(latch).finish()
+            }
+            CountLatchKind::Blocking { latch, .. } => {
+                f.debug_tuple("Blocking").field(latch).finish()
+            }
+        }
+    }
+}
+
+impl CountLatch {
+    pub(super) fn new(owner: Option<&WorkerThread>) -> Self {
+        Self::with_count(1, owner)
+    }
+
+    pub(super) fn with_count(count: usize, owner: Option<&WorkerThread>) -> Self {
+        Self {
+            counter: AtomicUsize::new(count),
+            kind: match owner {
+                Some(owner) => CountLatchKind::Stealing {
+                    latch: CoreLatch::new(),
+                    registry: Arc::clone(owner.registry()),
+                    worker_index: owner.index(),
+                },
+                None => CountLatchKind::Blocking { latch: LockLatch::new() },
+            },
+        }
+    }
+
+    #[inline]
+    pub(super) fn increment(&self) {
+        let old_counter = self.counter.fetch_add(1, Ordering::Relaxed);
+        debug_assert!(old_counter != 0);
+    }
+
+    pub(super) fn wait(&self, owner: Option<&WorkerThread>) {
+        match &self.kind {
+            CountLatchKind::Stealing { latch, registry, worker_index } => unsafe {
+                let owner = owner.expect("owner thread");
+                debug_assert_eq!(registry.id(), owner.registry().id());
+                debug_assert_eq!(*worker_index, owner.index());
+                owner.wait_until(latch);
+            },
+            CountLatchKind::Blocking { latch } => latch.wait(),
+        }
+    }
+}
+
+impl Latch for CountLatch {
+    #[inline]
+    unsafe fn set(this: *const Self) {
+        if unsafe { (*this).counter.fetch_sub(1, Ordering::SeqCst) == 1 } {
+            // NOTE: Once we call `set` on the internal `latch`,
+            // the target may proceed and invalidate `this`!
+            match unsafe { &(*this).kind } {
+                CountLatchKind::Stealing { latch, registry, worker_index } => {
+                    let registry = Arc::clone(registry);
+                    if unsafe { CoreLatch::set(latch) } {
+                        registry.notify_worker_latch_is_set(*worker_index);
+                    }
+                }
+                CountLatchKind::Blocking { latch } => unsafe { LockLatch::set(latch) },
+            }
+        }
+    }
+}
+
+/// `&L` without any implication of `dereferenceable` for `Latch::set`
+pub(super) struct LatchRef<'a, L> {
+    inner: *const L,
+    marker: PhantomData<&'a L>,
+}
+
+impl<L> LatchRef<'_, L> {
+    pub(super) fn new(inner: &L) -> LatchRef<'_, L> {
+        LatchRef { inner, marker: PhantomData }
+    }
+}
+
+unsafe impl<L: Sync> Sync for LatchRef<'_, L> {}
+
+impl<L> Deref for LatchRef<'_, L> {
+    type Target = L;
+
+    fn deref(&self) -> &L {
+        // SAFETY: if we have &self, the inner latch is still alive
+        unsafe { &*self.inner }
+    }
+}
+
+impl<L: Latch> Latch for LatchRef<'_, L> {
+    #[inline]
+    unsafe fn set(this: *const Self) {
+        unsafe { L::set((*this).inner) };
+    }
+}
diff --git a/compiler/rustc_thread_pool/src/lib.rs b/compiler/rustc_thread_pool/src/lib.rs
new file mode 100644
index 00000000000..34252d919e3
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/lib.rs
@@ -0,0 +1,903 @@
+//! Rayon-core houses the core stable APIs of Rayon.
+//!
+//! These APIs have been mirrored in the Rayon crate and it is recommended to use these from there.
+//!
+//! [`join`] is used to take two closures and potentially run them in parallel.
+//!   - It will run in parallel if task B gets stolen before task A can finish.
+//!   - It will run sequentially if task A finishes before task B is stolen and can continue on task B.
+//!
+//! [`scope`] creates a scope in which you can run any number of parallel tasks.
+//! These tasks can spawn nested tasks and scopes, but given the nature of work stealing, the order of execution can not be guaranteed.
+//! The scope will exist until all tasks spawned within the scope have been completed.
+//!
+//! [`spawn`] add a task into the 'static' or 'global' scope, or a local scope created by the [`scope()`] function.
+//!
+//! [`ThreadPool`] can be used to create your own thread pools (using [`ThreadPoolBuilder`]) or to customize the global one.
+//! Tasks spawned within the pool (using [`install()`], [`join()`], etc.) will be added to a deque,
+//! where it becomes available for work stealing from other threads in the local threadpool.
+//!
+//! [`join`]: fn.join.html
+//! [`scope`]: fn.scope.html
+//! [`scope()`]: fn.scope.html
+//! [`spawn`]: fn.spawn.html
+//! [`ThreadPool`]: struct.threadpool.html
+//! [`install()`]: struct.ThreadPool.html#method.install
+//! [`spawn()`]: struct.ThreadPool.html#method.spawn
+//! [`join()`]: struct.ThreadPool.html#method.join
+//! [`ThreadPoolBuilder`]: struct.ThreadPoolBuilder.html
+//!
+//! # Global fallback when threading is unsupported
+//!
+//! Rayon uses `std` APIs for threading, but some targets have incomplete implementations that
+//! always return `Unsupported` errors. The WebAssembly `wasm32-unknown-unknown` and `wasm32-wasi`
+//! targets are notable examples of this. Rather than panicking on the unsupported error when
+//! creating the implicit global threadpool, Rayon configures a fallback mode instead.
+//!
+//! This fallback mode mostly functions as if it were using a single-threaded "pool", like setting
+//! `RAYON_NUM_THREADS=1`. For example, `join` will execute its two closures sequentially, since
+//! there is no other thread to share the work. However, since the pool is not running independent
+//! of the main thread, non-blocking calls like `spawn` may not execute at all, unless a lower-
+//! priority call like `broadcast` gives them an opening. The fallback mode does not try to emulate
+//! anything like thread preemption or `async` task switching, but `yield_now` or `yield_local`
+//! can also volunteer execution time.
+//!
+//! Explicit `ThreadPoolBuilder` methods always report their error without any fallback.
+//!
+//! # Restricting multiple versions
+//!
+//! In order to ensure proper coordination between threadpools, and especially
+//! to make sure there's only one global threadpool, `rayon-core` is actively
+//! restricted from building multiple versions of itself into a single target.
+//! You may see a build error like this in violation:
+//!
+//! ```text
+//! error: native library `rayon-core` is being linked to by more
+//! than one package, and can only be linked to by one package
+//! ```
+//!
+//! While we strive to keep `rayon-core` semver-compatible, it's still
+//! possible to arrive at this situation if different crates have overly
+//! restrictive tilde or inequality requirements for `rayon-core`. The
+//! conflicting requirements will need to be resolved before the build will
+//! succeed.
+
+#![cfg_attr(test, allow(unused_crate_dependencies))]
+#![warn(rust_2018_idioms)]
+
+use std::any::Any;
+use std::error::Error;
+use std::marker::PhantomData;
+use std::str::FromStr;
+use std::{env, fmt, io, thread};
+
+#[macro_use]
+mod private;
+
+mod broadcast;
+mod job;
+mod join;
+mod latch;
+mod registry;
+mod scope;
+mod sleep;
+mod spawn;
+mod thread_pool;
+mod unwind;
+mod worker_local;
+
+mod compile_fail;
+mod tests;
+
+pub mod tlv;
+
+pub use worker_local::WorkerLocal;
+
+pub use self::broadcast::{BroadcastContext, broadcast, spawn_broadcast};
+pub use self::join::{join, join_context};
+use self::registry::{CustomSpawn, DefaultSpawn, ThreadSpawn};
+pub use self::registry::{Registry, ThreadBuilder, mark_blocked, mark_unblocked};
+pub use self::scope::{Scope, ScopeFifo, in_place_scope, in_place_scope_fifo, scope, scope_fifo};
+pub use self::spawn::{spawn, spawn_fifo};
+pub use self::thread_pool::{
+    ThreadPool, Yield, current_thread_has_pending_tasks, current_thread_index, yield_local,
+    yield_now,
+};
+
+/// Returns the maximum number of threads that Rayon supports in a single thread-pool.
+///
+/// If a higher thread count is requested by calling `ThreadPoolBuilder::num_threads` or by setting
+/// the `RAYON_NUM_THREADS` environment variable, then it will be reduced to this maximum.
+///
+/// The value may vary between different targets, and is subject to change in new Rayon versions.
+pub fn max_num_threads() -> usize {
+    // We are limited by the bits available in the sleep counter's `AtomicUsize`.
+    crate::sleep::THREADS_MAX
+}
+
+/// Returns the number of threads in the current registry. If this
+/// code is executing within a Rayon thread-pool, then this will be
+/// the number of threads for the thread-pool of the current
+/// thread. Otherwise, it will be the number of threads for the global
+/// thread-pool.
+///
+/// This can be useful when trying to judge how many times to split
+/// parallel work (the parallel iterator traits use this value
+/// internally for this purpose).
+///
+/// # Future compatibility note
+///
+/// Note that unless this thread-pool was created with a
+/// builder that specifies the number of threads, then this
+/// number may vary over time in future versions (see [the
+/// `num_threads()` method for details][snt]).
+///
+/// [snt]: struct.ThreadPoolBuilder.html#method.num_threads
+pub fn current_num_threads() -> usize {
+    crate::registry::Registry::current_num_threads()
+}
+
+/// Error when initializing a thread pool.
+#[derive(Debug)]
+pub struct ThreadPoolBuildError {
+    kind: ErrorKind,
+}
+
+#[derive(Debug)]
+enum ErrorKind {
+    GlobalPoolAlreadyInitialized,
+    IOError(io::Error),
+}
+
+/// Used to create a new [`ThreadPool`] or to configure the global rayon thread pool.
+/// ## Creating a ThreadPool
+/// The following creates a thread pool with 22 threads.
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// let pool = rayon::ThreadPoolBuilder::new().num_threads(22).build().unwrap();
+/// ```
+///
+/// To instead configure the global thread pool, use [`build_global()`]:
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// rayon::ThreadPoolBuilder::new().num_threads(22).build_global().unwrap();
+/// ```
+///
+/// [`ThreadPool`]: struct.ThreadPool.html
+/// [`build_global()`]: struct.ThreadPoolBuilder.html#method.build_global
+pub struct ThreadPoolBuilder<S = DefaultSpawn> {
+    /// The number of threads in the rayon thread pool.
+    /// If zero will use the RAYON_NUM_THREADS environment variable.
+    /// If RAYON_NUM_THREADS is invalid or zero will use the default.
+    num_threads: usize,
+
+    /// Custom closure, if any, to handle a panic that we cannot propagate
+    /// anywhere else.
+    panic_handler: Option<Box<PanicHandler>>,
+
+    /// Closure to compute the name of a thread.
+    get_thread_name: Option<Box<dyn FnMut(usize) -> String>>,
+
+    /// The stack size for the created worker threads
+    stack_size: Option<usize>,
+
+    /// Closure invoked on deadlock.
+    deadlock_handler: Option<Box<DeadlockHandler>>,
+
+    /// Closure invoked on worker thread start.
+    start_handler: Option<Box<StartHandler>>,
+
+    /// Closure invoked on worker thread exit.
+    exit_handler: Option<Box<ExitHandler>>,
+
+    /// Closure invoked to spawn threads.
+    spawn_handler: S,
+
+    /// Closure invoked when starting computations in a thread.
+    acquire_thread_handler: Option<Box<AcquireThreadHandler>>,
+
+    /// Closure invoked when blocking in a thread.
+    release_thread_handler: Option<Box<ReleaseThreadHandler>>,
+
+    /// If false, worker threads will execute spawned jobs in a
+    /// "depth-first" fashion. If true, they will do a "breadth-first"
+    /// fashion. Depth-first is the default.
+    breadth_first: bool,
+}
+
+/// Contains the rayon thread pool configuration. Use [`ThreadPoolBuilder`] instead.
+///
+/// [`ThreadPoolBuilder`]: struct.ThreadPoolBuilder.html
+#[deprecated(note = "Use `ThreadPoolBuilder`")]
+#[derive(Default)]
+pub struct Configuration {
+    builder: ThreadPoolBuilder,
+}
+
+/// The type for a panic handling closure. Note that this same closure
+/// may be invoked multiple times in parallel.
+type PanicHandler = dyn Fn(Box<dyn Any + Send>) + Send + Sync;
+
+/// The type for a closure that gets invoked when the Rayon thread pool deadlocks
+type DeadlockHandler = dyn Fn() + Send + Sync;
+
+/// The type for a closure that gets invoked when a thread starts. The
+/// closure is passed the index of the thread on which it is invoked.
+/// Note that this same closure may be invoked multiple times in parallel.
+type StartHandler = dyn Fn(usize) + Send + Sync;
+
+/// The type for a closure that gets invoked when a thread exits. The
+/// closure is passed the index of the thread on which it is invoked.
+/// Note that this same closure may be invoked multiple times in parallel.
+type ExitHandler = dyn Fn(usize) + Send + Sync;
+
+// NB: We can't `#[derive(Default)]` because `S` is left ambiguous.
+impl Default for ThreadPoolBuilder {
+    fn default() -> Self {
+        ThreadPoolBuilder {
+            num_threads: 0,
+            panic_handler: None,
+            get_thread_name: None,
+            stack_size: None,
+            start_handler: None,
+            exit_handler: None,
+            deadlock_handler: None,
+            acquire_thread_handler: None,
+            release_thread_handler: None,
+            spawn_handler: DefaultSpawn,
+            breadth_first: false,
+        }
+    }
+}
+
+/// The type for a closure that gets invoked before starting computations in a thread.
+/// Note that this same closure may be invoked multiple times in parallel.
+type AcquireThreadHandler = dyn Fn() + Send + Sync;
+
+/// The type for a closure that gets invoked before blocking in a thread.
+/// Note that this same closure may be invoked multiple times in parallel.
+type ReleaseThreadHandler = dyn Fn() + Send + Sync;
+
+impl ThreadPoolBuilder {
+    /// Creates and returns a valid rayon thread pool builder, but does not initialize it.
+    pub fn new() -> Self {
+        Self::default()
+    }
+}
+
+/// Note: the `S: ThreadSpawn` constraint is an internal implementation detail for the
+/// default spawn and those set by [`spawn_handler`](#method.spawn_handler).
+impl<S> ThreadPoolBuilder<S>
+where
+    S: ThreadSpawn,
+{
+    /// Creates a new `ThreadPool` initialized using this configuration.
+    pub fn build(self) -> Result<ThreadPool, ThreadPoolBuildError> {
+        ThreadPool::build(self)
+    }
+
+    /// Initializes the global thread pool. This initialization is
+    /// **optional**. If you do not call this function, the thread pool
+    /// will be automatically initialized with the default
+    /// configuration. Calling `build_global` is not recommended, except
+    /// in two scenarios:
+    ///
+    /// - You wish to change the default configuration.
+    /// - You are running a benchmark, in which case initializing may
+    ///   yield slightly more consistent results, since the worker threads
+    ///   will already be ready to go even in the first iteration. But
+    ///   this cost is minimal.
+    ///
+    /// Initialization of the global thread pool happens exactly
+    /// once. Once started, the configuration cannot be
+    /// changed. Therefore, if you call `build_global` a second time, it
+    /// will return an error. An `Ok` result indicates that this
+    /// is the first initialization of the thread pool.
+    pub fn build_global(self) -> Result<(), ThreadPoolBuildError> {
+        let registry = registry::init_global_registry(self)?;
+        registry.wait_until_primed();
+        Ok(())
+    }
+}
+
+impl ThreadPoolBuilder {
+    /// Creates a scoped `ThreadPool` initialized using this configuration.
+    ///
+    /// This is a convenience function for building a pool using [`std::thread::scope`]
+    /// to spawn threads in a [`spawn_handler`](#method.spawn_handler).
+    /// The threads in this pool will start by calling `wrapper`, which should
+    /// do initialization and continue by calling `ThreadBuilder::run()`.
+    ///
+    /// [`std::thread::scope`]: https://doc.rust-lang.org/std/thread/fn.scope.html
+    ///
+    /// # Examples
+    ///
+    /// A scoped pool may be useful in combination with scoped thread-local variables.
+    ///
+    /// ```
+    /// # use rustc_thread_pool as rayon;
+    ///
+    /// scoped_tls::scoped_thread_local!(static POOL_DATA: Vec<i32>);
+    ///
+    /// fn main() -> Result<(), rayon::ThreadPoolBuildError> {
+    ///     let pool_data = vec![1, 2, 3];
+    ///
+    ///     // We haven't assigned any TLS data yet.
+    ///     assert!(!POOL_DATA.is_set());
+    ///
+    ///     rayon::ThreadPoolBuilder::new()
+    ///         .build_scoped(
+    ///             // Borrow `pool_data` in TLS for each thread.
+    ///             |thread| POOL_DATA.set(&pool_data, || thread.run()),
+    ///             // Do some work that needs the TLS data.
+    ///             |pool| pool.install(|| assert!(POOL_DATA.is_set())),
+    ///         )?;
+    ///
+    ///     // Once we've returned, `pool_data` is no longer borrowed.
+    ///     drop(pool_data);
+    ///     Ok(())
+    /// }
+    /// ```
+    pub fn build_scoped<W, F, R>(self, wrapper: W, with_pool: F) -> Result<R, ThreadPoolBuildError>
+    where
+        W: Fn(ThreadBuilder) + Sync, // expected to call `run()`
+        F: FnOnce(&ThreadPool) -> R,
+    {
+        std::thread::scope(|scope| {
+            let pool = self
+                .spawn_handler(|thread| {
+                    let mut builder = std::thread::Builder::new();
+                    if let Some(name) = thread.name() {
+                        builder = builder.name(name.to_string());
+                    }
+                    if let Some(size) = thread.stack_size() {
+                        builder = builder.stack_size(size);
+                    }
+                    builder.spawn_scoped(scope, || wrapper(thread))?;
+                    Ok(())
+                })
+                .build()?;
+            let result = unwind::halt_unwinding(|| with_pool(&pool));
+            pool.wait_until_stopped();
+            match result {
+                Ok(result) => Ok(result),
+                Err(err) => unwind::resume_unwinding(err),
+            }
+        })
+    }
+}
+
+impl<S> ThreadPoolBuilder<S> {
+    /// Sets a custom function for spawning threads.
+    ///
+    /// Note that the threads will not exit until after the pool is dropped. It
+    /// is up to the caller to wait for thread termination if that is important
+    /// for any invariants. For instance, threads created in [`std::thread::scope`]
+    /// will be joined before that scope returns, and this will block indefinitely
+    /// if the pool is leaked. Furthermore, the global thread pool doesn't terminate
+    /// until the entire process exits!
+    ///
+    /// # Examples
+    ///
+    /// A minimal spawn handler just needs to call `run()` from an independent thread.
+    ///
+    /// ```
+    /// # use rustc_thread_pool as rayon;
+    /// fn main() -> Result<(), rayon::ThreadPoolBuildError> {
+    ///     let pool = rayon::ThreadPoolBuilder::new()
+    ///         .spawn_handler(|thread| {
+    ///             std::thread::spawn(|| thread.run());
+    ///             Ok(())
+    ///         })
+    ///         .build()?;
+    ///
+    ///     pool.install(|| println!("Hello from my custom thread!"));
+    ///     Ok(())
+    /// }
+    /// ```
+    ///
+    /// The default spawn handler sets the name and stack size if given, and propagates
+    /// any errors from the thread builder.
+    ///
+    /// ```
+    /// # use rustc_thread_pool as rayon;
+    /// fn main() -> Result<(), rayon::ThreadPoolBuildError> {
+    ///     let pool = rayon::ThreadPoolBuilder::new()
+    ///         .spawn_handler(|thread| {
+    ///             let mut b = std::thread::Builder::new();
+    ///             if let Some(name) = thread.name() {
+    ///                 b = b.name(name.to_owned());
+    ///             }
+    ///             if let Some(stack_size) = thread.stack_size() {
+    ///                 b = b.stack_size(stack_size);
+    ///             }
+    ///             b.spawn(|| thread.run())?;
+    ///             Ok(())
+    ///         })
+    ///         .build()?;
+    ///
+    ///     pool.install(|| println!("Hello from my fully custom thread!"));
+    ///     Ok(())
+    /// }
+    /// ```
+    ///
+    /// This can also be used for a pool of scoped threads like [`crossbeam::scope`],
+    /// or [`std::thread::scope`] introduced in Rust 1.63, which is encapsulated in
+    /// [`build_scoped`](#method.build_scoped).
+    ///
+    /// [`crossbeam::scope`]: https://docs.rs/crossbeam/0.8/crossbeam/fn.scope.html
+    /// [`std::thread::scope`]: https://doc.rust-lang.org/std/thread/fn.scope.html
+    ///
+    /// ```
+    /// # use rustc_thread_pool as rayon;
+    /// fn main() -> Result<(), rayon::ThreadPoolBuildError> {
+    ///     std::thread::scope(|scope| {
+    ///         let pool = rayon::ThreadPoolBuilder::new()
+    ///             .spawn_handler(|thread| {
+    ///                 let mut builder = std::thread::Builder::new();
+    ///                 if let Some(name) = thread.name() {
+    ///                     builder = builder.name(name.to_string());
+    ///                 }
+    ///                 if let Some(size) = thread.stack_size() {
+    ///                     builder = builder.stack_size(size);
+    ///                 }
+    ///                 builder.spawn_scoped(scope, || {
+    ///                     // Add any scoped initialization here, then run!
+    ///                     thread.run()
+    ///                 })?;
+    ///                 Ok(())
+    ///             })
+    ///             .build()?;
+    ///
+    ///         pool.install(|| println!("Hello from my custom scoped thread!"));
+    ///         Ok(())
+    ///     })
+    /// }
+    /// ```
+    pub fn spawn_handler<F>(self, spawn: F) -> ThreadPoolBuilder<CustomSpawn<F>>
+    where
+        F: FnMut(ThreadBuilder) -> io::Result<()>,
+    {
+        ThreadPoolBuilder {
+            spawn_handler: CustomSpawn::new(spawn),
+            // ..self
+            num_threads: self.num_threads,
+            panic_handler: self.panic_handler,
+            get_thread_name: self.get_thread_name,
+            stack_size: self.stack_size,
+            start_handler: self.start_handler,
+            exit_handler: self.exit_handler,
+            deadlock_handler: self.deadlock_handler,
+            acquire_thread_handler: self.acquire_thread_handler,
+            release_thread_handler: self.release_thread_handler,
+            breadth_first: self.breadth_first,
+        }
+    }
+
+    /// Returns a reference to the current spawn handler.
+    fn get_spawn_handler(&mut self) -> &mut S {
+        &mut self.spawn_handler
+    }
+
+    /// Get the number of threads that will be used for the thread
+    /// pool. See `num_threads()` for more information.
+    fn get_num_threads(&self) -> usize {
+        if self.num_threads > 0 {
+            self.num_threads
+        } else {
+            let default = || thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
+
+            match env::var("RAYON_NUM_THREADS").ok().and_then(|s| usize::from_str(&s).ok()) {
+                Some(x @ 1..) => return x,
+                Some(0) => return default(),
+                _ => {}
+            }
+
+            // Support for deprecated `RAYON_RS_NUM_CPUS`.
+            match env::var("RAYON_RS_NUM_CPUS").ok().and_then(|s| usize::from_str(&s).ok()) {
+                Some(x @ 1..) => x,
+                _ => default(),
+            }
+        }
+    }
+
+    /// Get the thread name for the thread with the given index.
+    fn get_thread_name(&mut self, index: usize) -> Option<String> {
+        let f = self.get_thread_name.as_mut()?;
+        Some(f(index))
+    }
+
+    /// Sets a closure which takes a thread index and returns
+    /// the thread's name.
+    pub fn thread_name<F>(mut self, closure: F) -> Self
+    where
+        F: FnMut(usize) -> String + 'static,
+    {
+        self.get_thread_name = Some(Box::new(closure));
+        self
+    }
+
+    /// Sets the number of threads to be used in the rayon threadpool.
+    ///
+    /// If you specify a non-zero number of threads using this
+    /// function, then the resulting thread-pools are guaranteed to
+    /// start at most this number of threads.
+    ///
+    /// If `num_threads` is 0, or you do not call this function, then
+    /// the Rayon runtime will select the number of threads
+    /// automatically. At present, this is based on the
+    /// `RAYON_NUM_THREADS` environment variable (if set),
+    /// or the number of logical CPUs (otherwise).
+    /// In the future, however, the default behavior may
+    /// change to dynamically add or remove threads as needed.
+    ///
+    /// **Future compatibility warning:** Given the default behavior
+    /// may change in the future, if you wish to rely on a fixed
+    /// number of threads, you should use this function to specify
+    /// that number. To reproduce the current default behavior, you
+    /// may wish to use [`std::thread::available_parallelism`]
+    /// to query the number of CPUs dynamically.
+    ///
+    /// **Old environment variable:** `RAYON_NUM_THREADS` is a one-to-one
+    /// replacement of the now deprecated `RAYON_RS_NUM_CPUS` environment
+    /// variable. If both variables are specified, `RAYON_NUM_THREADS` will
+    /// be preferred.
+    pub fn num_threads(mut self, num_threads: usize) -> Self {
+        self.num_threads = num_threads;
+        self
+    }
+
+    /// Returns a copy of the current panic handler.
+    fn take_panic_handler(&mut self) -> Option<Box<PanicHandler>> {
+        self.panic_handler.take()
+    }
+
+    /// Normally, whenever Rayon catches a panic, it tries to
+    /// propagate it to someplace sensible, to try and reflect the
+    /// semantics of sequential execution. But in some cases,
+    /// particularly with the `spawn()` APIs, there is no
+    /// obvious place where we should propagate the panic to.
+    /// In that case, this panic handler is invoked.
+    ///
+    /// If no panic handler is set, the default is to abort the
+    /// process, under the principle that panics should not go
+    /// unobserved.
+    ///
+    /// If the panic handler itself panics, this will abort the
+    /// process. To prevent this, wrap the body of your panic handler
+    /// in a call to `std::panic::catch_unwind()`.
+    pub fn panic_handler<H>(mut self, panic_handler: H) -> Self
+    where
+        H: Fn(Box<dyn Any + Send>) + Send + Sync + 'static,
+    {
+        self.panic_handler = Some(Box::new(panic_handler));
+        self
+    }
+
+    /// Get the stack size of the worker threads
+    fn get_stack_size(&self) -> Option<usize> {
+        self.stack_size
+    }
+
+    /// Sets the stack size of the worker threads
+    pub fn stack_size(mut self, stack_size: usize) -> Self {
+        self.stack_size = Some(stack_size);
+        self
+    }
+
+    /// **(DEPRECATED)** Suggest to worker threads that they execute
+    /// spawned jobs in a "breadth-first" fashion.
+    ///
+    /// Typically, when a worker thread is idle or blocked, it will
+    /// attempt to execute the job from the *top* of its local deque of
+    /// work (i.e., the job most recently spawned). If this flag is set
+    /// to true, however, workers will prefer to execute in a
+    /// *breadth-first* fashion -- that is, they will search for jobs at
+    /// the *bottom* of their local deque. (At present, workers *always*
+    /// steal from the bottom of other workers' deques, regardless of
+    /// the setting of this flag.)
+    ///
+    /// If you think of the tasks as a tree, where a parent task
+    /// spawns its children in the tree, then this flag loosely
+    /// corresponds to doing a breadth-first traversal of the tree,
+    /// whereas the default would be to do a depth-first traversal.
+    ///
+    /// **Note that this is an "execution hint".** Rayon's task
+    /// execution is highly dynamic and the precise order in which
+    /// independent tasks are executed is not intended to be
+    /// guaranteed.
+    ///
+    /// This `breadth_first()` method is now deprecated per [RFC #1],
+    /// and in the future its effect may be removed. Consider using
+    /// [`scope_fifo()`] for a similar effect.
+    ///
+    /// [RFC #1]: https://github.com/rayon-rs/rfcs/blob/master/accepted/rfc0001-scope-scheduling.md
+    /// [`scope_fifo()`]: fn.scope_fifo.html
+    #[deprecated(note = "use `scope_fifo` and `spawn_fifo` for similar effect")]
+    pub fn breadth_first(mut self) -> Self {
+        self.breadth_first = true;
+        self
+    }
+
+    fn get_breadth_first(&self) -> bool {
+        self.breadth_first
+    }
+
+    /// Takes the current acquire thread callback, leaving `None`.
+    fn take_acquire_thread_handler(&mut self) -> Option<Box<AcquireThreadHandler>> {
+        self.acquire_thread_handler.take()
+    }
+
+    /// Set a callback to be invoked when starting computations in a thread.
+    pub fn acquire_thread_handler<H>(mut self, acquire_thread_handler: H) -> Self
+    where
+        H: Fn() + Send + Sync + 'static,
+    {
+        self.acquire_thread_handler = Some(Box::new(acquire_thread_handler));
+        self
+    }
+
+    /// Takes the current release thread callback, leaving `None`.
+    fn take_release_thread_handler(&mut self) -> Option<Box<ReleaseThreadHandler>> {
+        self.release_thread_handler.take()
+    }
+
+    /// Set a callback to be invoked when blocking in thread.
+    pub fn release_thread_handler<H>(mut self, release_thread_handler: H) -> Self
+    where
+        H: Fn() + Send + Sync + 'static,
+    {
+        self.release_thread_handler = Some(Box::new(release_thread_handler));
+        self
+    }
+
+    /// Takes the current deadlock callback, leaving `None`.
+    fn take_deadlock_handler(&mut self) -> Option<Box<DeadlockHandler>> {
+        self.deadlock_handler.take()
+    }
+
+    /// Set a callback to be invoked on current deadlock.
+    pub fn deadlock_handler<H>(mut self, deadlock_handler: H) -> Self
+    where
+        H: Fn() + Send + Sync + 'static,
+    {
+        self.deadlock_handler = Some(Box::new(deadlock_handler));
+        self
+    }
+
+    /// Takes the current thread start callback, leaving `None`.
+    fn take_start_handler(&mut self) -> Option<Box<StartHandler>> {
+        self.start_handler.take()
+    }
+
+    /// Sets a callback to be invoked on thread start.
+    ///
+    /// The closure is passed the index of the thread on which it is invoked.
+    /// Note that this same closure may be invoked multiple times in parallel.
+    /// If this closure panics, the panic will be passed to the panic handler.
+    /// If that handler returns, then startup will continue normally.
+    pub fn start_handler<H>(mut self, start_handler: H) -> Self
+    where
+        H: Fn(usize) + Send + Sync + 'static,
+    {
+        self.start_handler = Some(Box::new(start_handler));
+        self
+    }
+
+    /// Returns a current thread exit callback, leaving `None`.
+    fn take_exit_handler(&mut self) -> Option<Box<ExitHandler>> {
+        self.exit_handler.take()
+    }
+
+    /// Sets a callback to be invoked on thread exit.
+    ///
+    /// The closure is passed the index of the thread on which it is invoked.
+    /// Note that this same closure may be invoked multiple times in parallel.
+    /// If this closure panics, the panic will be passed to the panic handler.
+    /// If that handler returns, then the thread will exit normally.
+    pub fn exit_handler<H>(mut self, exit_handler: H) -> Self
+    where
+        H: Fn(usize) + Send + Sync + 'static,
+    {
+        self.exit_handler = Some(Box::new(exit_handler));
+        self
+    }
+}
+
+#[allow(deprecated)]
+impl Configuration {
+    /// Creates and return a valid rayon thread pool configuration, but does not initialize it.
+    pub fn new() -> Configuration {
+        Configuration { builder: ThreadPoolBuilder::new() }
+    }
+
+    /// Deprecated in favor of `ThreadPoolBuilder::build`.
+    pub fn build(self) -> Result<ThreadPool, Box<dyn Error + 'static>> {
+        self.builder.build().map_err(Box::from)
+    }
+
+    /// Deprecated in favor of `ThreadPoolBuilder::thread_name`.
+    pub fn thread_name<F>(mut self, closure: F) -> Self
+    where
+        F: FnMut(usize) -> String + 'static,
+    {
+        self.builder = self.builder.thread_name(closure);
+        self
+    }
+
+    /// Deprecated in favor of `ThreadPoolBuilder::num_threads`.
+    pub fn num_threads(mut self, num_threads: usize) -> Configuration {
+        self.builder = self.builder.num_threads(num_threads);
+        self
+    }
+
+    /// Deprecated in favor of `ThreadPoolBuilder::panic_handler`.
+    pub fn panic_handler<H>(mut self, panic_handler: H) -> Configuration
+    where
+        H: Fn(Box<dyn Any + Send>) + Send + Sync + 'static,
+    {
+        self.builder = self.builder.panic_handler(panic_handler);
+        self
+    }
+
+    /// Deprecated in favor of `ThreadPoolBuilder::stack_size`.
+    pub fn stack_size(mut self, stack_size: usize) -> Self {
+        self.builder = self.builder.stack_size(stack_size);
+        self
+    }
+
+    /// Deprecated in favor of `ThreadPoolBuilder::breadth_first`.
+    pub fn breadth_first(mut self) -> Self {
+        self.builder = self.builder.breadth_first();
+        self
+    }
+
+    /// Deprecated in favor of `ThreadPoolBuilder::start_handler`.
+    pub fn start_handler<H>(mut self, start_handler: H) -> Configuration
+    where
+        H: Fn(usize) + Send + Sync + 'static,
+    {
+        self.builder = self.builder.start_handler(start_handler);
+        self
+    }
+
+    /// Deprecated in favor of `ThreadPoolBuilder::exit_handler`.
+    pub fn exit_handler<H>(mut self, exit_handler: H) -> Configuration
+    where
+        H: Fn(usize) + Send + Sync + 'static,
+    {
+        self.builder = self.builder.exit_handler(exit_handler);
+        self
+    }
+
+    /// Returns a ThreadPoolBuilder with identical parameters.
+    fn into_builder(self) -> ThreadPoolBuilder {
+        self.builder
+    }
+}
+
+impl ThreadPoolBuildError {
+    fn new(kind: ErrorKind) -> ThreadPoolBuildError {
+        ThreadPoolBuildError { kind }
+    }
+
+    fn is_unsupported(&self) -> bool {
+        matches!(&self.kind, ErrorKind::IOError(e) if e.kind() == io::ErrorKind::Unsupported)
+    }
+}
+
+const GLOBAL_POOL_ALREADY_INITIALIZED: &str =
+    "The global thread pool has already been initialized.";
+
+impl Error for ThreadPoolBuildError {
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        match self.kind {
+            ErrorKind::GlobalPoolAlreadyInitialized => GLOBAL_POOL_ALREADY_INITIALIZED,
+            ErrorKind::IOError(ref e) => e.description(),
+        }
+    }
+
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        match &self.kind {
+            ErrorKind::GlobalPoolAlreadyInitialized => None,
+            ErrorKind::IOError(e) => Some(e),
+        }
+    }
+}
+
+impl fmt::Display for ThreadPoolBuildError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match &self.kind {
+            ErrorKind::GlobalPoolAlreadyInitialized => GLOBAL_POOL_ALREADY_INITIALIZED.fmt(f),
+            ErrorKind::IOError(e) => e.fmt(f),
+        }
+    }
+}
+
+/// Deprecated in favor of `ThreadPoolBuilder::build_global`.
+#[deprecated(note = "use `ThreadPoolBuilder::build_global`")]
+#[allow(deprecated)]
+pub fn initialize(config: Configuration) -> Result<(), Box<dyn Error>> {
+    config.into_builder().build_global().map_err(Box::from)
+}
+
+impl<S> fmt::Debug for ThreadPoolBuilder<S> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let ThreadPoolBuilder {
+            ref num_threads,
+            ref get_thread_name,
+            ref panic_handler,
+            ref stack_size,
+            ref deadlock_handler,
+            ref start_handler,
+            ref exit_handler,
+            ref acquire_thread_handler,
+            ref release_thread_handler,
+            spawn_handler: _,
+            ref breadth_first,
+        } = *self;
+
+        // Just print `Some(<closure>)` or `None` to the debug
+        // output.
+        struct ClosurePlaceholder;
+        impl fmt::Debug for ClosurePlaceholder {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str("<closure>")
+            }
+        }
+        let get_thread_name = get_thread_name.as_ref().map(|_| ClosurePlaceholder);
+        let panic_handler = panic_handler.as_ref().map(|_| ClosurePlaceholder);
+        let deadlock_handler = deadlock_handler.as_ref().map(|_| ClosurePlaceholder);
+        let start_handler = start_handler.as_ref().map(|_| ClosurePlaceholder);
+        let exit_handler = exit_handler.as_ref().map(|_| ClosurePlaceholder);
+        let acquire_thread_handler = acquire_thread_handler.as_ref().map(|_| ClosurePlaceholder);
+        let release_thread_handler = release_thread_handler.as_ref().map(|_| ClosurePlaceholder);
+
+        f.debug_struct("ThreadPoolBuilder")
+            .field("num_threads", num_threads)
+            .field("get_thread_name", &get_thread_name)
+            .field("panic_handler", &panic_handler)
+            .field("stack_size", &stack_size)
+            .field("deadlock_handler", &deadlock_handler)
+            .field("start_handler", &start_handler)
+            .field("exit_handler", &exit_handler)
+            .field("acquire_thread_handler", &acquire_thread_handler)
+            .field("release_thread_handler", &release_thread_handler)
+            .field("breadth_first", &breadth_first)
+            .finish()
+    }
+}
+
+#[allow(deprecated)]
+impl fmt::Debug for Configuration {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.builder.fmt(f)
+    }
+}
+
+/// Provides the calling context to a closure called by `join_context`.
+#[derive(Debug)]
+pub struct FnContext {
+    migrated: bool,
+
+    /// disable `Send` and `Sync`, just for a little future-proofing.
+    _marker: PhantomData<*mut ()>,
+}
+
+impl FnContext {
+    #[inline]
+    fn new(migrated: bool) -> Self {
+        FnContext { migrated, _marker: PhantomData }
+    }
+}
+
+impl FnContext {
+    /// Returns `true` if the closure was called from a different thread
+    /// than it was provided from.
+    #[inline]
+    pub fn migrated(&self) -> bool {
+        self.migrated
+    }
+}
diff --git a/compiler/rustc_thread_pool/src/private.rs b/compiler/rustc_thread_pool/src/private.rs
new file mode 100644
index 00000000000..5d4f4a8c2ca
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/private.rs
@@ -0,0 +1,26 @@
+//! The public parts of this private module are used to create traits
+//! that cannot be implemented outside of our own crate. This way we
+//! can feel free to extend those traits without worrying about it
+//! being a breaking change for other implementations.
+
+/// If this type is pub but not publicly reachable, third parties
+/// can't name it and can't implement traits using it.
+#[allow(missing_debug_implementations)]
+pub struct PrivateMarker;
+
+macro_rules! private_decl {
+    () => {
+        /// This trait is private; this method exists to make it
+        /// impossible to implement outside the crate.
+        #[doc(hidden)]
+        fn __rayon_private__(&self) -> crate::private::PrivateMarker;
+    };
+}
+
+macro_rules! private_impl {
+    () => {
+        fn __rayon_private__(&self) -> crate::private::PrivateMarker {
+            crate::private::PrivateMarker
+        }
+    };
+}
diff --git a/compiler/rustc_thread_pool/src/registry.rs b/compiler/rustc_thread_pool/src/registry.rs
new file mode 100644
index 00000000000..03a01aa29d2
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/registry.rs
@@ -0,0 +1,1025 @@
+use std::cell::Cell;
+use std::collections::hash_map::DefaultHasher;
+use std::hash::Hasher;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::{Arc, Mutex, Once};
+use std::{fmt, io, mem, ptr, thread};
+
+use crossbeam_deque::{Injector, Steal, Stealer, Worker};
+
+use crate::job::{JobFifo, JobRef, StackJob};
+use crate::latch::{AsCoreLatch, CoreLatch, Latch, LatchRef, LockLatch, OnceLatch, SpinLatch};
+use crate::sleep::Sleep;
+use crate::tlv::Tlv;
+use crate::{
+    AcquireThreadHandler, DeadlockHandler, ErrorKind, ExitHandler, PanicHandler,
+    ReleaseThreadHandler, StartHandler, ThreadPoolBuildError, ThreadPoolBuilder, Yield, unwind,
+};
+
+/// Thread builder used for customization via
+/// [`ThreadPoolBuilder::spawn_handler`](struct.ThreadPoolBuilder.html#method.spawn_handler).
+pub struct ThreadBuilder {
+    name: Option<String>,
+    stack_size: Option<usize>,
+    worker: Worker<JobRef>,
+    stealer: Stealer<JobRef>,
+    registry: Arc<Registry>,
+    index: usize,
+}
+
+impl ThreadBuilder {
+    /// Gets the index of this thread in the pool, within `0..num_threads`.
+    pub fn index(&self) -> usize {
+        self.index
+    }
+
+    /// Gets the string that was specified by `ThreadPoolBuilder::name()`.
+    pub fn name(&self) -> Option<&str> {
+        self.name.as_deref()
+    }
+
+    /// Gets the value that was specified by `ThreadPoolBuilder::stack_size()`.
+    pub fn stack_size(&self) -> Option<usize> {
+        self.stack_size
+    }
+
+    /// Executes the main loop for this thread. This will not return until the
+    /// thread pool is dropped.
+    pub fn run(self) {
+        unsafe { main_loop(self) }
+    }
+}
+
+impl fmt::Debug for ThreadBuilder {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("ThreadBuilder")
+            .field("pool", &self.registry.id())
+            .field("index", &self.index)
+            .field("name", &self.name)
+            .field("stack_size", &self.stack_size)
+            .finish()
+    }
+}
+
+/// Generalized trait for spawning a thread in the `Registry`.
+///
+/// This trait is pub-in-private -- E0445 forces us to make it public,
+/// but we don't actually want to expose these details in the API.
+pub trait ThreadSpawn {
+    private_decl! {}
+
+    /// Spawn a thread with the `ThreadBuilder` parameters, and then
+    /// call `ThreadBuilder::run()`.
+    fn spawn(&mut self, thread: ThreadBuilder) -> io::Result<()>;
+}
+
+/// Spawns a thread in the "normal" way with `std::thread::Builder`.
+///
+/// This type is pub-in-private -- E0445 forces us to make it public,
+/// but we don't actually want to expose these details in the API.
+#[derive(Debug, Default)]
+pub struct DefaultSpawn;
+
+impl ThreadSpawn for DefaultSpawn {
+    private_impl! {}
+
+    fn spawn(&mut self, thread: ThreadBuilder) -> io::Result<()> {
+        let mut b = thread::Builder::new();
+        if let Some(name) = thread.name() {
+            b = b.name(name.to_owned());
+        }
+        if let Some(stack_size) = thread.stack_size() {
+            b = b.stack_size(stack_size);
+        }
+        b.spawn(|| thread.run())?;
+        Ok(())
+    }
+}
+
+/// Spawns a thread with a user's custom callback.
+///
+/// This type is pub-in-private -- E0445 forces us to make it public,
+/// but we don't actually want to expose these details in the API.
+#[derive(Debug)]
+pub struct CustomSpawn<F>(F);
+
+impl<F> CustomSpawn<F>
+where
+    F: FnMut(ThreadBuilder) -> io::Result<()>,
+{
+    pub(super) fn new(spawn: F) -> Self {
+        CustomSpawn(spawn)
+    }
+}
+
+impl<F> ThreadSpawn for CustomSpawn<F>
+where
+    F: FnMut(ThreadBuilder) -> io::Result<()>,
+{
+    private_impl! {}
+
+    #[inline]
+    fn spawn(&mut self, thread: ThreadBuilder) -> io::Result<()> {
+        (self.0)(thread)
+    }
+}
+
+pub struct Registry {
+    thread_infos: Vec<ThreadInfo>,
+    sleep: Sleep,
+    injected_jobs: Injector<JobRef>,
+    broadcasts: Mutex<Vec<Worker<JobRef>>>,
+    panic_handler: Option<Box<PanicHandler>>,
+    pub(crate) deadlock_handler: Option<Box<DeadlockHandler>>,
+    start_handler: Option<Box<StartHandler>>,
+    exit_handler: Option<Box<ExitHandler>>,
+    pub(crate) acquire_thread_handler: Option<Box<AcquireThreadHandler>>,
+    pub(crate) release_thread_handler: Option<Box<ReleaseThreadHandler>>,
+
+    // When this latch reaches 0, it means that all work on this
+    // registry must be complete. This is ensured in the following ways:
+    //
+    // - if this is the global registry, there is a ref-count that never
+    //   gets released.
+    // - if this is a user-created thread-pool, then so long as the thread-pool
+    //   exists, it holds a reference.
+    // - when we inject a "blocking job" into the registry with `ThreadPool::install()`,
+    //   no adjustment is needed; the `ThreadPool` holds the reference, and since we won't
+    //   return until the blocking job is complete, that ref will continue to be held.
+    // - when `join()` or `scope()` is invoked, similarly, no adjustments are needed.
+    //   These are always owned by some other job (e.g., one injected by `ThreadPool::install()`)
+    //   and that job will keep the pool alive.
+    terminate_count: AtomicUsize,
+}
+
+/// ////////////////////////////////////////////////////////////////////////
+/// Initialization
+
+static mut THE_REGISTRY: Option<Arc<Registry>> = None;
+static THE_REGISTRY_SET: Once = Once::new();
+
+/// Starts the worker threads (if that has not already happened). If
+/// initialization has not already occurred, use the default
+/// configuration.
+pub(super) fn global_registry() -> &'static Arc<Registry> {
+    set_global_registry(default_global_registry)
+        .or_else(|err| {
+            // SAFETY: we only create a shared reference to `THE_REGISTRY` after the `call_once`
+            // that initializes it, and there will be no more mutable accesses at all.
+            debug_assert!(THE_REGISTRY_SET.is_completed());
+            let the_registry = unsafe { &*ptr::addr_of!(THE_REGISTRY) };
+            the_registry.as_ref().ok_or(err)
+        })
+        .expect("The global thread pool has not been initialized.")
+}
+
+/// Starts the worker threads (if that has not already happened) with
+/// the given builder.
+pub(super) fn init_global_registry<S>(
+    builder: ThreadPoolBuilder<S>,
+) -> Result<&'static Arc<Registry>, ThreadPoolBuildError>
+where
+    S: ThreadSpawn,
+{
+    set_global_registry(|| Registry::new(builder))
+}
+
+/// Starts the worker threads (if that has not already happened)
+/// by creating a registry with the given callback.
+fn set_global_registry<F>(registry: F) -> Result<&'static Arc<Registry>, ThreadPoolBuildError>
+where
+    F: FnOnce() -> Result<Arc<Registry>, ThreadPoolBuildError>,
+{
+    let mut result = Err(ThreadPoolBuildError::new(ErrorKind::GlobalPoolAlreadyInitialized));
+
+    THE_REGISTRY_SET.call_once(|| {
+        result = registry().map(|registry: Arc<Registry>| {
+            // SAFETY: this is the only mutable access to `THE_REGISTRY`, thanks to `Once`, and
+            // `global_registry()` only takes a shared reference **after** this `call_once`.
+            unsafe {
+                ptr::addr_of_mut!(THE_REGISTRY).write(Some(registry));
+                (*ptr::addr_of!(THE_REGISTRY)).as_ref().unwrap_unchecked()
+            }
+        })
+    });
+
+    result
+}
+
+fn default_global_registry() -> Result<Arc<Registry>, ThreadPoolBuildError> {
+    let result = Registry::new(ThreadPoolBuilder::new());
+
+    // If we're running in an environment that doesn't support threads at all, we can fall back to
+    // using the current thread alone. This is crude, and probably won't work for non-blocking
+    // calls like `spawn` or `broadcast_spawn`, but a lot of stuff does work fine.
+    //
+    // Notably, this allows current WebAssembly targets to work even though their threading support
+    // is stubbed out, and we won't have to change anything if they do add real threading.
+    let unsupported = matches!(&result, Err(e) if e.is_unsupported());
+    if unsupported && WorkerThread::current().is_null() {
+        let builder = ThreadPoolBuilder::new().num_threads(1).spawn_handler(|thread| {
+            // Rather than starting a new thread, we're just taking over the current thread
+            // *without* running the main loop, so we can still return from here.
+            // The WorkerThread is leaked, but we never shutdown the global pool anyway.
+            let worker_thread = Box::leak(Box::new(WorkerThread::from(thread)));
+            let registry = &*worker_thread.registry;
+            let index = worker_thread.index;
+
+            unsafe {
+                WorkerThread::set_current(worker_thread);
+
+                // let registry know we are ready to do work
+                Latch::set(&registry.thread_infos[index].primed);
+            }
+
+            Ok(())
+        });
+
+        let fallback_result = Registry::new(builder);
+        if fallback_result.is_ok() {
+            return fallback_result;
+        }
+    }
+
+    result
+}
+
+struct Terminator<'a>(&'a Arc<Registry>);
+
+impl<'a> Drop for Terminator<'a> {
+    fn drop(&mut self) {
+        self.0.terminate()
+    }
+}
+
+impl Registry {
+    pub(super) fn new<S>(
+        mut builder: ThreadPoolBuilder<S>,
+    ) -> Result<Arc<Self>, ThreadPoolBuildError>
+    where
+        S: ThreadSpawn,
+    {
+        // Soft-limit the number of threads that we can actually support.
+        let n_threads = Ord::min(builder.get_num_threads(), crate::max_num_threads());
+
+        let breadth_first = builder.get_breadth_first();
+
+        let (workers, stealers): (Vec<_>, Vec<_>) = (0..n_threads)
+            .map(|_| {
+                let worker = if breadth_first { Worker::new_fifo() } else { Worker::new_lifo() };
+
+                let stealer = worker.stealer();
+                (worker, stealer)
+            })
+            .unzip();
+
+        let (broadcasts, broadcast_stealers): (Vec<_>, Vec<_>) = (0..n_threads)
+            .map(|_| {
+                let worker = Worker::new_fifo();
+                let stealer = worker.stealer();
+                (worker, stealer)
+            })
+            .unzip();
+
+        let registry = Arc::new(Registry {
+            thread_infos: stealers.into_iter().map(ThreadInfo::new).collect(),
+            sleep: Sleep::new(n_threads),
+            injected_jobs: Injector::new(),
+            broadcasts: Mutex::new(broadcasts),
+            terminate_count: AtomicUsize::new(1),
+            panic_handler: builder.take_panic_handler(),
+            deadlock_handler: builder.take_deadlock_handler(),
+            start_handler: builder.take_start_handler(),
+            exit_handler: builder.take_exit_handler(),
+            acquire_thread_handler: builder.take_acquire_thread_handler(),
+            release_thread_handler: builder.take_release_thread_handler(),
+        });
+
+        // If we return early or panic, make sure to terminate existing threads.
+        let t1000 = Terminator(&registry);
+
+        for (index, (worker, stealer)) in workers.into_iter().zip(broadcast_stealers).enumerate() {
+            let thread = ThreadBuilder {
+                name: builder.get_thread_name(index),
+                stack_size: builder.get_stack_size(),
+                registry: Arc::clone(&registry),
+                worker,
+                stealer,
+                index,
+            };
+            if let Err(e) = builder.get_spawn_handler().spawn(thread) {
+                return Err(ThreadPoolBuildError::new(ErrorKind::IOError(e)));
+            }
+        }
+
+        // Returning normally now, without termination.
+        mem::forget(t1000);
+
+        Ok(registry)
+    }
+
+    pub fn current() -> Arc<Registry> {
+        unsafe {
+            let worker_thread = WorkerThread::current();
+            let registry = if worker_thread.is_null() {
+                global_registry()
+            } else {
+                &(*worker_thread).registry
+            };
+            Arc::clone(registry)
+        }
+    }
+
+    /// Returns the number of threads in the current registry. This
+    /// is better than `Registry::current().num_threads()` because it
+    /// avoids incrementing the `Arc`.
+    pub(super) fn current_num_threads() -> usize {
+        unsafe {
+            let worker_thread = WorkerThread::current();
+            if worker_thread.is_null() {
+                global_registry().num_threads()
+            } else {
+                (*worker_thread).registry.num_threads()
+            }
+        }
+    }
+
+    /// Returns the current `WorkerThread` if it's part of this `Registry`.
+    pub(super) fn current_thread(&self) -> Option<&WorkerThread> {
+        unsafe {
+            let worker = WorkerThread::current().as_ref()?;
+            if worker.registry().id() == self.id() { Some(worker) } else { None }
+        }
+    }
+
+    /// Returns an opaque identifier for this registry.
+    pub(super) fn id(&self) -> RegistryId {
+        // We can rely on `self` not to change since we only ever create
+        // registries that are boxed up in an `Arc` (see `new()` above).
+        RegistryId { addr: self as *const Self as usize }
+    }
+
+    pub(super) fn num_threads(&self) -> usize {
+        self.thread_infos.len()
+    }
+
+    pub(super) fn catch_unwind(&self, f: impl FnOnce()) {
+        if let Err(err) = unwind::halt_unwinding(f) {
+            // If there is no handler, or if that handler itself panics, then we abort.
+            let abort_guard = unwind::AbortIfPanic;
+            if let Some(ref handler) = self.panic_handler {
+                handler(err);
+                mem::forget(abort_guard);
+            }
+        }
+    }
+
+    /// Waits for the worker threads to get up and running. This is
+    /// meant to be used for benchmarking purposes, primarily, so that
+    /// you can get more consistent numbers by having everything
+    /// "ready to go".
+    pub(super) fn wait_until_primed(&self) {
+        for info in &self.thread_infos {
+            info.primed.wait();
+        }
+    }
+
+    /// Waits for the worker threads to stop. This is used for testing
+    /// -- so we can check that termination actually works.
+    pub(super) fn wait_until_stopped(&self) {
+        self.release_thread();
+        for info in &self.thread_infos {
+            info.stopped.wait();
+        }
+        self.acquire_thread();
+    }
+
+    pub(crate) fn acquire_thread(&self) {
+        if let Some(ref acquire_thread_handler) = self.acquire_thread_handler {
+            acquire_thread_handler();
+        }
+    }
+
+    pub(crate) fn release_thread(&self) {
+        if let Some(ref release_thread_handler) = self.release_thread_handler {
+            release_thread_handler();
+        }
+    }
+
+    /// ////////////////////////////////////////////////////////////////////////
+    /// MAIN LOOP
+    ///
+    /// So long as all of the worker threads are hanging out in their
+    /// top-level loop, there is no work to be done.
+
+    /// Push a job into the given `registry`. If we are running on a
+    /// worker thread for the registry, this will push onto the
+    /// deque. Else, it will inject from the outside (which is slower).
+    pub(super) fn inject_or_push(&self, job_ref: JobRef) {
+        let worker_thread = WorkerThread::current();
+        unsafe {
+            if !worker_thread.is_null() && (*worker_thread).registry().id() == self.id() {
+                (*worker_thread).push(job_ref);
+            } else {
+                self.inject(job_ref);
+            }
+        }
+    }
+
+    /// Push a job into the "external jobs" queue; it will be taken by
+    /// whatever worker has nothing to do. Use this if you know that
+    /// you are not on a worker of this registry.
+    pub(super) fn inject(&self, injected_job: JobRef) {
+        // It should not be possible for `state.terminate` to be true
+        // here. It is only set to true when the user creates (and
+        // drops) a `ThreadPool`; and, in that case, they cannot be
+        // calling `inject()` later, since they dropped their
+        // `ThreadPool`.
+        debug_assert_ne!(
+            self.terminate_count.load(Ordering::Acquire),
+            0,
+            "inject() sees state.terminate as true"
+        );
+
+        let queue_was_empty = self.injected_jobs.is_empty();
+
+        self.injected_jobs.push(injected_job);
+        self.sleep.new_injected_jobs(1, queue_was_empty);
+    }
+
+    pub(crate) fn has_injected_job(&self) -> bool {
+        !self.injected_jobs.is_empty()
+    }
+
+    fn pop_injected_job(&self) -> Option<JobRef> {
+        loop {
+            match self.injected_jobs.steal() {
+                Steal::Success(job) => return Some(job),
+                Steal::Empty => return None,
+                Steal::Retry => {}
+            }
+        }
+    }
+
+    /// Push a job into each thread's own "external jobs" queue; it will be
+    /// executed only on that thread, when it has nothing else to do locally,
+    /// before it tries to steal other work.
+    ///
+    /// **Panics** if not given exactly as many jobs as there are threads.
+    pub(super) fn inject_broadcast(&self, injected_jobs: impl ExactSizeIterator<Item = JobRef>) {
+        assert_eq!(self.num_threads(), injected_jobs.len());
+        {
+            let broadcasts = self.broadcasts.lock().unwrap();
+
+            // It should not be possible for `state.terminate` to be true
+            // here. It is only set to true when the user creates (and
+            // drops) a `ThreadPool`; and, in that case, they cannot be
+            // calling `inject_broadcast()` later, since they dropped their
+            // `ThreadPool`.
+            debug_assert_ne!(
+                self.terminate_count.load(Ordering::Acquire),
+                0,
+                "inject_broadcast() sees state.terminate as true"
+            );
+
+            assert_eq!(broadcasts.len(), injected_jobs.len());
+            for (worker, job_ref) in broadcasts.iter().zip(injected_jobs) {
+                worker.push(job_ref);
+            }
+        }
+        for i in 0..self.num_threads() {
+            self.sleep.notify_worker_latch_is_set(i);
+        }
+    }
+
+    /// If already in a worker-thread of this registry, just execute `op`.
+    /// Otherwise, inject `op` in this thread-pool. Either way, block until `op`
+    /// completes and return its return value. If `op` panics, that panic will
+    /// be propagated as well. The second argument indicates `true` if injection
+    /// was performed, `false` if executed directly.
+    pub(super) fn in_worker<OP, R>(&self, op: OP) -> R
+    where
+        OP: FnOnce(&WorkerThread, bool) -> R + Send,
+        R: Send,
+    {
+        unsafe {
+            let worker_thread = WorkerThread::current();
+            if worker_thread.is_null() {
+                self.in_worker_cold(op)
+            } else if (*worker_thread).registry().id() != self.id() {
+                self.in_worker_cross(&*worker_thread, op)
+            } else {
+                // Perfectly valid to give them a `&T`: this is the
+                // current thread, so we know the data structure won't be
+                // invalidated until we return.
+                op(&*worker_thread, false)
+            }
+        }
+    }
+
+    #[cold]
+    unsafe fn in_worker_cold<OP, R>(&self, op: OP) -> R
+    where
+        OP: FnOnce(&WorkerThread, bool) -> R + Send,
+        R: Send,
+    {
+        thread_local!(static LOCK_LATCH: LockLatch = LockLatch::new());
+
+        LOCK_LATCH.with(|l| {
+            // This thread isn't a member of *any* thread pool, so just block.
+            debug_assert!(WorkerThread::current().is_null());
+            let job = StackJob::new(
+                Tlv::null(),
+                |injected| {
+                    let worker_thread = WorkerThread::current();
+                    assert!(injected && !worker_thread.is_null());
+                    op(unsafe { &*worker_thread }, true)
+                },
+                LatchRef::new(l),
+            );
+            self.inject(unsafe { job.as_job_ref() });
+            self.release_thread();
+            job.latch.wait_and_reset(); // Make sure we can use the same latch again next time.
+            self.acquire_thread();
+
+            unsafe { job.into_result() }
+        })
+    }
+
+    #[cold]
+    unsafe fn in_worker_cross<OP, R>(&self, current_thread: &WorkerThread, op: OP) -> R
+    where
+        OP: FnOnce(&WorkerThread, bool) -> R + Send,
+        R: Send,
+    {
+        // This thread is a member of a different pool, so let it process
+        // other work while waiting for this `op` to complete.
+        debug_assert!(current_thread.registry().id() != self.id());
+        let latch = SpinLatch::cross(current_thread);
+        let job = StackJob::new(
+            Tlv::null(),
+            |injected| {
+                let worker_thread = WorkerThread::current();
+                assert!(injected && !worker_thread.is_null());
+                op(unsafe { &*worker_thread }, true)
+            },
+            latch,
+        );
+        self.inject(unsafe { job.as_job_ref() });
+        unsafe { current_thread.wait_until(&job.latch) };
+        unsafe { job.into_result() }
+    }
+
+    /// Increments the terminate counter. This increment should be
+    /// balanced by a call to `terminate`, which will decrement. This
+    /// is used when spawning asynchronous work, which needs to
+    /// prevent the registry from terminating so long as it is active.
+    ///
+    /// Note that blocking functions such as `join` and `scope` do not
+    /// need to concern themselves with this fn; their context is
+    /// responsible for ensuring the current thread-pool will not
+    /// terminate until they return.
+    ///
+    /// The global thread-pool always has an outstanding reference
+    /// (the initial one). Custom thread-pools have one outstanding
+    /// reference that is dropped when the `ThreadPool` is dropped:
+    /// since installing the thread-pool blocks until any joins/scopes
+    /// complete, this ensures that joins/scopes are covered.
+    ///
+    /// The exception is `::spawn()`, which can create a job outside
+    /// of any blocking scope. In that case, the job itself holds a
+    /// terminate count and is responsible for invoking `terminate()`
+    /// when finished.
+    pub(super) fn increment_terminate_count(&self) {
+        let previous = self.terminate_count.fetch_add(1, Ordering::AcqRel);
+        debug_assert!(previous != 0, "registry ref count incremented from zero");
+        assert!(previous != usize::MAX, "overflow in registry ref count");
+    }
+
+    /// Signals that the thread-pool which owns this registry has been
+    /// dropped. The worker threads will gradually terminate, once any
+    /// extant work is completed.
+    pub(super) fn terminate(&self) {
+        if self.terminate_count.fetch_sub(1, Ordering::AcqRel) == 1 {
+            for (i, thread_info) in self.thread_infos.iter().enumerate() {
+                unsafe { OnceLatch::set_and_tickle_one(&thread_info.terminate, self, i) };
+            }
+        }
+    }
+
+    /// Notify the worker that the latch they are sleeping on has been "set".
+    pub(super) fn notify_worker_latch_is_set(&self, target_worker_index: usize) {
+        self.sleep.notify_worker_latch_is_set(target_worker_index);
+    }
+}
+
+/// Mark a Rayon worker thread as blocked. This triggers the deadlock handler
+/// if no other worker thread is active
+#[inline]
+pub fn mark_blocked() {
+    let worker_thread = WorkerThread::current();
+    assert!(!worker_thread.is_null());
+    unsafe {
+        let registry = &(*worker_thread).registry;
+        registry.sleep.mark_blocked(&registry.deadlock_handler)
+    }
+}
+
+/// Mark a previously blocked Rayon worker thread as unblocked
+#[inline]
+pub fn mark_unblocked(registry: &Registry) {
+    registry.sleep.mark_unblocked()
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub(super) struct RegistryId {
+    addr: usize,
+}
+
+struct ThreadInfo {
+    /// Latch set once thread has started and we are entering into the
+    /// main loop. Used to wait for worker threads to become primed,
+    /// primarily of interest for benchmarking.
+    primed: LockLatch,
+
+    /// Latch is set once worker thread has completed. Used to wait
+    /// until workers have stopped; only used for tests.
+    stopped: LockLatch,
+
+    /// The latch used to signal that terminated has been requested.
+    /// This latch is *set* by the `terminate` method on the
+    /// `Registry`, once the registry's main "terminate" counter
+    /// reaches zero.
+    terminate: OnceLatch,
+
+    /// the "stealer" half of the worker's deque
+    stealer: Stealer<JobRef>,
+}
+
+impl ThreadInfo {
+    fn new(stealer: Stealer<JobRef>) -> ThreadInfo {
+        ThreadInfo {
+            primed: LockLatch::new(),
+            stopped: LockLatch::new(),
+            terminate: OnceLatch::new(),
+            stealer,
+        }
+    }
+}
+
+/// ////////////////////////////////////////////////////////////////////////
+/// WorkerThread identifiers
+
+pub(super) struct WorkerThread {
+    /// the "worker" half of our local deque
+    worker: Worker<JobRef>,
+
+    /// the "stealer" half of the worker's broadcast deque
+    stealer: Stealer<JobRef>,
+
+    /// local queue used for `spawn_fifo` indirection
+    fifo: JobFifo,
+
+    pub(crate) index: usize,
+
+    /// A weak random number generator.
+    rng: XorShift64Star,
+
+    pub(crate) registry: Arc<Registry>,
+}
+
+// This is a bit sketchy, but basically: the WorkerThread is
+// allocated on the stack of the worker on entry and stored into this
+// thread local variable. So it will remain valid at least until the
+// worker is fully unwound. Using an unsafe pointer avoids the need
+// for a RefCell<T> etc.
+thread_local! {
+    static WORKER_THREAD_STATE: Cell<*const WorkerThread> = const { Cell::new(ptr::null()) };
+}
+
+impl From<ThreadBuilder> for WorkerThread {
+    fn from(thread: ThreadBuilder) -> Self {
+        Self {
+            worker: thread.worker,
+            stealer: thread.stealer,
+            fifo: JobFifo::new(),
+            index: thread.index,
+            rng: XorShift64Star::new(),
+            registry: thread.registry,
+        }
+    }
+}
+
+impl Drop for WorkerThread {
+    fn drop(&mut self) {
+        // Undo `set_current`
+        WORKER_THREAD_STATE.with(|t| {
+            assert!(t.get().eq(&(self as *const _)));
+            t.set(ptr::null());
+        });
+    }
+}
+
+impl WorkerThread {
+    /// Gets the `WorkerThread` index for the current thread; returns
+    /// NULL if this is not a worker thread. This pointer is valid
+    /// anywhere on the current thread.
+    #[inline]
+    pub(super) fn current() -> *const WorkerThread {
+        WORKER_THREAD_STATE.with(Cell::get)
+    }
+
+    /// Sets `self` as the worker thread index for the current thread.
+    /// This is done during worker thread startup.
+    unsafe fn set_current(thread: *const WorkerThread) {
+        WORKER_THREAD_STATE.with(|t| {
+            assert!(t.get().is_null());
+            t.set(thread);
+        });
+    }
+
+    /// Returns the registry that owns this worker thread.
+    #[inline]
+    pub(super) fn registry(&self) -> &Arc<Registry> {
+        &self.registry
+    }
+
+    /// Our index amongst the worker threads (ranges from `0..self.num_threads()`).
+    #[inline]
+    pub(super) fn index(&self) -> usize {
+        self.index
+    }
+
+    #[inline]
+    pub(super) unsafe fn push(&self, job: JobRef) {
+        let queue_was_empty = self.worker.is_empty();
+        self.worker.push(job);
+        self.registry.sleep.new_internal_jobs(1, queue_was_empty);
+    }
+
+    #[inline]
+    pub(super) unsafe fn push_fifo(&self, job: JobRef) {
+        unsafe { self.push(self.fifo.push(job)) };
+    }
+
+    #[inline]
+    pub(super) fn local_deque_is_empty(&self) -> bool {
+        self.worker.is_empty()
+    }
+
+    /// Attempts to obtain a "local" job -- typically this means
+    /// popping from the top of the stack, though if we are configured
+    /// for breadth-first execution, it would mean dequeuing from the
+    /// bottom.
+    #[inline]
+    pub(super) fn take_local_job(&self) -> Option<JobRef> {
+        let popped_job = self.worker.pop();
+
+        if popped_job.is_some() {
+            return popped_job;
+        }
+
+        loop {
+            match self.stealer.steal() {
+                Steal::Success(job) => return Some(job),
+                Steal::Empty => return None,
+                Steal::Retry => {}
+            }
+        }
+    }
+
+    pub(super) fn has_injected_job(&self) -> bool {
+        !self.stealer.is_empty() || self.registry.has_injected_job()
+    }
+
+    /// Wait until the latch is set. Try to keep busy by popping and
+    /// stealing tasks as necessary.
+    #[inline]
+    pub(super) unsafe fn wait_until<L: AsCoreLatch + ?Sized>(&self, latch: &L) {
+        let latch = latch.as_core_latch();
+        if !latch.probe() {
+            unsafe { self.wait_until_cold(latch) };
+        }
+    }
+
+    #[cold]
+    unsafe fn wait_until_cold(&self, latch: &CoreLatch) {
+        // the code below should swallow all panics and hence never
+        // unwind; but if something does wrong, we want to abort,
+        // because otherwise other code in rayon may assume that the
+        // latch has been signaled, and that can lead to random memory
+        // accesses, which would be *very bad*
+        let abort_guard = unwind::AbortIfPanic;
+
+        'outer: while !latch.probe() {
+            // Check for local work *before* we start marking ourself idle,
+            // especially to avoid modifying shared sleep state.
+            if let Some(job) = self.take_local_job() {
+                unsafe { self.execute(job) };
+                continue;
+            }
+
+            let mut idle_state = self.registry.sleep.start_looking(self.index);
+            while !latch.probe() {
+                if let Some(job) = self.find_work() {
+                    self.registry.sleep.work_found();
+                    unsafe { self.execute(job) };
+                    // The job might have injected local work, so go back to the outer loop.
+                    continue 'outer;
+                } else {
+                    self.registry.sleep.no_work_found(&mut idle_state, latch, &self)
+                }
+            }
+
+            // If we were sleepy, we are not anymore. We "found work" --
+            // whatever the surrounding thread was doing before it had to wait.
+            self.registry.sleep.work_found();
+            break;
+        }
+
+        mem::forget(abort_guard); // successful execution, do not abort
+    }
+
+    unsafe fn wait_until_out_of_work(&self) {
+        debug_assert_eq!(self as *const _, WorkerThread::current());
+        let registry = &*self.registry;
+        let index = self.index;
+
+        registry.acquire_thread();
+        unsafe { self.wait_until(&registry.thread_infos[index].terminate) };
+
+        // Should not be any work left in our queue.
+        debug_assert!(self.take_local_job().is_none());
+
+        // Let registry know we are done
+        unsafe { Latch::set(&registry.thread_infos[index].stopped) };
+    }
+
+    fn find_work(&self) -> Option<JobRef> {
+        // Try to find some work to do. We give preference first
+        // to things in our local deque, then in other workers
+        // deques, and finally to injected jobs from the
+        // outside. The idea is to finish what we started before
+        // we take on something new.
+        self.take_local_job().or_else(|| self.steal()).or_else(|| self.registry.pop_injected_job())
+    }
+
+    pub(super) fn yield_now(&self) -> Yield {
+        match self.find_work() {
+            Some(job) => unsafe {
+                self.execute(job);
+                Yield::Executed
+            },
+            None => Yield::Idle,
+        }
+    }
+
+    pub(super) fn yield_local(&self) -> Yield {
+        match self.take_local_job() {
+            Some(job) => unsafe {
+                self.execute(job);
+                Yield::Executed
+            },
+            None => Yield::Idle,
+        }
+    }
+
+    #[inline]
+    pub(super) unsafe fn execute(&self, job: JobRef) {
+        unsafe { job.execute() };
+    }
+
+    /// Try to steal a single job and return it.
+    ///
+    /// This should only be done as a last resort, when there is no
+    /// local work to do.
+    fn steal(&self) -> Option<JobRef> {
+        // we only steal when we don't have any work to do locally
+        debug_assert!(self.local_deque_is_empty());
+
+        // otherwise, try to steal
+        let thread_infos = &self.registry.thread_infos.as_slice();
+        let num_threads = thread_infos.len();
+        if num_threads <= 1 {
+            return None;
+        }
+
+        loop {
+            let mut retry = false;
+            let start = self.rng.next_usize(num_threads);
+            let job = (start..num_threads)
+                .chain(0..start)
+                .filter(move |&i| i != self.index)
+                .find_map(|victim_index| {
+                    let victim = &thread_infos[victim_index];
+                    match victim.stealer.steal() {
+                        Steal::Success(job) => Some(job),
+                        Steal::Empty => None,
+                        Steal::Retry => {
+                            retry = true;
+                            None
+                        }
+                    }
+                });
+            if job.is_some() || !retry {
+                return job;
+            }
+        }
+    }
+}
+
+/// ////////////////////////////////////////////////////////////////////////
+
+unsafe fn main_loop(thread: ThreadBuilder) {
+    let worker_thread = &WorkerThread::from(thread);
+    unsafe { WorkerThread::set_current(worker_thread) };
+    let registry = &*worker_thread.registry;
+    let index = worker_thread.index;
+
+    // let registry know we are ready to do work
+    unsafe { Latch::set(&registry.thread_infos[index].primed) };
+
+    // Worker threads should not panic. If they do, just abort, as the
+    // internal state of the threadpool is corrupted. Note that if
+    // **user code** panics, we should catch that and redirect.
+    let abort_guard = unwind::AbortIfPanic;
+
+    // Inform a user callback that we started a thread.
+    if let Some(ref handler) = registry.start_handler {
+        registry.catch_unwind(|| handler(index));
+    }
+
+    unsafe { worker_thread.wait_until_out_of_work() };
+
+    // Normal termination, do not abort.
+    mem::forget(abort_guard);
+
+    // Inform a user callback that we exited a thread.
+    if let Some(ref handler) = registry.exit_handler {
+        registry.catch_unwind(|| handler(index));
+        // We're already exiting the thread, there's nothing else to do.
+    }
+
+    registry.release_thread();
+}
+
+/// If already in a worker-thread, just execute `op`. Otherwise,
+/// execute `op` in the default thread-pool. Either way, block until
+/// `op` completes and return its return value. If `op` panics, that
+/// panic will be propagated as well. The second argument indicates
+/// `true` if injection was performed, `false` if executed directly.
+pub(super) fn in_worker<OP, R>(op: OP) -> R
+where
+    OP: FnOnce(&WorkerThread, bool) -> R + Send,
+    R: Send,
+{
+    unsafe {
+        let owner_thread = WorkerThread::current();
+        if !owner_thread.is_null() {
+            // Perfectly valid to give them a `&T`: this is the
+            // current thread, so we know the data structure won't be
+            // invalidated until we return.
+            op(&*owner_thread, false)
+        } else {
+            global_registry().in_worker(op)
+        }
+    }
+}
+
+/// [xorshift*] is a fast pseudorandom number generator which will
+/// even tolerate weak seeding, as long as it's not zero.
+///
+/// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift*
+struct XorShift64Star {
+    state: Cell<u64>,
+}
+
+impl XorShift64Star {
+    fn new() -> Self {
+        // Any non-zero seed will do -- this uses the hash of a global counter.
+        let mut seed = 0;
+        while seed == 0 {
+            let mut hasher = DefaultHasher::new();
+            static COUNTER: AtomicUsize = AtomicUsize::new(0);
+            hasher.write_usize(COUNTER.fetch_add(1, Ordering::Relaxed));
+            seed = hasher.finish();
+        }
+
+        XorShift64Star { state: Cell::new(seed) }
+    }
+
+    fn next(&self) -> u64 {
+        let mut x = self.state.get();
+        debug_assert_ne!(x, 0);
+        x ^= x >> 12;
+        x ^= x << 25;
+        x ^= x >> 27;
+        self.state.set(x);
+        x.wrapping_mul(0x2545_f491_4f6c_dd1d)
+    }
+
+    /// Return a value from `0..n`.
+    fn next_usize(&self, n: usize) -> usize {
+        (self.next() % n as u64) as usize
+    }
+}
diff --git a/compiler/rustc_thread_pool/src/scope/mod.rs b/compiler/rustc_thread_pool/src/scope/mod.rs
new file mode 100644
index 00000000000..55e58b3509d
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/scope/mod.rs
@@ -0,0 +1,783 @@
+//! Methods for custom fork-join scopes, created by the [`scope()`]
+//! and [`in_place_scope()`] functions. These are a more flexible alternative to [`join()`].
+//!
+//! [`scope()`]: fn.scope.html
+//! [`in_place_scope()`]: fn.in_place_scope.html
+//! [`join()`]: ../join/join.fn.html
+
+use std::any::Any;
+use std::marker::PhantomData;
+use std::mem::ManuallyDrop;
+use std::sync::Arc;
+use std::sync::atomic::{AtomicPtr, Ordering};
+use std::{fmt, ptr};
+
+use crate::broadcast::BroadcastContext;
+use crate::job::{ArcJob, HeapJob, JobFifo, JobRef};
+use crate::latch::{CountLatch, Latch};
+use crate::registry::{Registry, WorkerThread, global_registry, in_worker};
+use crate::tlv::{self, Tlv};
+use crate::unwind;
+
+#[cfg(test)]
+mod tests;
+
+/// Represents a fork-join scope which can be used to spawn any number of tasks.
+/// See [`scope()`] for more information.
+///
+///[`scope()`]: fn.scope.html
+pub struct Scope<'scope> {
+    base: ScopeBase<'scope>,
+}
+
+/// Represents a fork-join scope which can be used to spawn any number of tasks.
+/// Those spawned from the same thread are prioritized in relative FIFO order.
+/// See [`scope_fifo()`] for more information.
+///
+///[`scope_fifo()`]: fn.scope_fifo.html
+pub struct ScopeFifo<'scope> {
+    base: ScopeBase<'scope>,
+    fifos: Vec<JobFifo>,
+}
+
+struct ScopeBase<'scope> {
+    /// thread registry where `scope()` was executed or where `in_place_scope()`
+    /// should spawn jobs.
+    registry: Arc<Registry>,
+
+    /// if some job panicked, the error is stored here; it will be
+    /// propagated to the one who created the scope
+    panic: AtomicPtr<Box<dyn Any + Send + 'static>>,
+
+    /// latch to track job counts
+    job_completed_latch: CountLatch,
+
+    /// You can think of a scope as containing a list of closures to execute,
+    /// all of which outlive `'scope`. They're not actually required to be
+    /// `Sync`, but it's still safe to let the `Scope` implement `Sync` because
+    /// the closures are only *moved* across threads to be executed.
+    #[allow(clippy::type_complexity)]
+    marker: PhantomData<Box<dyn FnOnce(&Scope<'scope>) + Send + Sync + 'scope>>,
+
+    /// The TLV at the scope's creation. Used to set the TLV for spawned jobs.
+    tlv: Tlv,
+}
+
+/// Creates a "fork-join" scope `s` and invokes the closure with a
+/// reference to `s`. This closure can then spawn asynchronous tasks
+/// into `s`. Those tasks may run asynchronously with respect to the
+/// closure; they may themselves spawn additional tasks into `s`. When
+/// the closure returns, it will block until all tasks that have been
+/// spawned into `s` complete.
+///
+/// `scope()` is a more flexible building block compared to `join()`,
+/// since a loop can be used to spawn any number of tasks without
+/// recursing. However, that flexibility comes at a performance price:
+/// tasks spawned using `scope()` must be allocated onto the heap,
+/// whereas `join()` can make exclusive use of the stack. **Prefer
+/// `join()` (or, even better, parallel iterators) where possible.**
+///
+/// # Example
+///
+/// The Rayon `join()` function launches two closures and waits for them
+/// to stop. One could implement `join()` using a scope like so, although
+/// it would be less efficient than the real implementation:
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// pub fn join<A,B,RA,RB>(oper_a: A, oper_b: B) -> (RA, RB)
+///     where A: FnOnce() -> RA + Send,
+///           B: FnOnce() -> RB + Send,
+///           RA: Send,
+///           RB: Send,
+/// {
+///     let mut result_a: Option<RA> = None;
+///     let mut result_b: Option<RB> = None;
+///     rayon::scope(|s| {
+///         s.spawn(|_| result_a = Some(oper_a()));
+///         s.spawn(|_| result_b = Some(oper_b()));
+///     });
+///     (result_a.unwrap(), result_b.unwrap())
+/// }
+/// ```
+///
+/// # A note on threading
+///
+/// The closure given to `scope()` executes in the Rayon thread-pool,
+/// as do those given to `spawn()`. This means that you can't access
+/// thread-local variables (well, you can, but they may have
+/// unexpected values).
+///
+/// # Task execution
+///
+/// Task execution potentially starts as soon as `spawn()` is called.
+/// The task will end sometime before `scope()` returns. Note that the
+/// *closure* given to scope may return much earlier. In general
+/// the lifetime of a scope created like `scope(body)` goes something like this:
+///
+/// - Scope begins when `scope(body)` is called
+/// - Scope body `body()` is invoked
+///     - Scope tasks may be spawned
+/// - Scope body returns
+/// - Scope tasks execute, possibly spawning more tasks
+/// - Once all tasks are done, scope ends and `scope()` returns
+///
+/// To see how and when tasks are joined, consider this example:
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// // point start
+/// rayon::scope(|s| {
+///     s.spawn(|s| { // task s.1
+///         s.spawn(|s| { // task s.1.1
+///             rayon::scope(|t| {
+///                 t.spawn(|_| ()); // task t.1
+///                 t.spawn(|_| ()); // task t.2
+///             });
+///         });
+///     });
+///     s.spawn(|s| { // task s.2
+///     });
+///     // point mid
+/// });
+/// // point end
+/// ```
+///
+/// The various tasks that are run will execute roughly like so:
+///
+/// ```notrust
+/// | (start)
+/// |
+/// | (scope `s` created)
+/// +-----------------------------------------------+ (task s.2)
+/// +-------+ (task s.1)                            |
+/// |       |                                       |
+/// |       +---+ (task s.1.1)                      |
+/// |       |   |                                   |
+/// |       |   | (scope `t` created)               |
+/// |       |   +----------------+ (task t.2)       |
+/// |       |   +---+ (task t.1) |                  |
+/// | (mid) |   |   |            |                  |
+/// :       |   + <-+------------+ (scope `t` ends) |
+/// :       |   |                                   |
+/// |<------+---+-----------------------------------+ (scope `s` ends)
+/// |
+/// | (end)
+/// ```
+///
+/// The point here is that everything spawned into scope `s` will
+/// terminate (at latest) at the same point -- right before the
+/// original call to `rayon::scope` returns. This includes new
+/// subtasks created by other subtasks (e.g., task `s.1.1`). If a new
+/// scope is created (such as `t`), the things spawned into that scope
+/// will be joined before that scope returns, which in turn occurs
+/// before the creating task (task `s.1.1` in this case) finishes.
+///
+/// There is no guaranteed order of execution for spawns in a scope,
+/// given that other threads may steal tasks at any time. However, they
+/// are generally prioritized in a LIFO order on the thread from which
+/// they were spawned. So in this example, absent any stealing, we can
+/// expect `s.2` to execute before `s.1`, and `t.2` before `t.1`. Other
+/// threads always steal from the other end of the deque, like FIFO
+/// order. The idea is that "recent" tasks are most likely to be fresh
+/// in the local CPU's cache, while other threads can steal older
+/// "stale" tasks. For an alternate approach, consider
+/// [`scope_fifo()`] instead.
+///
+/// [`scope_fifo()`]: fn.scope_fifo.html
+///
+/// # Accessing stack data
+///
+/// In general, spawned tasks may access stack data in place that
+/// outlives the scope itself. Other data must be fully owned by the
+/// spawned task.
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// let ok: Vec<i32> = vec![1, 2, 3];
+/// rayon::scope(|s| {
+///     let bad: Vec<i32> = vec![4, 5, 6];
+///     s.spawn(|_| {
+///         // We can access `ok` because outlives the scope `s`.
+///         println!("ok: {:?}", ok);
+///
+///         // If we just try to use `bad` here, the closure will borrow `bad`
+///         // (because we are just printing it out, and that only requires a
+///         // borrow), which will result in a compilation error. Read on
+///         // for options.
+///         // println!("bad: {:?}", bad);
+///    });
+/// });
+/// ```
+///
+/// As the comments example above suggest, to reference `bad` we must
+/// take ownership of it. One way to do this is to detach the closure
+/// from the surrounding stack frame, using the `move` keyword. This
+/// will cause it to take ownership of *all* the variables it touches,
+/// in this case including both `ok` *and* `bad`:
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// let ok: Vec<i32> = vec![1, 2, 3];
+/// rayon::scope(|s| {
+///     let bad: Vec<i32> = vec![4, 5, 6];
+///     s.spawn(move |_| {
+///         println!("ok: {:?}", ok);
+///         println!("bad: {:?}", bad);
+///     });
+///
+///     // That closure is fine, but now we can't use `ok` anywhere else,
+///     // since it is owned by the previous task:
+///     // s.spawn(|_| println!("ok: {:?}", ok));
+/// });
+/// ```
+///
+/// While this works, it could be a problem if we want to use `ok` elsewhere.
+/// There are two choices. We can keep the closure as a `move` closure, but
+/// instead of referencing the variable `ok`, we create a shadowed variable that
+/// is a borrow of `ok` and capture *that*:
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// let ok: Vec<i32> = vec![1, 2, 3];
+/// rayon::scope(|s| {
+///     let bad: Vec<i32> = vec![4, 5, 6];
+///     let ok: &Vec<i32> = &ok; // shadow the original `ok`
+///     s.spawn(move |_| {
+///         println!("ok: {:?}", ok); // captures the shadowed version
+///         println!("bad: {:?}", bad);
+///     });
+///
+///     // Now we too can use the shadowed `ok`, since `&Vec<i32>` references
+///     // can be shared freely. Note that we need a `move` closure here though,
+///     // because otherwise we'd be trying to borrow the shadowed `ok`,
+///     // and that doesn't outlive `scope`.
+///     s.spawn(move |_| println!("ok: {:?}", ok));
+/// });
+/// ```
+///
+/// Another option is not to use the `move` keyword but instead to take ownership
+/// of individual variables:
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// let ok: Vec<i32> = vec![1, 2, 3];
+/// rayon::scope(|s| {
+///     let bad: Vec<i32> = vec![4, 5, 6];
+///     s.spawn(|_| {
+///         // Transfer ownership of `bad` into a local variable (also named `bad`).
+///         // This will force the closure to take ownership of `bad` from the environment.
+///         let bad = bad;
+///         println!("ok: {:?}", ok); // `ok` is only borrowed.
+///         println!("bad: {:?}", bad); // refers to our local variable, above.
+///     });
+///
+///     s.spawn(|_| println!("ok: {:?}", ok)); // we too can borrow `ok`
+/// });
+/// ```
+///
+/// # Panics
+///
+/// If a panic occurs, either in the closure given to `scope()` or in
+/// any of the spawned jobs, that panic will be propagated and the
+/// call to `scope()` will panic. If multiple panics occurs, it is
+/// non-deterministic which of their panic values will propagate.
+/// Regardless, once a task is spawned using `scope.spawn()`, it will
+/// execute, even if the spawning task should later panic. `scope()`
+/// returns once all spawned jobs have completed, and any panics are
+/// propagated at that point.
+pub fn scope<'scope, OP, R>(op: OP) -> R
+where
+    OP: FnOnce(&Scope<'scope>) -> R + Send,
+    R: Send,
+{
+    in_worker(|owner_thread, _| {
+        let scope = Scope::<'scope>::new(Some(owner_thread), None);
+        scope.base.complete(Some(owner_thread), || op(&scope))
+    })
+}
+
+/// Creates a "fork-join" scope `s` with FIFO order, and invokes the
+/// closure with a reference to `s`. This closure can then spawn
+/// asynchronous tasks into `s`. Those tasks may run asynchronously with
+/// respect to the closure; they may themselves spawn additional tasks
+/// into `s`. When the closure returns, it will block until all tasks
+/// that have been spawned into `s` complete.
+///
+/// # Task execution
+///
+/// Tasks in a `scope_fifo()` run similarly to [`scope()`], but there's a
+/// difference in the order of execution. Consider a similar example:
+///
+/// [`scope()`]: fn.scope.html
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// // point start
+/// rayon::scope_fifo(|s| {
+///     s.spawn_fifo(|s| { // task s.1
+///         s.spawn_fifo(|s| { // task s.1.1
+///             rayon::scope_fifo(|t| {
+///                 t.spawn_fifo(|_| ()); // task t.1
+///                 t.spawn_fifo(|_| ()); // task t.2
+///             });
+///         });
+///     });
+///     s.spawn_fifo(|s| { // task s.2
+///     });
+///     // point mid
+/// });
+/// // point end
+/// ```
+///
+/// The various tasks that are run will execute roughly like so:
+///
+/// ```notrust
+/// | (start)
+/// |
+/// | (FIFO scope `s` created)
+/// +--------------------+ (task s.1)
+/// +-------+ (task s.2) |
+/// |       |            +---+ (task s.1.1)
+/// |       |            |   |
+/// |       |            |   | (FIFO scope `t` created)
+/// |       |            |   +----------------+ (task t.1)
+/// |       |            |   +---+ (task t.2) |
+/// | (mid) |            |   |   |            |
+/// :       |            |   + <-+------------+ (scope `t` ends)
+/// :       |            |   |
+/// |<------+------------+---+ (scope `s` ends)
+/// |
+/// | (end)
+/// ```
+///
+/// Under `scope_fifo()`, the spawns are prioritized in a FIFO order on
+/// the thread from which they were spawned, as opposed to `scope()`'s
+/// LIFO. So in this example, we can expect `s.1` to execute before
+/// `s.2`, and `t.1` before `t.2`. Other threads also steal tasks in
+/// FIFO order, as usual. Overall, this has roughly the same order as
+/// the now-deprecated [`breadth_first`] option, except the effect is
+/// isolated to a particular scope. If spawns are intermingled from any
+/// combination of `scope()` and `scope_fifo()`, or from different
+/// threads, their order is only specified with respect to spawns in the
+/// same scope and thread.
+///
+/// For more details on this design, see Rayon [RFC #1].
+///
+/// [`breadth_first`]: struct.ThreadPoolBuilder.html#method.breadth_first
+/// [RFC #1]: https://github.com/rayon-rs/rfcs/blob/master/accepted/rfc0001-scope-scheduling.md
+///
+/// # Panics
+///
+/// If a panic occurs, either in the closure given to `scope_fifo()` or
+/// in any of the spawned jobs, that panic will be propagated and the
+/// call to `scope_fifo()` will panic. If multiple panics occurs, it is
+/// non-deterministic which of their panic values will propagate.
+/// Regardless, once a task is spawned using `scope.spawn_fifo()`, it
+/// will execute, even if the spawning task should later panic.
+/// `scope_fifo()` returns once all spawned jobs have completed, and any
+/// panics are propagated at that point.
+pub fn scope_fifo<'scope, OP, R>(op: OP) -> R
+where
+    OP: FnOnce(&ScopeFifo<'scope>) -> R + Send,
+    R: Send,
+{
+    in_worker(|owner_thread, _| {
+        let scope = ScopeFifo::<'scope>::new(Some(owner_thread), None);
+        scope.base.complete(Some(owner_thread), || op(&scope))
+    })
+}
+
+/// Creates a "fork-join" scope `s` and invokes the closure with a
+/// reference to `s`. This closure can then spawn asynchronous tasks
+/// into `s`. Those tasks may run asynchronously with respect to the
+/// closure; they may themselves spawn additional tasks into `s`. When
+/// the closure returns, it will block until all tasks that have been
+/// spawned into `s` complete.
+///
+/// This is just like `scope()` except the closure runs on the same thread
+/// that calls `in_place_scope()`. Only work that it spawns runs in the
+/// thread pool.
+///
+/// # Panics
+///
+/// If a panic occurs, either in the closure given to `in_place_scope()` or in
+/// any of the spawned jobs, that panic will be propagated and the
+/// call to `in_place_scope()` will panic. If multiple panics occurs, it is
+/// non-deterministic which of their panic values will propagate.
+/// Regardless, once a task is spawned using `scope.spawn()`, it will
+/// execute, even if the spawning task should later panic. `in_place_scope()`
+/// returns once all spawned jobs have completed, and any panics are
+/// propagated at that point.
+pub fn in_place_scope<'scope, OP, R>(op: OP) -> R
+where
+    OP: FnOnce(&Scope<'scope>) -> R,
+{
+    do_in_place_scope(None, op)
+}
+
+pub(crate) fn do_in_place_scope<'scope, OP, R>(registry: Option<&Arc<Registry>>, op: OP) -> R
+where
+    OP: FnOnce(&Scope<'scope>) -> R,
+{
+    let thread = unsafe { WorkerThread::current().as_ref() };
+    let scope = Scope::<'scope>::new(thread, registry);
+    scope.base.complete(thread, || op(&scope))
+}
+
+/// Creates a "fork-join" scope `s` with FIFO order, and invokes the
+/// closure with a reference to `s`. This closure can then spawn
+/// asynchronous tasks into `s`. Those tasks may run asynchronously with
+/// respect to the closure; they may themselves spawn additional tasks
+/// into `s`. When the closure returns, it will block until all tasks
+/// that have been spawned into `s` complete.
+///
+/// This is just like `scope_fifo()` except the closure runs on the same thread
+/// that calls `in_place_scope_fifo()`. Only work that it spawns runs in the
+/// thread pool.
+///
+/// # Panics
+///
+/// If a panic occurs, either in the closure given to `in_place_scope_fifo()` or in
+/// any of the spawned jobs, that panic will be propagated and the
+/// call to `in_place_scope_fifo()` will panic. If multiple panics occurs, it is
+/// non-deterministic which of their panic values will propagate.
+/// Regardless, once a task is spawned using `scope.spawn_fifo()`, it will
+/// execute, even if the spawning task should later panic. `in_place_scope_fifo()`
+/// returns once all spawned jobs have completed, and any panics are
+/// propagated at that point.
+pub fn in_place_scope_fifo<'scope, OP, R>(op: OP) -> R
+where
+    OP: FnOnce(&ScopeFifo<'scope>) -> R,
+{
+    do_in_place_scope_fifo(None, op)
+}
+
+pub(crate) fn do_in_place_scope_fifo<'scope, OP, R>(registry: Option<&Arc<Registry>>, op: OP) -> R
+where
+    OP: FnOnce(&ScopeFifo<'scope>) -> R,
+{
+    let thread = unsafe { WorkerThread::current().as_ref() };
+    let scope = ScopeFifo::<'scope>::new(thread, registry);
+    scope.base.complete(thread, || op(&scope))
+}
+
+impl<'scope> Scope<'scope> {
+    fn new(owner: Option<&WorkerThread>, registry: Option<&Arc<Registry>>) -> Self {
+        let base = ScopeBase::new(owner, registry);
+        Scope { base }
+    }
+
+    /// Spawns a job into the fork-join scope `self`. This job will
+    /// execute sometime before the fork-join scope completes. The
+    /// job is specified as a closure, and this closure receives its
+    /// own reference to the scope `self` as argument. This can be
+    /// used to inject new jobs into `self`.
+    ///
+    /// # Returns
+    ///
+    /// Nothing. The spawned closures cannot pass back values to the
+    /// caller directly, though they can write to local variables on
+    /// the stack (if those variables outlive the scope) or
+    /// communicate through shared channels.
+    ///
+    /// (The intention is to eventually integrate with Rust futures to
+    /// support spawns of functions that compute a value.)
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// # use rustc_thread_pool as rayon;
+    /// let mut value_a = None;
+    /// let mut value_b = None;
+    /// let mut value_c = None;
+    /// rayon::scope(|s| {
+    ///     s.spawn(|s1| {
+    ///           // ^ this is the same scope as `s`; this handle `s1`
+    ///           //   is intended for use by the spawned task,
+    ///           //   since scope handles cannot cross thread boundaries.
+    ///
+    ///         value_a = Some(22);
+    ///
+    ///         // the scope `s` will not end until all these tasks are done
+    ///         s1.spawn(|_| {
+    ///             value_b = Some(44);
+    ///         });
+    ///     });
+    ///
+    ///     s.spawn(|_| {
+    ///         value_c = Some(66);
+    ///     });
+    /// });
+    /// assert_eq!(value_a, Some(22));
+    /// assert_eq!(value_b, Some(44));
+    /// assert_eq!(value_c, Some(66));
+    /// ```
+    ///
+    /// # See also
+    ///
+    /// The [`scope` function] has more extensive documentation about
+    /// task spawning.
+    ///
+    /// [`scope` function]: fn.scope.html
+    pub fn spawn<BODY>(&self, body: BODY)
+    where
+        BODY: FnOnce(&Scope<'scope>) + Send + 'scope,
+    {
+        let scope_ptr = ScopePtr(self);
+        let job = HeapJob::new(self.base.tlv, move || unsafe {
+            // SAFETY: this job will execute before the scope ends.
+            let scope = scope_ptr.as_ref();
+            ScopeBase::execute_job(&scope.base, move || body(scope))
+        });
+        let job_ref = self.base.heap_job_ref(job);
+
+        // Since `Scope` implements `Sync`, we can't be sure that we're still in a
+        // thread of this pool, so we can't just push to the local worker thread.
+        // Also, this might be an in-place scope.
+        self.base.registry.inject_or_push(job_ref);
+    }
+
+    /// Spawns a job into every thread of the fork-join scope `self`. This job will
+    /// execute on each thread sometime before the fork-join scope completes. The
+    /// job is specified as a closure, and this closure receives its own reference
+    /// to the scope `self` as argument, as well as a `BroadcastContext`.
+    pub fn spawn_broadcast<BODY>(&self, body: BODY)
+    where
+        BODY: Fn(&Scope<'scope>, BroadcastContext<'_>) + Send + Sync + 'scope,
+    {
+        let scope_ptr = ScopePtr(self);
+        let job = ArcJob::new(move || unsafe {
+            // SAFETY: this job will execute before the scope ends.
+            let scope = scope_ptr.as_ref();
+            let body = &body;
+            let func = move || BroadcastContext::with(move |ctx| body(scope, ctx));
+            ScopeBase::execute_job(&scope.base, func)
+        });
+        self.base.inject_broadcast(job)
+    }
+}
+
+impl<'scope> ScopeFifo<'scope> {
+    fn new(owner: Option<&WorkerThread>, registry: Option<&Arc<Registry>>) -> Self {
+        let base = ScopeBase::new(owner, registry);
+        let num_threads = base.registry.num_threads();
+        let fifos = (0..num_threads).map(|_| JobFifo::new()).collect();
+        ScopeFifo { base, fifos }
+    }
+
+    /// Spawns a job into the fork-join scope `self`. This job will
+    /// execute sometime before the fork-join scope completes. The
+    /// job is specified as a closure, and this closure receives its
+    /// own reference to the scope `self` as argument. This can be
+    /// used to inject new jobs into `self`.
+    ///
+    /// # See also
+    ///
+    /// This method is akin to [`Scope::spawn()`], but with a FIFO
+    /// priority. The [`scope_fifo` function] has more details about
+    /// this distinction.
+    ///
+    /// [`Scope::spawn()`]: struct.Scope.html#method.spawn
+    /// [`scope_fifo` function]: fn.scope_fifo.html
+    pub fn spawn_fifo<BODY>(&self, body: BODY)
+    where
+        BODY: FnOnce(&ScopeFifo<'scope>) + Send + 'scope,
+    {
+        let scope_ptr = ScopePtr(self);
+        let job = HeapJob::new(self.base.tlv, move || unsafe {
+            // SAFETY: this job will execute before the scope ends.
+            let scope = scope_ptr.as_ref();
+            ScopeBase::execute_job(&scope.base, move || body(scope))
+        });
+        let job_ref = self.base.heap_job_ref(job);
+
+        // If we're in the pool, use our scope's private fifo for this thread to execute
+        // in a locally-FIFO order. Otherwise, just use the pool's global injector.
+        match self.base.registry.current_thread() {
+            Some(worker) => {
+                let fifo = &self.fifos[worker.index()];
+                // SAFETY: this job will execute before the scope ends.
+                unsafe { worker.push(fifo.push(job_ref)) };
+            }
+            None => self.base.registry.inject(job_ref),
+        }
+    }
+
+    /// Spawns a job into every thread of the fork-join scope `self`. This job will
+    /// execute on each thread sometime before the fork-join scope completes. The
+    /// job is specified as a closure, and this closure receives its own reference
+    /// to the scope `self` as argument, as well as a `BroadcastContext`.
+    pub fn spawn_broadcast<BODY>(&self, body: BODY)
+    where
+        BODY: Fn(&ScopeFifo<'scope>, BroadcastContext<'_>) + Send + Sync + 'scope,
+    {
+        let scope_ptr = ScopePtr(self);
+        let job = ArcJob::new(move || unsafe {
+            // SAFETY: this job will execute before the scope ends.
+            let scope = scope_ptr.as_ref();
+            let body = &body;
+            let func = move || BroadcastContext::with(move |ctx| body(scope, ctx));
+            ScopeBase::execute_job(&scope.base, func)
+        });
+        self.base.inject_broadcast(job)
+    }
+}
+
+impl<'scope> ScopeBase<'scope> {
+    /// Creates the base of a new scope for the given registry
+    fn new(owner: Option<&WorkerThread>, registry: Option<&Arc<Registry>>) -> Self {
+        let registry = registry.unwrap_or_else(|| match owner {
+            Some(owner) => owner.registry(),
+            None => global_registry(),
+        });
+
+        ScopeBase {
+            registry: Arc::clone(registry),
+            panic: AtomicPtr::new(ptr::null_mut()),
+            job_completed_latch: CountLatch::new(owner),
+            marker: PhantomData,
+            tlv: tlv::get(),
+        }
+    }
+
+    fn heap_job_ref<FUNC>(&self, job: Box<HeapJob<FUNC>>) -> JobRef
+    where
+        FUNC: FnOnce() + Send + 'scope,
+    {
+        unsafe {
+            self.job_completed_latch.increment();
+            job.into_job_ref()
+        }
+    }
+
+    fn inject_broadcast<FUNC>(&self, job: Arc<ArcJob<FUNC>>)
+    where
+        FUNC: Fn() + Send + Sync + 'scope,
+    {
+        let n_threads = self.registry.num_threads();
+        let job_refs = (0..n_threads).map(|_| unsafe {
+            self.job_completed_latch.increment();
+            ArcJob::as_job_ref(&job)
+        });
+
+        self.registry.inject_broadcast(job_refs);
+    }
+
+    /// Executes `func` as a job, either aborting or executing as
+    /// appropriate.
+    fn complete<FUNC, R>(&self, owner: Option<&WorkerThread>, func: FUNC) -> R
+    where
+        FUNC: FnOnce() -> R,
+    {
+        let result = unsafe { Self::execute_job_closure(self, func) };
+        self.job_completed_latch.wait(owner);
+
+        // Restore the TLV if we ran some jobs while waiting
+        tlv::set(self.tlv);
+
+        self.maybe_propagate_panic();
+        result.unwrap() // only None if `op` panicked, and that would have been propagated
+    }
+
+    /// Executes `func` as a job, either aborting or executing as
+    /// appropriate.
+    unsafe fn execute_job<FUNC>(this: *const Self, func: FUNC)
+    where
+        FUNC: FnOnce(),
+    {
+        let _: Option<()> = unsafe { Self::execute_job_closure(this, func) };
+    }
+
+    /// Executes `func` as a job in scope. Adjusts the "job completed"
+    /// counters and also catches any panic and stores it into
+    /// `scope`.
+    unsafe fn execute_job_closure<FUNC, R>(this: *const Self, func: FUNC) -> Option<R>
+    where
+        FUNC: FnOnce() -> R,
+    {
+        let result = match unwind::halt_unwinding(func) {
+            Ok(r) => Some(r),
+            Err(err) => {
+                unsafe { (*this).job_panicked(err) };
+                None
+            }
+        };
+        unsafe { Latch::set(&(*this).job_completed_latch) };
+        result
+    }
+
+    fn job_panicked(&self, err: Box<dyn Any + Send + 'static>) {
+        // capture the first error we see, free the rest
+        if self.panic.load(Ordering::Relaxed).is_null() {
+            let nil = ptr::null_mut();
+            let mut err = ManuallyDrop::new(Box::new(err)); // box up the fat ptr
+            let err_ptr: *mut Box<dyn Any + Send + 'static> = &mut **err;
+            if self
+                .panic
+                .compare_exchange(nil, err_ptr, Ordering::Release, Ordering::Relaxed)
+                .is_ok()
+            {
+                // ownership now transferred into self.panic
+            } else {
+                // another panic raced in ahead of us, so drop ours
+                let _: Box<Box<_>> = ManuallyDrop::into_inner(err);
+            }
+        }
+    }
+
+    fn maybe_propagate_panic(&self) {
+        // propagate panic, if any occurred; at this point, all
+        // outstanding jobs have completed, so we can use a relaxed
+        // ordering:
+        let panic = self.panic.swap(ptr::null_mut(), Ordering::Relaxed);
+        if !panic.is_null() {
+            let value = unsafe { Box::from_raw(panic) };
+
+            // Restore the TLV if we ran some jobs while waiting
+            tlv::set(self.tlv);
+
+            unwind::resume_unwinding(*value);
+        }
+    }
+}
+
+impl<'scope> fmt::Debug for Scope<'scope> {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt.debug_struct("Scope")
+            .field("pool_id", &self.base.registry.id())
+            .field("panic", &self.base.panic)
+            .field("job_completed_latch", &self.base.job_completed_latch)
+            .finish()
+    }
+}
+
+impl<'scope> fmt::Debug for ScopeFifo<'scope> {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt.debug_struct("ScopeFifo")
+            .field("num_fifos", &self.fifos.len())
+            .field("pool_id", &self.base.registry.id())
+            .field("panic", &self.base.panic)
+            .field("job_completed_latch", &self.base.job_completed_latch)
+            .finish()
+    }
+}
+
+/// Used to capture a scope `&Self` pointer in jobs, without faking a lifetime.
+///
+/// Unsafe code is still required to dereference the pointer, but that's fine in
+/// scope jobs that are guaranteed to execute before the scope ends.
+struct ScopePtr<T>(*const T);
+
+// SAFETY: !Send for raw pointers is not for safety, just as a lint
+unsafe impl<T: Sync> Send for ScopePtr<T> {}
+
+// SAFETY: !Sync for raw pointers is not for safety, just as a lint
+unsafe impl<T: Sync> Sync for ScopePtr<T> {}
+
+impl<T> ScopePtr<T> {
+    // Helper to avoid disjoint captures of `scope_ptr.0`
+    unsafe fn as_ref(&self) -> &T {
+        unsafe { &*self.0 }
+    }
+}
diff --git a/compiler/rustc_thread_pool/src/scope/tests.rs b/compiler/rustc_thread_pool/src/scope/tests.rs
new file mode 100644
index 00000000000..2df3bc67e29
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/scope/tests.rs
@@ -0,0 +1,607 @@
+use std::iter::once;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::{Barrier, Mutex};
+use std::vec;
+
+use rand::{Rng, SeedableRng};
+use rand_xorshift::XorShiftRng;
+
+use crate::{Scope, ScopeFifo, ThreadPoolBuilder, scope, scope_fifo, unwind};
+
+#[test]
+fn scope_empty() {
+    scope(|_| {});
+}
+
+#[test]
+fn scope_result() {
+    let x = scope(|_| 22);
+    assert_eq!(x, 22);
+}
+
+#[test]
+fn scope_two() {
+    let counter = &AtomicUsize::new(0);
+    scope(|s| {
+        s.spawn(move |_| {
+            counter.fetch_add(1, Ordering::SeqCst);
+        });
+        s.spawn(move |_| {
+            counter.fetch_add(10, Ordering::SeqCst);
+        });
+    });
+
+    let v = counter.load(Ordering::SeqCst);
+    assert_eq!(v, 11);
+}
+
+#[test]
+fn scope_divide_and_conquer() {
+    let counter_p = &AtomicUsize::new(0);
+    scope(|s| s.spawn(move |s| divide_and_conquer(s, counter_p, 1024)));
+
+    let counter_s = &AtomicUsize::new(0);
+    divide_and_conquer_seq(counter_s, 1024);
+
+    let p = counter_p.load(Ordering::SeqCst);
+    let s = counter_s.load(Ordering::SeqCst);
+    assert_eq!(p, s);
+}
+
+fn divide_and_conquer<'scope>(scope: &Scope<'scope>, counter: &'scope AtomicUsize, size: usize) {
+    if size > 1 {
+        scope.spawn(move |scope| divide_and_conquer(scope, counter, size / 2));
+        scope.spawn(move |scope| divide_and_conquer(scope, counter, size / 2));
+    } else {
+        // count the leaves
+        counter.fetch_add(1, Ordering::SeqCst);
+    }
+}
+
+fn divide_and_conquer_seq(counter: &AtomicUsize, size: usize) {
+    if size > 1 {
+        divide_and_conquer_seq(counter, size / 2);
+        divide_and_conquer_seq(counter, size / 2);
+    } else {
+        // count the leaves
+        counter.fetch_add(1, Ordering::SeqCst);
+    }
+}
+
+struct Tree<T: Send> {
+    value: T,
+    children: Vec<Tree<T>>,
+}
+
+impl<T: Send> Tree<T> {
+    fn iter(&self) -> vec::IntoIter<&T> {
+        once(&self.value)
+            .chain(self.children.iter().flat_map(Tree::iter))
+            .collect::<Vec<_>>() // seems like it shouldn't be needed... but prevents overflow
+            .into_iter()
+    }
+
+    fn update<OP>(&mut self, op: OP)
+    where
+        OP: Fn(&mut T) + Sync,
+        T: Send,
+    {
+        scope(|s| self.update_in_scope(&op, s));
+    }
+
+    fn update_in_scope<'scope, OP>(&'scope mut self, op: &'scope OP, scope: &Scope<'scope>)
+    where
+        OP: Fn(&mut T) + Sync,
+    {
+        let Tree { ref mut value, ref mut children } = *self;
+        scope.spawn(move |scope| {
+            for child in children {
+                scope.spawn(move |scope| child.update_in_scope(op, scope));
+            }
+        });
+
+        op(value);
+    }
+}
+
+fn random_tree(depth: usize) -> Tree<u32> {
+    assert!(depth > 0);
+    let mut seed = <XorShiftRng as SeedableRng>::Seed::default();
+    (0..).zip(seed.as_mut()).for_each(|(i, x)| *x = i);
+    let mut rng = XorShiftRng::from_seed(seed);
+    random_tree1(depth, &mut rng)
+}
+
+fn random_tree1(depth: usize, rng: &mut XorShiftRng) -> Tree<u32> {
+    let children = if depth == 0 {
+        vec![]
+    } else {
+        (0..rng.random_range(0..4)) // somewhere between 0 and 3 children at each level
+            .map(|_| random_tree1(depth - 1, rng))
+            .collect()
+    };
+
+    Tree { value: rng.random_range(0..1_000_000), children }
+}
+
+#[test]
+fn update_tree() {
+    let mut tree: Tree<u32> = random_tree(10);
+    let values: Vec<u32> = tree.iter().cloned().collect();
+    tree.update(|v| *v += 1);
+    let new_values: Vec<u32> = tree.iter().cloned().collect();
+    assert_eq!(values.len(), new_values.len());
+    for (&i, &j) in values.iter().zip(&new_values) {
+        assert_eq!(i + 1, j);
+    }
+}
+
+/// Check that if you have a chain of scoped tasks where T0 spawns T1
+/// spawns T2 and so forth down to Tn, the stack space should not grow
+/// linearly with N. We test this by some unsafe hackery and
+/// permitting an approx 10% change with a 10x input change.
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn linear_stack_growth() {
+    let builder = ThreadPoolBuilder::new().num_threads(1);
+    let pool = builder.build().unwrap();
+    pool.install(|| {
+        let mut max_diff = Mutex::new(0);
+        let bottom_of_stack = 0;
+        scope(|s| the_final_countdown(s, &bottom_of_stack, &max_diff, 5));
+        let diff_when_5 = *max_diff.get_mut().unwrap() as f64;
+
+        scope(|s| the_final_countdown(s, &bottom_of_stack, &max_diff, 500));
+        let diff_when_500 = *max_diff.get_mut().unwrap() as f64;
+
+        let ratio = diff_when_5 / diff_when_500;
+        assert!(ratio > 0.9 && ratio < 1.1, "stack usage ratio out of bounds: {}", ratio);
+    });
+}
+
+fn the_final_countdown<'scope>(
+    s: &Scope<'scope>,
+    bottom_of_stack: &'scope i32,
+    max: &'scope Mutex<usize>,
+    n: usize,
+) {
+    let top_of_stack = 0;
+    let p = bottom_of_stack as *const i32 as usize;
+    let q = &top_of_stack as *const i32 as usize;
+    let diff = if p > q { p - q } else { q - p };
+
+    let mut data = max.lock().unwrap();
+    *data = Ord::max(diff, *data);
+
+    if n > 0 {
+        s.spawn(move |s| the_final_countdown(s, bottom_of_stack, max, n - 1));
+    }
+}
+
+#[test]
+#[should_panic(expected = "Hello, world!")]
+fn panic_propagate_scope() {
+    scope(|_| panic!("Hello, world!"));
+}
+
+#[test]
+#[should_panic(expected = "Hello, world!")]
+fn panic_propagate_spawn() {
+    scope(|s| s.spawn(|_| panic!("Hello, world!")));
+}
+
+#[test]
+#[should_panic(expected = "Hello, world!")]
+fn panic_propagate_nested_spawn() {
+    scope(|s| s.spawn(|s| s.spawn(|s| s.spawn(|_| panic!("Hello, world!")))));
+}
+
+#[test]
+#[should_panic(expected = "Hello, world!")]
+fn panic_propagate_nested_scope_spawn() {
+    scope(|s| s.spawn(|_| scope(|s| s.spawn(|_| panic!("Hello, world!")))));
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn panic_propagate_still_execute_1() {
+    let mut x = false;
+    let result = unwind::halt_unwinding(|| {
+        scope(|s| {
+            s.spawn(|_| panic!("Hello, world!")); // job A
+            s.spawn(|_| x = true); // job B, should still execute even though A panics
+        });
+    });
+    match result {
+        Ok(_) => panic!("failed to propagate panic"),
+        Err(_) => assert!(x, "job b failed to execute"),
+    }
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn panic_propagate_still_execute_2() {
+    let mut x = false;
+    let result = unwind::halt_unwinding(|| {
+        scope(|s| {
+            s.spawn(|_| x = true); // job B, should still execute even though A panics
+            s.spawn(|_| panic!("Hello, world!")); // job A
+        });
+    });
+    match result {
+        Ok(_) => panic!("failed to propagate panic"),
+        Err(_) => assert!(x, "job b failed to execute"),
+    }
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn panic_propagate_still_execute_3() {
+    let mut x = false;
+    let result = unwind::halt_unwinding(|| {
+        scope(|s| {
+            s.spawn(|_| x = true); // spawned job should still execute despite later panic
+            panic!("Hello, world!");
+        });
+    });
+    match result {
+        Ok(_) => panic!("failed to propagate panic"),
+        Err(_) => assert!(x, "panic after spawn, spawn failed to execute"),
+    }
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn panic_propagate_still_execute_4() {
+    let mut x = false;
+    let result = unwind::halt_unwinding(|| {
+        scope(|s| {
+            s.spawn(|_| panic!("Hello, world!"));
+            x = true;
+        });
+    });
+    match result {
+        Ok(_) => panic!("failed to propagate panic"),
+        Err(_) => assert!(x, "panic in spawn tainted scope"),
+    }
+}
+
+macro_rules! test_order {
+    ($scope:ident => $spawn:ident) => {{
+        let builder = ThreadPoolBuilder::new().num_threads(1);
+        let pool = builder.build().unwrap();
+        pool.install(|| {
+            let vec = Mutex::new(vec![]);
+            $scope(|scope| {
+                let vec = &vec;
+                for i in 0..10 {
+                    scope.$spawn(move |scope| {
+                        for j in 0..10 {
+                            scope.$spawn(move |_| {
+                                vec.lock().unwrap().push(i * 10 + j);
+                            });
+                        }
+                    });
+                }
+            });
+            vec.into_inner().unwrap()
+        })
+    }};
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn lifo_order() {
+    // In the absence of stealing, `scope()` runs its `spawn()` jobs in LIFO order.
+    let vec = test_order!(scope => spawn);
+    let expected: Vec<i32> = (0..100).rev().collect(); // LIFO -> reversed
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn fifo_order() {
+    // In the absence of stealing, `scope_fifo()` runs its `spawn_fifo()` jobs in FIFO order.
+    let vec = test_order!(scope_fifo => spawn_fifo);
+    let expected: Vec<i32> = (0..100).collect(); // FIFO -> natural order
+    assert_eq!(vec, expected);
+}
+
+macro_rules! test_nested_order {
+    ($outer_scope:ident => $outer_spawn:ident,
+     $inner_scope:ident => $inner_spawn:ident) => {{
+        let builder = ThreadPoolBuilder::new().num_threads(1);
+        let pool = builder.build().unwrap();
+        pool.install(|| {
+            let vec = Mutex::new(vec![]);
+            $outer_scope(|scope| {
+                let vec = &vec;
+                for i in 0..10 {
+                    scope.$outer_spawn(move |_| {
+                        $inner_scope(|scope| {
+                            for j in 0..10 {
+                                scope.$inner_spawn(move |_| {
+                                    vec.lock().unwrap().push(i * 10 + j);
+                                });
+                            }
+                        });
+                    });
+                }
+            });
+            vec.into_inner().unwrap()
+        })
+    }};
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn nested_lifo_order() {
+    // In the absence of stealing, `scope()` runs its `spawn()` jobs in LIFO order.
+    let vec = test_nested_order!(scope => spawn, scope => spawn);
+    let expected: Vec<i32> = (0..100).rev().collect(); // LIFO -> reversed
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn nested_fifo_order() {
+    // In the absence of stealing, `scope_fifo()` runs its `spawn_fifo()` jobs in FIFO order.
+    let vec = test_nested_order!(scope_fifo => spawn_fifo, scope_fifo => spawn_fifo);
+    let expected: Vec<i32> = (0..100).collect(); // FIFO -> natural order
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn nested_lifo_fifo_order() {
+    // LIFO on the outside, FIFO on the inside
+    let vec = test_nested_order!(scope => spawn, scope_fifo => spawn_fifo);
+    let expected: Vec<i32> = (0..10).rev().flat_map(|i| (0..10).map(move |j| i * 10 + j)).collect();
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn nested_fifo_lifo_order() {
+    // FIFO on the outside, LIFO on the inside
+    let vec = test_nested_order!(scope_fifo => spawn_fifo, scope => spawn);
+    let expected: Vec<i32> = (0..10).flat_map(|i| (0..10).rev().map(move |j| i * 10 + j)).collect();
+    assert_eq!(vec, expected);
+}
+
+macro_rules! spawn_push {
+    ($scope:ident . $spawn:ident, $vec:ident, $i:expr) => {{
+        $scope.$spawn(move |_| $vec.lock().unwrap().push($i));
+    }};
+}
+
+/// Test spawns pushing a series of numbers, interleaved
+/// such that negative values are using an inner scope.
+macro_rules! test_mixed_order {
+    ($outer_scope:ident => $outer_spawn:ident,
+     $inner_scope:ident => $inner_spawn:ident) => {{
+        let builder = ThreadPoolBuilder::new().num_threads(1);
+        let pool = builder.build().unwrap();
+        pool.install(|| {
+            let vec = Mutex::new(vec![]);
+            $outer_scope(|outer_scope| {
+                let vec = &vec;
+                spawn_push!(outer_scope.$outer_spawn, vec, 0);
+                $inner_scope(|inner_scope| {
+                    spawn_push!(inner_scope.$inner_spawn, vec, -1);
+                    spawn_push!(outer_scope.$outer_spawn, vec, 1);
+                    spawn_push!(inner_scope.$inner_spawn, vec, -2);
+                    spawn_push!(outer_scope.$outer_spawn, vec, 2);
+                    spawn_push!(inner_scope.$inner_spawn, vec, -3);
+                });
+                spawn_push!(outer_scope.$outer_spawn, vec, 3);
+            });
+            vec.into_inner().unwrap()
+        })
+    }};
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn mixed_lifo_order() {
+    // NB: the end of the inner scope makes us execute some of the outer scope
+    // before they've all been spawned, so they're not perfectly LIFO.
+    let vec = test_mixed_order!(scope => spawn, scope => spawn);
+    let expected = vec![-3, 2, -2, 1, -1, 3, 0];
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn mixed_fifo_order() {
+    let vec = test_mixed_order!(scope_fifo => spawn_fifo, scope_fifo => spawn_fifo);
+    let expected = vec![-1, 0, -2, 1, -3, 2, 3];
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn mixed_lifo_fifo_order() {
+    // NB: the end of the inner scope makes us execute some of the outer scope
+    // before they've all been spawned, so they're not perfectly LIFO.
+    let vec = test_mixed_order!(scope => spawn, scope_fifo => spawn_fifo);
+    let expected = vec![-1, 2, -2, 1, -3, 3, 0];
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn mixed_fifo_lifo_order() {
+    let vec = test_mixed_order!(scope_fifo => spawn_fifo, scope => spawn);
+    let expected = vec![-3, 0, -2, 1, -1, 2, 3];
+    assert_eq!(vec, expected);
+}
+
+#[test]
+fn static_scope() {
+    static COUNTER: AtomicUsize = AtomicUsize::new(0);
+
+    let mut range = 0..100;
+    let sum = range.clone().sum();
+    let iter = &mut range;
+
+    COUNTER.store(0, Ordering::Relaxed);
+    scope(|s: &Scope<'static>| {
+        // While we're allowed the locally borrowed iterator,
+        // the spawns must be static.
+        for i in iter {
+            s.spawn(move |_| {
+                COUNTER.fetch_add(i, Ordering::Relaxed);
+            });
+        }
+    });
+
+    assert_eq!(COUNTER.load(Ordering::Relaxed), sum);
+}
+
+#[test]
+fn static_scope_fifo() {
+    static COUNTER: AtomicUsize = AtomicUsize::new(0);
+
+    let mut range = 0..100;
+    let sum = range.clone().sum();
+    let iter = &mut range;
+
+    COUNTER.store(0, Ordering::Relaxed);
+    scope_fifo(|s: &ScopeFifo<'static>| {
+        // While we're allowed the locally borrowed iterator,
+        // the spawns must be static.
+        for i in iter {
+            s.spawn_fifo(move |_| {
+                COUNTER.fetch_add(i, Ordering::Relaxed);
+            });
+        }
+    });
+
+    assert_eq!(COUNTER.load(Ordering::Relaxed), sum);
+}
+
+#[test]
+fn mixed_lifetime_scope() {
+    fn increment<'slice, 'counter>(counters: &'slice [&'counter AtomicUsize]) {
+        scope(move |s: &Scope<'counter>| {
+            // We can borrow 'slice here, but the spawns can only borrow 'counter.
+            for &c in counters {
+                s.spawn(move |_| {
+                    c.fetch_add(1, Ordering::Relaxed);
+                });
+            }
+        });
+    }
+
+    let counter = AtomicUsize::new(0);
+    increment(&[&counter; 100]);
+    assert_eq!(counter.into_inner(), 100);
+}
+
+#[test]
+fn mixed_lifetime_scope_fifo() {
+    fn increment<'slice, 'counter>(counters: &'slice [&'counter AtomicUsize]) {
+        scope_fifo(move |s: &ScopeFifo<'counter>| {
+            // We can borrow 'slice here, but the spawns can only borrow 'counter.
+            for &c in counters {
+                s.spawn_fifo(move |_| {
+                    c.fetch_add(1, Ordering::Relaxed);
+                });
+            }
+        });
+    }
+
+    let counter = AtomicUsize::new(0);
+    increment(&[&counter; 100]);
+    assert_eq!(counter.into_inner(), 100);
+}
+
+#[test]
+fn scope_spawn_broadcast() {
+    let sum = AtomicUsize::new(0);
+    let n = scope(|s| {
+        s.spawn_broadcast(|_, ctx| {
+            sum.fetch_add(ctx.index(), Ordering::Relaxed);
+        });
+        crate::current_num_threads()
+    });
+    assert_eq!(sum.into_inner(), n * (n - 1) / 2);
+}
+
+#[test]
+fn scope_fifo_spawn_broadcast() {
+    let sum = AtomicUsize::new(0);
+    let n = scope_fifo(|s| {
+        s.spawn_broadcast(|_, ctx| {
+            sum.fetch_add(ctx.index(), Ordering::Relaxed);
+        });
+        crate::current_num_threads()
+    });
+    assert_eq!(sum.into_inner(), n * (n - 1) / 2);
+}
+
+#[test]
+fn scope_spawn_broadcast_nested() {
+    let sum = AtomicUsize::new(0);
+    let n = scope(|s| {
+        s.spawn_broadcast(|s, _| {
+            s.spawn_broadcast(|_, ctx| {
+                sum.fetch_add(ctx.index(), Ordering::Relaxed);
+            });
+        });
+        crate::current_num_threads()
+    });
+    assert_eq!(sum.into_inner(), n * n * (n - 1) / 2);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn scope_spawn_broadcast_barrier() {
+    let barrier = Barrier::new(8);
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    pool.in_place_scope(|s| {
+        s.spawn_broadcast(|_, _| {
+            barrier.wait();
+        });
+        barrier.wait();
+    });
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn scope_spawn_broadcast_panic_one() {
+    let count = AtomicUsize::new(0);
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    let result = crate::unwind::halt_unwinding(|| {
+        pool.scope(|s| {
+            s.spawn_broadcast(|_, ctx| {
+                count.fetch_add(1, Ordering::Relaxed);
+                if ctx.index() == 3 {
+                    panic!("Hello, world!");
+                }
+            });
+        });
+    });
+    assert_eq!(count.into_inner(), 7);
+    assert!(result.is_err(), "broadcast panic should propagate!");
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn scope_spawn_broadcast_panic_many() {
+    let count = AtomicUsize::new(0);
+    let pool = ThreadPoolBuilder::new().num_threads(7).build().unwrap();
+    let result = crate::unwind::halt_unwinding(|| {
+        pool.scope(|s| {
+            s.spawn_broadcast(|_, ctx| {
+                count.fetch_add(1, Ordering::Relaxed);
+                if ctx.index() % 2 == 0 {
+                    panic!("Hello, world!");
+                }
+            });
+        });
+    });
+    assert_eq!(count.into_inner(), 7);
+    assert!(result.is_err(), "broadcast panic should propagate!");
+}
diff --git a/compiler/rustc_thread_pool/src/sleep/README.md b/compiler/rustc_thread_pool/src/sleep/README.md
new file mode 100644
index 00000000000..1e11da55f4a
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/sleep/README.md
@@ -0,0 +1,252 @@
+# Introduction: the sleep module
+
+The code in this module governs when worker threads should go to
+sleep. The system used in this code was introduced in [Rayon RFC #5].
+There is also a [video walkthrough] available. Both of those may be
+valuable resources to understanding the code, though naturally they
+will also grow stale over time. The comments in this file are
+extracted from the RFC and meant to be kept up to date.
+
+[Rayon RFC #5]: https://github.com/rayon-rs/rfcs/pull/5
+[video walkthrough]: https://youtu.be/HvmQsE5M4cY
+
+# The `Sleep` struct
+
+The `Sleep` struct is embedded into each registry. It performs several functions:
+
+* It tracks when workers are awake or asleep.
+* It decides how long a worker should look for work before it goes to sleep,
+  via a callback that is invoked periodically from the worker's search loop.
+* It is notified when latches are set, jobs are published, or other
+  events occur, and it will go and wake the appropriate threads if
+  they are sleeping.
+
+# Thread states
+
+There are three main thread states:
+
+* An **active** thread is one that is actively executing a job.
+* An **idle** thread is one that is searching for work to do. It will be
+  trying to steal work or pop work from the global injector queue.
+* A **sleeping** thread is one that is blocked on a condition variable,
+  waiting to be awoken.
+
+We sometimes refer to the final two states collectively as **inactive**.
+Threads begin as idle but transition to idle and finally sleeping when
+they're unable to find work to do.
+
+## Sleepy threads
+
+There is one other special state worth mentioning. During the idle state,
+threads can get **sleepy**. A sleepy thread is still idle, in that it is still
+searching for work, but it is *about* to go to sleep after it does one more
+search (or some other number, potentially). When a thread enters the sleepy
+state, it signals (via the **jobs event counter**, described below) that it is
+about to go to sleep. If new work is published, this will lead to the counter
+being adjusted. When the thread actually goes to sleep, it will (hopefully, but
+not guaranteed) see that the counter has changed and elect not to sleep, but
+instead to search again. See the section on the **jobs event counter** for more
+details.
+
+# The counters
+
+One of the key structs in the sleep module is `AtomicCounters`, found in
+`counters.rs`. It packs three counters into one atomically managed value:
+
+* Two **thread counters**, which track the number of threads in a particular state.
+* The **jobs event counter**, which is used to signal when new work is available.
+  It (sort of) tracks the number of jobs posted, but not quite, and it can rollover.
+
+## Thread counters
+
+There are two thread counters, one that tracks **inactive** threads and one that
+tracks **sleeping** threads. From this, one can deduce the number of threads
+that are idle by subtracting sleeping threads from inactive threads. We track
+the counters in this way because it permits simpler atomic operations. One can
+increment the number of sleeping threads (and thus decrease the number of idle
+threads) simply by doing one atomic increment, for example. Similarly, one can
+decrease the number of sleeping threads (and increase the number of idle
+threads) through one atomic decrement.
+
+These counters are adjusted as follows:
+
+* When a thread enters the idle state: increment the inactive thread counter.
+* When a thread enters the sleeping state: increment the sleeping thread counter.
+* When a thread awakens a sleeping thread: decrement the sleeping thread counter.
+  * Subtle point: the thread that *awakens* the sleeping thread decrements the
+    counter, not the thread that is *sleeping*. This is because there is a delay
+    between signaling a thread to wake and the thread actually waking:
+    decrementing the counter when awakening the thread means that other threads
+    that may be posting work will see the up-to-date value that much faster.
+* When a thread finds work, exiting the idle state: decrement the inactive
+  thread counter.
+
+## Jobs event counter
+
+The final counter is the **jobs event counter**. The role of this counter is to
+help sleepy threads detect when new work is posted in a lightweight fashion. In
+its simplest form, we would simply have a counter that gets incremented each
+time a new job is posted. This way, when a thread gets sleepy, it could read the
+counter, and then compare to see if the value has changed before it actually
+goes to sleep. But this [turns out to be too expensive] in practice, so we use a
+somewhat more complex scheme.
+
+[turns out to be too expensive]: https://github.com/rayon-rs/rayon/pull/746#issuecomment-624802747
+
+The idea is that the counter toggles between two states, depending on whether
+its value is even or odd (or, equivalently, on the value of its low bit):
+
+* Even -- If the low bit is zero, then it means that there has been no new work
+  since the last thread got sleepy.
+* Odd -- If the low bit is one, then it means that new work was posted since
+  the last thread got sleepy.
+
+### New work is posted
+
+When new work is posted, we check the value of the counter: if it is even,
+then we increment it by one, so that it becomes odd.
+
+### Worker thread gets sleepy
+
+When a worker thread gets sleepy, it will read the value of the counter. If the
+counter is odd, it will increment the counter so that it is even. Either way, it
+remembers the final value of the counter. The final value will be used later,
+when the thread is going to sleep. If at that time the counter has not changed,
+then we can assume no new jobs have been posted (though note the remote
+possibility of rollover, discussed in detail below).
+
+# Protocol for a worker thread to post work
+
+The full protocol for a thread to post work is as follows
+
+* If the work is posted into the injection queue, then execute a seq-cst fence (see below).
+* Load the counters, incrementing the JEC if it is even so that it is odd.
+* Check if there are idle threads available to handle this new job. If not,
+  and there are sleeping threads, then wake one or more threads.
+
+# Protocol for a worker thread to fall asleep
+
+The full protocol for a thread to fall asleep is as follows:
+
+* After completing all its jobs, the worker goes idle and begins to
+  search for work. As it searches, it counts "rounds". In each round,
+  it searches all other work threads' queues, plus the 'injector queue' for
+  work injected from the outside. If work is found in this search, the thread
+  becomes active again and hence restarts this protocol from the top.
+* After a certain number of rounds, the thread "gets sleepy" and executes `get_sleepy`
+  above, remembering the `final_value` of the JEC. It does one more search for work.
+* If no work is found, the thread atomically:
+  * Checks the JEC to see that it has not changed from `final_value`.
+    * If it has, then the thread goes back to searching for work. We reset to
+      just before we got sleepy, so that we will do one more search
+      before attempting to sleep again (rather than searching for many rounds).
+  * Increments the number of sleeping threads by 1.
+* The thread then executes a seq-cst fence operation (see below).
+* The thread then does one final check for injected jobs (see below). If any
+  are available, it returns to the 'pre-sleepy' state as if the JEC had changed.
+* The thread waits to be signaled. Once signaled, it returns to the idle state.
+
+# The jobs event counter and deadlock
+
+As described in the section on the JEC, the main concern around going to sleep
+is avoiding a race condition wherein:
+
+* Thread A looks for work, finds none.
+* Thread B posts work but sees no sleeping threads.
+* Thread A goes to sleep.
+
+The JEC protocol largely prevents this, but due to rollover, this prevention is
+not complete. It is possible -- if unlikely -- that enough activity occurs for
+Thread A to observe the same JEC value that it saw when getting sleepy. If the
+new work being published came from *inside* the thread-pool, then this race
+condition isn't too harmful. It means that we have fewer workers processing the
+work then we should, but we won't deadlock. This seems like an acceptable risk
+given that this is unlikely in practice.
+
+However, if the work was posted as an *external* job, that is a problem. In that
+case, it's possible that all of our workers could go to sleep, and the external
+job would never get processed. To prevent that, the sleeping protocol includes
+one final check to see if the injector queue is empty before fully falling
+asleep. Note that this final check occurs **after** the number of sleeping
+threads has been incremented. We are not concerned therefore with races against
+injections that occur after that increment, only before.
+
+Unfortunately, there is one rather subtle point concerning this final check:
+we wish to avoid the possibility that:
+
+* work is pushed into the injection queue by an outside thread X,
+* the sleepy thread S sees the JEC but it has rolled over and is equal
+* the sleepy thread S reads the injection queue but does not see the work posted by X.
+
+This is possible because the C++ memory model typically offers guarantees of the
+form "if you see the access A, then you must see those other accesses" -- but it
+doesn't guarantee that you will see the access A (i.e., if you think of
+processors with independent caches, you may be operating on very out of date
+cache state).
+
+## Using seq-cst fences to prevent deadlock
+
+To overcome this problem, we have inserted two sequentially consistent fence
+operations into the protocols above:
+
+* One fence occurs after work is posted into the injection queue, but before the
+  counters are read (including the number of sleeping threads).
+  * Note that no fence is needed for work posted to internal queues, since it is ok
+    to overlook work in that case.
+* One fence occurs after the number of sleeping threads is incremented, but
+  before the injection queue is read.
+
+### Proof sketch
+
+What follows is a "proof sketch" that the protocol is deadlock free. We model
+two relevant bits of memory, the job injector queue J and the atomic counters C.
+
+Consider the actions of the injecting thread:
+
+* PushJob: Job is injected, which can be modeled as an atomic write to J with release semantics.
+* PushFence: A sequentially consistent fence is executed.
+* ReadSleepers: The counters C are read (they may also be incremented, but we just consider the read that comes first).
+
+Meanwhile, the sleepy thread does the following:
+
+* IncSleepers: The number of sleeping threads is incremented, which is atomic exchange to C.
+* SleepFence: A sequentially consistent fence is executed.
+* ReadJob: We look to see if the queue is empty, which is a read of J with acquire semantics.
+
+Either PushFence or SleepFence must come first:
+
+* If PushFence comes first, then PushJob must be visible to ReadJob.
+* If SleepFence comes first, then IncSleepers is visible to ReadSleepers.
+
+# Deadlock detection
+
+This module tracks a number of variables in order to detect deadlocks due to user code blocking.
+These variables are stored in the `SleepData` struct which itself is kept behind a mutex.
+It contains the following fields:
+- `worker_count` - The number of threads in the thread pool.
+- `active_threads` - The number of threads in the thread pool which are running
+                     and aren't blocked in user code or sleeping.
+- `blocked_threads` - The number of threads which are blocked in user code.
+                      This doesn't include threads blocked by Rayon.
+
+User code can indicate blocking by calling `mark_blocked` before blocking and
+calling `mark_unblocked` before unblocking a thread.
+This will adjust `active_threads` and `blocked_threads` accordingly.
+
+When we tickle the thread pool in `Sleep::tickle_cold`, we set `active_threads` to
+`worker_count` - `blocked_threads` since we wake up all Rayon threads, but not thread blocked
+by user code.
+
+A deadlock is detected by checking if `active_threads` is 0 and `blocked_threads` is above 0.
+If we ignored `blocked_threads` we would have a deadlock
+immediately when creating the thread pool.
+We would also deadlock once the thread pool ran out of work.
+It is not possible for Rayon itself to deadlock.
+Deadlocks can only be caused by user code blocking, so this condition doesn't miss any deadlocks.
+
+We check for the deadlock condition when
+threads fall asleep in `mark_unblocked` and in `Sleep::sleep`.
+If there's a deadlock detected we call the user provided deadlock handler while we hold the
+lock to `SleepData`. This means the deadlock handler cannot call `mark_blocked` and
+`mark_unblocked`. The user is expected to handle the deadlock in some non-Rayon thread.
+Once the deadlock handler returns, the thread which called the deadlock handler will go to sleep.
diff --git a/compiler/rustc_thread_pool/src/sleep/counters.rs b/compiler/rustc_thread_pool/src/sleep/counters.rs
new file mode 100644
index 00000000000..f2682028b96
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/sleep/counters.rs
@@ -0,0 +1,273 @@
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+pub(super) struct AtomicCounters {
+    /// Packs together a number of counters. The counters are ordered as
+    /// follows, from least to most significant bits (here, we assuming
+    /// that [`THREADS_BITS`] is equal to 10):
+    ///
+    /// * Bits 0..10: Stores the number of **sleeping threads**
+    /// * Bits 10..20: Stores the number of **inactive threads**
+    /// * Bits 20..: Stores the **job event counter** (JEC)
+    ///
+    /// This uses 10 bits ([`THREADS_BITS`]) to encode the number of threads. Note
+    /// that the total number of bits (and hence the number of bits used for the
+    /// JEC) will depend on whether we are using a 32- or 64-bit architecture.
+    value: AtomicUsize,
+}
+
+#[derive(Copy, Clone)]
+pub(super) struct Counters {
+    word: usize,
+}
+
+/// A value read from the **Jobs Event Counter**.
+/// See the [`README.md`](README.md) for more
+/// coverage of how the jobs event counter works.
+#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
+pub(super) struct JobsEventCounter(usize);
+
+impl JobsEventCounter {
+    pub(super) const DUMMY: JobsEventCounter = JobsEventCounter(usize::MAX);
+
+    #[inline]
+    pub(super) fn as_usize(self) -> usize {
+        self.0
+    }
+
+    /// The JEC "is sleepy" if the last thread to increment it was in the
+    /// process of becoming sleepy. This is indicated by its value being *even*.
+    /// When new jobs are posted, they check if the JEC is sleepy, and if so
+    /// they incremented it.
+    #[inline]
+    pub(super) fn is_sleepy(self) -> bool {
+        (self.as_usize() & 1) == 0
+    }
+
+    /// The JEC "is active" if the last thread to increment it was posting new
+    /// work. This is indicated by its value being *odd*. When threads get
+    /// sleepy, they will check if the JEC is active, and increment it.
+    #[inline]
+    pub(super) fn is_active(self) -> bool {
+        !self.is_sleepy()
+    }
+}
+
+/// Number of bits used for the thread counters.
+#[cfg(target_pointer_width = "64")]
+const THREADS_BITS: usize = 16;
+
+#[cfg(target_pointer_width = "32")]
+const THREADS_BITS: usize = 8;
+
+/// Bits to shift to select the sleeping threads
+/// (used with `select_bits`).
+#[allow(clippy::erasing_op)]
+const SLEEPING_SHIFT: usize = 0 * THREADS_BITS;
+
+/// Bits to shift to select the inactive threads
+/// (used with `select_bits`).
+#[allow(clippy::identity_op)]
+const INACTIVE_SHIFT: usize = 1 * THREADS_BITS;
+
+/// Bits to shift to select the JEC
+/// (use JOBS_BITS).
+const JEC_SHIFT: usize = 2 * THREADS_BITS;
+
+/// Max value for the thread counters.
+pub(crate) const THREADS_MAX: usize = (1 << THREADS_BITS) - 1;
+
+/// Constant that can be added to add one sleeping thread.
+const ONE_SLEEPING: usize = 1;
+
+/// Constant that can be added to add one inactive thread.
+/// An inactive thread is either idle, sleepy, or sleeping.
+const ONE_INACTIVE: usize = 1 << INACTIVE_SHIFT;
+
+/// Constant that can be added to add one to the JEC.
+const ONE_JEC: usize = 1 << JEC_SHIFT;
+
+impl AtomicCounters {
+    #[inline]
+    pub(super) fn new() -> AtomicCounters {
+        AtomicCounters { value: AtomicUsize::new(0) }
+    }
+
+    /// Load and return the current value of the various counters.
+    /// This value can then be given to other method which will
+    /// attempt to update the counters via compare-and-swap.
+    #[inline]
+    pub(super) fn load(&self, ordering: Ordering) -> Counters {
+        Counters::new(self.value.load(ordering))
+    }
+
+    #[inline]
+    fn try_exchange(&self, old_value: Counters, new_value: Counters, ordering: Ordering) -> bool {
+        self.value
+            .compare_exchange(old_value.word, new_value.word, ordering, Ordering::Relaxed)
+            .is_ok()
+    }
+
+    /// Adds an inactive thread. This cannot fail.
+    ///
+    /// This should be invoked when a thread enters its idle loop looking
+    /// for work. It is decremented when work is found. Note that it is
+    /// not decremented if the thread transitions from idle to sleepy or sleeping;
+    /// so the number of inactive threads is always greater-than-or-equal
+    /// to the number of sleeping threads.
+    #[inline]
+    pub(super) fn add_inactive_thread(&self) {
+        self.value.fetch_add(ONE_INACTIVE, Ordering::SeqCst);
+    }
+
+    /// Increments the jobs event counter if `increment_when`, when applied to
+    /// the current value, is true. Used to toggle the JEC from even (sleepy) to
+    /// odd (active) or vice versa. Returns the final value of the counters, for
+    /// which `increment_when` is guaranteed to return false.
+    pub(super) fn increment_jobs_event_counter_if(
+        &self,
+        increment_when: impl Fn(JobsEventCounter) -> bool,
+    ) -> Counters {
+        loop {
+            let old_value = self.load(Ordering::SeqCst);
+            if increment_when(old_value.jobs_counter()) {
+                let new_value = old_value.increment_jobs_counter();
+                if self.try_exchange(old_value, new_value, Ordering::SeqCst) {
+                    return new_value;
+                }
+            } else {
+                return old_value;
+            }
+        }
+    }
+
+    /// Subtracts an inactive thread. This cannot fail. It is invoked
+    /// when a thread finds work and hence becomes active. It returns the
+    /// number of sleeping threads to wake up (if any).
+    ///
+    /// See `add_inactive_thread`.
+    #[inline]
+    pub(super) fn sub_inactive_thread(&self) -> usize {
+        let old_value = Counters::new(self.value.fetch_sub(ONE_INACTIVE, Ordering::SeqCst));
+        debug_assert!(
+            old_value.inactive_threads() > 0,
+            "sub_inactive_thread: old_value {:?} has no inactive threads",
+            old_value,
+        );
+        debug_assert!(
+            old_value.sleeping_threads() <= old_value.inactive_threads(),
+            "sub_inactive_thread: old_value {:?} had {} sleeping threads and {} inactive threads",
+            old_value,
+            old_value.sleeping_threads(),
+            old_value.inactive_threads(),
+        );
+
+        // Current heuristic: whenever an inactive thread goes away, if
+        // there are any sleeping threads, wake 'em up.
+        let sleeping_threads = old_value.sleeping_threads();
+        Ord::min(sleeping_threads, 2)
+    }
+
+    /// Subtracts a sleeping thread. This cannot fail, but it is only
+    /// safe to do if you you know the number of sleeping threads is
+    /// non-zero (i.e., because you have just awoken a sleeping
+    /// thread).
+    #[inline]
+    pub(super) fn sub_sleeping_thread(&self) {
+        let old_value = Counters::new(self.value.fetch_sub(ONE_SLEEPING, Ordering::SeqCst));
+        debug_assert!(
+            old_value.sleeping_threads() > 0,
+            "sub_sleeping_thread: old_value {:?} had no sleeping threads",
+            old_value,
+        );
+        debug_assert!(
+            old_value.sleeping_threads() <= old_value.inactive_threads(),
+            "sub_sleeping_thread: old_value {:?} had {} sleeping threads and {} inactive threads",
+            old_value,
+            old_value.sleeping_threads(),
+            old_value.inactive_threads(),
+        );
+    }
+
+    #[inline]
+    pub(super) fn try_add_sleeping_thread(&self, old_value: Counters) -> bool {
+        debug_assert!(
+            old_value.inactive_threads() > 0,
+            "try_add_sleeping_thread: old_value {:?} has no inactive threads",
+            old_value,
+        );
+        debug_assert!(
+            old_value.sleeping_threads() < THREADS_MAX,
+            "try_add_sleeping_thread: old_value {:?} has too many sleeping threads",
+            old_value,
+        );
+
+        let mut new_value = old_value;
+        new_value.word += ONE_SLEEPING;
+
+        self.try_exchange(old_value, new_value, Ordering::SeqCst)
+    }
+}
+
+#[inline]
+fn select_thread(word: usize, shift: usize) -> usize {
+    (word >> shift) & THREADS_MAX
+}
+
+#[inline]
+fn select_jec(word: usize) -> usize {
+    word >> JEC_SHIFT
+}
+
+impl Counters {
+    #[inline]
+    fn new(word: usize) -> Counters {
+        Counters { word }
+    }
+
+    #[inline]
+    fn increment_jobs_counter(self) -> Counters {
+        // We can freely add to JEC because it occupies the most significant bits.
+        // Thus it doesn't overflow into the other counters, just wraps itself.
+        Counters { word: self.word.wrapping_add(ONE_JEC) }
+    }
+
+    #[inline]
+    pub(super) fn jobs_counter(self) -> JobsEventCounter {
+        JobsEventCounter(select_jec(self.word))
+    }
+
+    /// The number of threads that are not actively
+    /// executing work. They may be idle, sleepy, or asleep.
+    #[inline]
+    pub(super) fn inactive_threads(self) -> usize {
+        select_thread(self.word, INACTIVE_SHIFT)
+    }
+
+    #[inline]
+    pub(super) fn awake_but_idle_threads(self) -> usize {
+        debug_assert!(
+            self.sleeping_threads() <= self.inactive_threads(),
+            "sleeping threads: {} > raw idle threads {}",
+            self.sleeping_threads(),
+            self.inactive_threads()
+        );
+        self.inactive_threads() - self.sleeping_threads()
+    }
+
+    #[inline]
+    pub(super) fn sleeping_threads(self) -> usize {
+        select_thread(self.word, SLEEPING_SHIFT)
+    }
+}
+
+impl std::fmt::Debug for Counters {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let word = format!("{:016x}", self.word);
+        fmt.debug_struct("Counters")
+            .field("word", &word)
+            .field("jobs", &self.jobs_counter().0)
+            .field("inactive", &self.inactive_threads())
+            .field("sleeping", &self.sleeping_threads())
+            .finish()
+    }
+}
diff --git a/compiler/rustc_thread_pool/src/sleep/mod.rs b/compiler/rustc_thread_pool/src/sleep/mod.rs
new file mode 100644
index 00000000000..a9cdf68cc7e
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/sleep/mod.rs
@@ -0,0 +1,386 @@
+//! Code that decides when workers should go to sleep. See README.md
+//! for an overview.
+
+use std::sync::atomic::Ordering;
+use std::sync::{Condvar, Mutex};
+use std::thread;
+
+use crossbeam_utils::CachePadded;
+
+use crate::DeadlockHandler;
+use crate::latch::CoreLatch;
+use crate::registry::WorkerThread;
+
+mod counters;
+pub(crate) use self::counters::THREADS_MAX;
+use self::counters::{AtomicCounters, JobsEventCounter};
+
+struct SleepData {
+    /// The number of threads in the thread pool.
+    worker_count: usize,
+
+    /// The number of threads in the thread pool which are running and
+    /// aren't blocked in user code or sleeping.
+    active_threads: usize,
+
+    /// The number of threads which are blocked in user code.
+    /// This doesn't include threads blocked by this module.
+    blocked_threads: usize,
+}
+
+impl SleepData {
+    /// Checks if the conditions for a deadlock holds and if so calls the deadlock handler
+    #[inline]
+    pub(super) fn deadlock_check(&self, deadlock_handler: &Option<Box<DeadlockHandler>>) {
+        if self.active_threads == 0 && self.blocked_threads > 0 {
+            (deadlock_handler.as_ref().unwrap())();
+        }
+    }
+}
+
+/// The `Sleep` struct is embedded into each registry. It governs the waking and sleeping
+/// of workers. It has callbacks that are invoked periodically at significant events,
+/// such as when workers are looping and looking for work, when latches are set, or when
+/// jobs are published, and it either blocks threads or wakes them in response to these
+/// events. See the [`README.md`] in this module for more details.
+///
+/// [`README.md`] README.md
+pub(super) struct Sleep {
+    /// One "sleep state" per worker. Used to track if a worker is sleeping and to have
+    /// them block.
+    worker_sleep_states: Vec<CachePadded<WorkerSleepState>>,
+
+    counters: AtomicCounters,
+
+    data: Mutex<SleepData>,
+}
+
+/// An instance of this struct is created when a thread becomes idle.
+/// It is consumed when the thread finds work, and passed by `&mut`
+/// reference for operations that preserve the idle state. (In other
+/// words, producing one of these structs is evidence the thread is
+/// idle.) It tracks state such as how long the thread has been idle.
+pub(super) struct IdleState {
+    /// What is worker index of the idle thread?
+    worker_index: usize,
+
+    /// How many rounds have we been circling without sleeping?
+    rounds: u32,
+
+    /// Once we become sleepy, what was the sleepy counter value?
+    /// Set to `INVALID_SLEEPY_COUNTER` otherwise.
+    jobs_counter: JobsEventCounter,
+}
+
+/// The "sleep state" for an individual worker.
+#[derive(Default)]
+struct WorkerSleepState {
+    /// Set to true when the worker goes to sleep; set to false when
+    /// the worker is notified or when it wakes.
+    is_blocked: Mutex<bool>,
+
+    condvar: Condvar,
+}
+
+const ROUNDS_UNTIL_SLEEPY: u32 = 32;
+const ROUNDS_UNTIL_SLEEPING: u32 = ROUNDS_UNTIL_SLEEPY + 1;
+
+impl Sleep {
+    pub(super) fn new(n_threads: usize) -> Sleep {
+        assert!(n_threads <= THREADS_MAX);
+        Sleep {
+            worker_sleep_states: (0..n_threads).map(|_| Default::default()).collect(),
+            counters: AtomicCounters::new(),
+            data: Mutex::new(SleepData {
+                worker_count: n_threads,
+                active_threads: n_threads,
+                blocked_threads: 0,
+            }),
+        }
+    }
+
+    /// Mark a Rayon worker thread as blocked. This triggers the deadlock handler
+    /// if no other worker thread is active
+    #[inline]
+    pub(super) fn mark_blocked(&self, deadlock_handler: &Option<Box<DeadlockHandler>>) {
+        let mut data = self.data.lock().unwrap();
+        debug_assert!(data.active_threads > 0);
+        debug_assert!(data.blocked_threads < data.worker_count);
+        debug_assert!(data.active_threads > 0);
+        data.active_threads -= 1;
+        data.blocked_threads += 1;
+
+        data.deadlock_check(deadlock_handler);
+    }
+
+    /// Mark a previously blocked Rayon worker thread as unblocked
+    #[inline]
+    pub(super) fn mark_unblocked(&self) {
+        let mut data = self.data.lock().unwrap();
+        debug_assert!(data.active_threads < data.worker_count);
+        debug_assert!(data.blocked_threads > 0);
+        data.active_threads += 1;
+        data.blocked_threads -= 1;
+    }
+
+    #[inline]
+    pub(super) fn start_looking(&self, worker_index: usize) -> IdleState {
+        self.counters.add_inactive_thread();
+
+        IdleState { worker_index, rounds: 0, jobs_counter: JobsEventCounter::DUMMY }
+    }
+
+    #[inline]
+    pub(super) fn work_found(&self) {
+        // If we were the last idle thread and other threads are still sleeping,
+        // then we should wake up another thread.
+        let threads_to_wake = self.counters.sub_inactive_thread();
+        self.wake_any_threads(threads_to_wake as u32);
+    }
+
+    #[inline]
+    pub(super) fn no_work_found(
+        &self,
+        idle_state: &mut IdleState,
+        latch: &CoreLatch,
+        thread: &WorkerThread,
+    ) {
+        if idle_state.rounds < ROUNDS_UNTIL_SLEEPY {
+            thread::yield_now();
+            idle_state.rounds += 1;
+        } else if idle_state.rounds == ROUNDS_UNTIL_SLEEPY {
+            idle_state.jobs_counter = self.announce_sleepy();
+            idle_state.rounds += 1;
+            thread::yield_now();
+        } else if idle_state.rounds < ROUNDS_UNTIL_SLEEPING {
+            idle_state.rounds += 1;
+            thread::yield_now();
+        } else {
+            debug_assert_eq!(idle_state.rounds, ROUNDS_UNTIL_SLEEPING);
+            self.sleep(idle_state, latch, thread);
+        }
+    }
+
+    #[cold]
+    fn announce_sleepy(&self) -> JobsEventCounter {
+        self.counters.increment_jobs_event_counter_if(JobsEventCounter::is_active).jobs_counter()
+    }
+
+    #[cold]
+    fn sleep(&self, idle_state: &mut IdleState, latch: &CoreLatch, thread: &WorkerThread) {
+        let worker_index = idle_state.worker_index;
+
+        if !latch.get_sleepy() {
+            return;
+        }
+
+        let sleep_state = &self.worker_sleep_states[worker_index];
+        let mut is_blocked = sleep_state.is_blocked.lock().unwrap();
+        debug_assert!(!*is_blocked);
+
+        // Our latch was signalled. We should wake back up fully as we
+        // will have some stuff to do.
+        if !latch.fall_asleep() {
+            idle_state.wake_fully();
+            return;
+        }
+
+        loop {
+            let counters = self.counters.load(Ordering::SeqCst);
+
+            // Check if the JEC has changed since we got sleepy.
+            debug_assert!(idle_state.jobs_counter.is_sleepy());
+            if counters.jobs_counter() != idle_state.jobs_counter {
+                // JEC has changed, so a new job was posted, but for some reason
+                // we didn't see it. We should return to just before the SLEEPY
+                // state so we can do another search and (if we fail to find
+                // work) go back to sleep.
+                idle_state.wake_partly();
+                latch.wake_up();
+                return;
+            }
+
+            // Otherwise, let's move from IDLE to SLEEPING.
+            if self.counters.try_add_sleeping_thread(counters) {
+                break;
+            }
+        }
+
+        // Successfully registered as asleep.
+
+        // We have one last check for injected jobs to do. This protects against
+        // deadlock in the very unlikely event that
+        //
+        // - an external job is being injected while we are sleepy
+        // - that job triggers the rollover over the JEC such that we don't see it
+        // - we are the last active worker thread
+        std::sync::atomic::fence(Ordering::SeqCst);
+        if thread.has_injected_job() {
+            // If we see an externally injected job, then we have to 'wake
+            // ourselves up'. (Ordinarily, `sub_sleeping_thread` is invoked by
+            // the one that wakes us.)
+            self.counters.sub_sleeping_thread();
+        } else {
+            {
+                // Decrement the number of active threads and check for a deadlock
+                let mut data = self.data.lock().unwrap();
+                data.active_threads -= 1;
+                data.deadlock_check(&thread.registry.deadlock_handler);
+            }
+
+            // If we don't see an injected job (the normal case), then flag
+            // ourselves as asleep and wait till we are notified.
+            //
+            // (Note that `is_blocked` is held under a mutex and the mutex was
+            // acquired *before* we incremented the "sleepy counter". This means
+            // that whomever is coming to wake us will have to wait until we
+            // release the mutex in the call to `wait`, so they will see this
+            // boolean as true.)
+            thread.registry.release_thread();
+            *is_blocked = true;
+            while *is_blocked {
+                is_blocked = sleep_state.condvar.wait(is_blocked).unwrap();
+            }
+
+            // Drop `is_blocked` now in case `acquire_thread` blocks
+            drop(is_blocked);
+
+            thread.registry.acquire_thread();
+        }
+
+        // Update other state:
+        idle_state.wake_fully();
+        latch.wake_up();
+    }
+
+    /// Notify the given thread that it should wake up (if it is
+    /// sleeping). When this method is invoked, we typically know the
+    /// thread is asleep, though in rare cases it could have been
+    /// awoken by (e.g.) new work having been posted.
+    pub(super) fn notify_worker_latch_is_set(&self, target_worker_index: usize) {
+        self.wake_specific_thread(target_worker_index);
+    }
+
+    /// Signals that `num_jobs` new jobs were injected into the thread
+    /// pool from outside. This function will ensure that there are
+    /// threads available to process them, waking threads from sleep
+    /// if necessary.
+    ///
+    /// # Parameters
+    ///
+    /// - `num_jobs` -- lower bound on number of jobs available for stealing.
+    ///   We'll try to get at least one thread per job.
+    #[inline]
+    pub(super) fn new_injected_jobs(&self, num_jobs: u32, queue_was_empty: bool) {
+        // This fence is needed to guarantee that threads
+        // as they are about to fall asleep, observe any
+        // new jobs that may have been injected.
+        std::sync::atomic::fence(Ordering::SeqCst);
+
+        self.new_jobs(num_jobs, queue_was_empty)
+    }
+
+    /// Signals that `num_jobs` new jobs were pushed onto a thread's
+    /// local deque. This function will try to ensure that there are
+    /// threads available to process them, waking threads from sleep
+    /// if necessary. However, this is not guaranteed: under certain
+    /// race conditions, the function may fail to wake any new
+    /// threads; in that case the existing thread should eventually
+    /// pop the job.
+    ///
+    /// # Parameters
+    ///
+    /// - `num_jobs` -- lower bound on number of jobs available for stealing.
+    ///   We'll try to get at least one thread per job.
+    #[inline]
+    pub(super) fn new_internal_jobs(&self, num_jobs: u32, queue_was_empty: bool) {
+        self.new_jobs(num_jobs, queue_was_empty)
+    }
+
+    /// Common helper for `new_injected_jobs` and `new_internal_jobs`.
+    #[inline]
+    fn new_jobs(&self, num_jobs: u32, queue_was_empty: bool) {
+        // Read the counters and -- if sleepy workers have announced themselves
+        // -- announce that there is now work available. The final value of `counters`
+        // with which we exit the loop thus corresponds to a state when
+        let counters = self.counters.increment_jobs_event_counter_if(JobsEventCounter::is_sleepy);
+        let num_awake_but_idle = counters.awake_but_idle_threads();
+        let num_sleepers = counters.sleeping_threads();
+
+        if num_sleepers == 0 {
+            // nobody to wake
+            return;
+        }
+
+        // Promote from u16 to u32 so we can interoperate with
+        // num_jobs more easily.
+        let num_awake_but_idle = num_awake_but_idle as u32;
+        let num_sleepers = num_sleepers as u32;
+
+        // If the queue is non-empty, then we always wake up a worker
+        // -- clearly the existing idle jobs aren't enough. Otherwise,
+        // check to see if we have enough idle workers.
+        if !queue_was_empty {
+            let num_to_wake = Ord::min(num_jobs, num_sleepers);
+            self.wake_any_threads(num_to_wake);
+        } else if num_awake_but_idle < num_jobs {
+            let num_to_wake = Ord::min(num_jobs - num_awake_but_idle, num_sleepers);
+            self.wake_any_threads(num_to_wake);
+        }
+    }
+
+    #[cold]
+    fn wake_any_threads(&self, mut num_to_wake: u32) {
+        if num_to_wake > 0 {
+            for i in 0..self.worker_sleep_states.len() {
+                if self.wake_specific_thread(i) {
+                    num_to_wake -= 1;
+                    if num_to_wake == 0 {
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    fn wake_specific_thread(&self, index: usize) -> bool {
+        let sleep_state = &self.worker_sleep_states[index];
+
+        let mut is_blocked = sleep_state.is_blocked.lock().unwrap();
+        if *is_blocked {
+            *is_blocked = false;
+
+            // Increment the number of active threads
+            self.data.lock().unwrap().active_threads += 1;
+
+            sleep_state.condvar.notify_one();
+
+            // When the thread went to sleep, it will have incremented
+            // this value. When we wake it, its our job to decrement
+            // it. We could have the thread do it, but that would
+            // introduce a delay between when the thread was
+            // *notified* and when this counter was decremented. That
+            // might mislead people with new work into thinking that
+            // there are sleeping threads that they should try to
+            // wake, when in fact there is nothing left for them to
+            // do.
+            self.counters.sub_sleeping_thread();
+
+            true
+        } else {
+            false
+        }
+    }
+}
+
+impl IdleState {
+    fn wake_fully(&mut self) {
+        self.rounds = 0;
+        self.jobs_counter = JobsEventCounter::DUMMY;
+    }
+
+    fn wake_partly(&mut self) {
+        self.rounds = ROUNDS_UNTIL_SLEEPY;
+        self.jobs_counter = JobsEventCounter::DUMMY;
+    }
+}
diff --git a/compiler/rustc_thread_pool/src/spawn/mod.rs b/compiler/rustc_thread_pool/src/spawn/mod.rs
new file mode 100644
index 00000000000..040a02bfa67
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/spawn/mod.rs
@@ -0,0 +1,165 @@
+use std::mem;
+use std::sync::Arc;
+
+use crate::job::*;
+use crate::registry::Registry;
+use crate::tlv::Tlv;
+use crate::unwind;
+
+/// Puts the task into the Rayon threadpool's job queue in the "static"
+/// or "global" scope. Just like a standard thread, this task is not
+/// tied to the current stack frame, and hence it cannot hold any
+/// references other than those with `'static` lifetime. If you want
+/// to spawn a task that references stack data, use [the `scope()`
+/// function][scope] to create a scope.
+///
+/// [scope]: fn.scope.html
+///
+/// Since tasks spawned with this function cannot hold references into
+/// the enclosing stack frame, you almost certainly want to use a
+/// `move` closure as their argument (otherwise, the closure will
+/// typically hold references to any variables from the enclosing
+/// function that you happen to use).
+///
+/// This API assumes that the closure is executed purely for its
+/// side-effects (i.e., it might send messages, modify data protected
+/// by a mutex, or some such thing).
+///
+/// There is no guaranteed order of execution for spawns, given that
+/// other threads may steal tasks at any time. However, they are
+/// generally prioritized in a LIFO order on the thread from which
+/// they were spawned. Other threads always steal from the other end of
+/// the deque, like FIFO order. The idea is that "recent" tasks are
+/// most likely to be fresh in the local CPU's cache, while other
+/// threads can steal older "stale" tasks. For an alternate approach,
+/// consider [`spawn_fifo()`] instead.
+///
+/// [`spawn_fifo()`]: fn.spawn_fifo.html
+///
+/// # Panic handling
+///
+/// If this closure should panic, the resulting panic will be
+/// propagated to the panic handler registered in the `ThreadPoolBuilder`,
+/// if any. See [`ThreadPoolBuilder::panic_handler()`][ph] for more
+/// details.
+///
+/// [ph]: struct.ThreadPoolBuilder.html#method.panic_handler
+///
+/// # Examples
+///
+/// This code creates a Rayon task that increments a global counter.
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
+///
+/// static GLOBAL_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
+///
+/// rayon::spawn(move || {
+///     GLOBAL_COUNTER.fetch_add(1, Ordering::SeqCst);
+/// });
+/// ```
+pub fn spawn<F>(func: F)
+where
+    F: FnOnce() + Send + 'static,
+{
+    // We assert that current registry has not terminated.
+    unsafe { spawn_in(func, &Registry::current()) }
+}
+
+/// Spawns an asynchronous job in `registry.`
+///
+/// Unsafe because `registry` must not yet have terminated.
+pub(super) unsafe fn spawn_in<F>(func: F, registry: &Arc<Registry>)
+where
+    F: FnOnce() + Send + 'static,
+{
+    // We assert that this does not hold any references (we know
+    // this because of the `'static` bound in the interface);
+    // moreover, we assert that the code below is not supposed to
+    // be able to panic, and hence the data won't leak but will be
+    // enqueued into some deque for later execution.
+    let abort_guard = unwind::AbortIfPanic; // just in case we are wrong, and code CAN panic
+    let job_ref = unsafe { spawn_job(func, registry) };
+    registry.inject_or_push(job_ref);
+    mem::forget(abort_guard);
+}
+
+unsafe fn spawn_job<F>(func: F, registry: &Arc<Registry>) -> JobRef
+where
+    F: FnOnce() + Send + 'static,
+{
+    // Ensure that registry cannot terminate until this job has
+    // executed. This ref is decremented at the (*) below.
+    registry.increment_terminate_count();
+
+    HeapJob::new(Tlv::null(), {
+        let registry = Arc::clone(registry);
+        move || {
+            registry.catch_unwind(func);
+            registry.terminate(); // (*) permit registry to terminate now
+        }
+    })
+    .into_static_job_ref()
+}
+
+/// Fires off a task into the Rayon threadpool in the "static" or
+/// "global" scope. Just like a standard thread, this task is not
+/// tied to the current stack frame, and hence it cannot hold any
+/// references other than those with `'static` lifetime. If you want
+/// to spawn a task that references stack data, use [the `scope_fifo()`
+/// function](fn.scope_fifo.html) to create a scope.
+///
+/// The behavior is essentially the same as [the `spawn`
+/// function](fn.spawn.html), except that calls from the same thread
+/// will be prioritized in FIFO order. This is similar to the now-
+/// deprecated [`breadth_first`] option, except the effect is isolated
+/// to relative `spawn_fifo` calls, not all threadpool tasks.
+///
+/// For more details on this design, see Rayon [RFC #1].
+///
+/// [`breadth_first`]: struct.ThreadPoolBuilder.html#method.breadth_first
+/// [RFC #1]: https://github.com/rayon-rs/rfcs/blob/master/accepted/rfc0001-scope-scheduling.md
+///
+/// # Panic handling
+///
+/// If this closure should panic, the resulting panic will be
+/// propagated to the panic handler registered in the `ThreadPoolBuilder`,
+/// if any. See [`ThreadPoolBuilder::panic_handler()`][ph] for more
+/// details.
+///
+/// [ph]: struct.ThreadPoolBuilder.html#method.panic_handler
+pub fn spawn_fifo<F>(func: F)
+where
+    F: FnOnce() + Send + 'static,
+{
+    // We assert that current registry has not terminated.
+    unsafe { spawn_fifo_in(func, &Registry::current()) }
+}
+
+/// Spawns an asynchronous FIFO job in `registry.`
+///
+/// Unsafe because `registry` must not yet have terminated.
+pub(super) unsafe fn spawn_fifo_in<F>(func: F, registry: &Arc<Registry>)
+where
+    F: FnOnce() + Send + 'static,
+{
+    // We assert that this does not hold any references (we know
+    // this because of the `'static` bound in the interface);
+    // moreover, we assert that the code below is not supposed to
+    // be able to panic, and hence the data won't leak but will be
+    // enqueued into some deque for later execution.
+    let abort_guard = unwind::AbortIfPanic; // just in case we are wrong, and code CAN panic
+    let job_ref = unsafe { spawn_job(func, registry) };
+
+    // If we're in the pool, use our thread's private fifo for this thread to execute
+    // in a locally-FIFO order. Otherwise, just use the pool's global injector.
+    match registry.current_thread() {
+        Some(worker) => unsafe { worker.push_fifo(job_ref) },
+        None => registry.inject(job_ref),
+    }
+    mem::forget(abort_guard);
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/compiler/rustc_thread_pool/src/spawn/tests.rs b/compiler/rustc_thread_pool/src/spawn/tests.rs
new file mode 100644
index 00000000000..8a70d2faf9c
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/spawn/tests.rs
@@ -0,0 +1,246 @@
+use std::any::Any;
+use std::sync::Mutex;
+use std::sync::mpsc::channel;
+
+use super::{spawn, spawn_fifo};
+use crate::{ThreadPoolBuilder, scope};
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_then_join_in_worker() {
+    let (tx, rx) = channel();
+    scope(move |_| {
+        spawn(move || tx.send(22).unwrap());
+    });
+    assert_eq!(22, rx.recv().unwrap());
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_then_join_outside_worker() {
+    let (tx, rx) = channel();
+    spawn(move || tx.send(22).unwrap());
+    assert_eq!(22, rx.recv().unwrap());
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn panic_fwd() {
+    let (tx, rx) = channel();
+
+    let tx = Mutex::new(tx);
+    let panic_handler = move |err: Box<dyn Any + Send>| {
+        let tx = tx.lock().unwrap();
+        if let Some(&msg) = err.downcast_ref::<&str>() {
+            if msg == "Hello, world!" {
+                tx.send(1).unwrap();
+            } else {
+                tx.send(2).unwrap();
+            }
+        } else {
+            tx.send(3).unwrap();
+        }
+    };
+
+    let builder = ThreadPoolBuilder::new().panic_handler(panic_handler);
+
+    builder.build().unwrap().spawn(move || panic!("Hello, world!"));
+
+    assert_eq!(1, rx.recv().unwrap());
+}
+
+/// Test what happens when the thread-pool is dropped but there are
+/// still active asynchronous tasks. We expect the thread-pool to stay
+/// alive and executing until those threads are complete.
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn termination_while_things_are_executing() {
+    let (tx0, rx0) = channel();
+    let (tx1, rx1) = channel();
+
+    // Create a thread-pool and spawn some code in it, but then drop
+    // our reference to it.
+    {
+        let thread_pool = ThreadPoolBuilder::new().build().unwrap();
+        thread_pool.spawn(move || {
+            let data = rx0.recv().unwrap();
+
+            // At this point, we know the "main" reference to the
+            // `ThreadPool` has been dropped, but there are still
+            // active threads. Launch one more.
+            spawn(move || {
+                tx1.send(data).unwrap();
+            });
+        });
+    }
+
+    tx0.send(22).unwrap();
+    let v = rx1.recv().unwrap();
+    assert_eq!(v, 22);
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn custom_panic_handler_and_spawn() {
+    let (tx, rx) = channel();
+
+    // Create a parallel closure that will send panics on the
+    // channel; since the closure is potentially executed in parallel
+    // with itself, we have to wrap `tx` in a mutex.
+    let tx = Mutex::new(tx);
+    let panic_handler = move |e: Box<dyn Any + Send>| {
+        tx.lock().unwrap().send(e).unwrap();
+    };
+
+    // Execute an async that will panic.
+    let builder = ThreadPoolBuilder::new().panic_handler(panic_handler);
+    builder.build().unwrap().spawn(move || {
+        panic!("Hello, world!");
+    });
+
+    // Check that we got back the panic we expected.
+    let error = rx.recv().unwrap();
+    if let Some(&msg) = error.downcast_ref::<&str>() {
+        assert_eq!(msg, "Hello, world!");
+    } else {
+        panic!("did not receive a string from panic handler");
+    }
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn custom_panic_handler_and_nested_spawn() {
+    let (tx, rx) = channel();
+
+    // Create a parallel closure that will send panics on the
+    // channel; since the closure is potentially executed in parallel
+    // with itself, we have to wrap `tx` in a mutex.
+    let tx = Mutex::new(tx);
+    let panic_handler = move |e| {
+        tx.lock().unwrap().send(e).unwrap();
+    };
+
+    // Execute an async that will (eventually) panic.
+    const PANICS: usize = 3;
+    let builder = ThreadPoolBuilder::new().panic_handler(panic_handler);
+    builder.build().unwrap().spawn(move || {
+        // launch 3 nested spawn-asyncs; these should be in the same
+        // thread-pool and hence inherit the same panic handler
+        for _ in 0..PANICS {
+            spawn(move || {
+                panic!("Hello, world!");
+            });
+        }
+    });
+
+    // Check that we get back the panics we expected.
+    for _ in 0..PANICS {
+        let error = rx.recv().unwrap();
+        if let Some(&msg) = error.downcast_ref::<&str>() {
+            assert_eq!(msg, "Hello, world!");
+        } else {
+            panic!("did not receive a string from panic handler");
+        }
+    }
+}
+
+macro_rules! test_order {
+    ($outer_spawn:ident, $inner_spawn:ident) => {{
+        let builder = ThreadPoolBuilder::new().num_threads(1);
+        let pool = builder.build().unwrap();
+        let (tx, rx) = channel();
+        pool.install(move || {
+            for i in 0..10 {
+                let tx = tx.clone();
+                $outer_spawn(move || {
+                    for j in 0..10 {
+                        let tx = tx.clone();
+                        $inner_spawn(move || {
+                            tx.send(i * 10 + j).unwrap();
+                        });
+                    }
+                });
+            }
+        });
+        rx.iter().collect::<Vec<i32>>()
+    }};
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn lifo_order() {
+    // In the absence of stealing, `spawn()` jobs on a thread will run in LIFO order.
+    let vec = test_order!(spawn, spawn);
+    let expected: Vec<i32> = (0..100).rev().collect(); // LIFO -> reversed
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn fifo_order() {
+    // In the absence of stealing, `spawn_fifo()` jobs on a thread will run in FIFO order.
+    let vec = test_order!(spawn_fifo, spawn_fifo);
+    let expected: Vec<i32> = (0..100).collect(); // FIFO -> natural order
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn lifo_fifo_order() {
+    // LIFO on the outside, FIFO on the inside
+    let vec = test_order!(spawn, spawn_fifo);
+    let expected: Vec<i32> = (0..10).rev().flat_map(|i| (0..10).map(move |j| i * 10 + j)).collect();
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn fifo_lifo_order() {
+    // FIFO on the outside, LIFO on the inside
+    let vec = test_order!(spawn_fifo, spawn);
+    let expected: Vec<i32> = (0..10).flat_map(|i| (0..10).rev().map(move |j| i * 10 + j)).collect();
+    assert_eq!(vec, expected);
+}
+
+macro_rules! spawn_send {
+    ($spawn:ident, $tx:ident, $i:expr) => {{
+        let tx = $tx.clone();
+        $spawn(move || tx.send($i).unwrap());
+    }};
+}
+
+/// Test mixed spawns pushing a series of numbers, interleaved such
+/// such that negative values are using the second kind of spawn.
+macro_rules! test_mixed_order {
+    ($pos_spawn:ident, $neg_spawn:ident) => {{
+        let builder = ThreadPoolBuilder::new().num_threads(1);
+        let pool = builder.build().unwrap();
+        let (tx, rx) = channel();
+        pool.install(move || {
+            spawn_send!($pos_spawn, tx, 0);
+            spawn_send!($neg_spawn, tx, -1);
+            spawn_send!($pos_spawn, tx, 1);
+            spawn_send!($neg_spawn, tx, -2);
+            spawn_send!($pos_spawn, tx, 2);
+            spawn_send!($neg_spawn, tx, -3);
+            spawn_send!($pos_spawn, tx, 3);
+        });
+        rx.iter().collect::<Vec<i32>>()
+    }};
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn mixed_lifo_fifo_order() {
+    let vec = test_mixed_order!(spawn, spawn_fifo);
+    let expected = vec![3, -1, 2, -2, 1, -3, 0];
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn mixed_fifo_lifo_order() {
+    let vec = test_mixed_order!(spawn_fifo, spawn);
+    let expected = vec![0, -3, 1, -2, 2, -1, 3];
+    assert_eq!(vec, expected);
+}
diff --git a/compiler/rustc_thread_pool/src/tests.rs b/compiler/rustc_thread_pool/src/tests.rs
new file mode 100644
index 00000000000..3082f11a167
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/tests.rs
@@ -0,0 +1,197 @@
+#![cfg(test)]
+
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::{Arc, Barrier};
+
+use crate::{ThreadPoolBuildError, ThreadPoolBuilder};
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn worker_thread_index() {
+    let pool = ThreadPoolBuilder::new().num_threads(22).build().unwrap();
+    assert_eq!(pool.current_num_threads(), 22);
+    assert_eq!(pool.current_thread_index(), None);
+    let index = pool.install(|| pool.current_thread_index().unwrap());
+    assert!(index < 22);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn start_callback_called() {
+    let n_threads = 16;
+    let n_called = Arc::new(AtomicUsize::new(0));
+    // Wait for all the threads in the pool plus the one running tests.
+    let barrier = Arc::new(Barrier::new(n_threads + 1));
+
+    let b = Arc::clone(&barrier);
+    let nc = Arc::clone(&n_called);
+    let start_handler = move |_| {
+        nc.fetch_add(1, Ordering::SeqCst);
+        b.wait();
+    };
+
+    let conf = ThreadPoolBuilder::new().num_threads(n_threads).start_handler(start_handler);
+    let _ = conf.build().unwrap();
+
+    // Wait for all the threads to have been scheduled to run.
+    barrier.wait();
+
+    // The handler must have been called on every started thread.
+    assert_eq!(n_called.load(Ordering::SeqCst), n_threads);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn exit_callback_called() {
+    let n_threads = 16;
+    let n_called = Arc::new(AtomicUsize::new(0));
+    // Wait for all the threads in the pool plus the one running tests.
+    let barrier = Arc::new(Barrier::new(n_threads + 1));
+
+    let b = Arc::clone(&barrier);
+    let nc = Arc::clone(&n_called);
+    let exit_handler = move |_| {
+        nc.fetch_add(1, Ordering::SeqCst);
+        b.wait();
+    };
+
+    let conf = ThreadPoolBuilder::new().num_threads(n_threads).exit_handler(exit_handler);
+    {
+        let _ = conf.build().unwrap();
+        // Drop the pool so it stops the running threads.
+    }
+
+    // Wait for all the threads to have been scheduled to run.
+    barrier.wait();
+
+    // The handler must have been called on every exiting thread.
+    assert_eq!(n_called.load(Ordering::SeqCst), n_threads);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn handler_panics_handled_correctly() {
+    let n_threads = 16;
+    let n_called = Arc::new(AtomicUsize::new(0));
+    // Wait for all the threads in the pool plus the one running tests.
+    let start_barrier = Arc::new(Barrier::new(n_threads + 1));
+    let exit_barrier = Arc::new(Barrier::new(n_threads + 1));
+
+    let start_handler = move |_| {
+        panic!("ensure panic handler is called when starting");
+    };
+    let exit_handler = move |_| {
+        panic!("ensure panic handler is called when exiting");
+    };
+
+    let sb = Arc::clone(&start_barrier);
+    let eb = Arc::clone(&exit_barrier);
+    let nc = Arc::clone(&n_called);
+    let panic_handler = move |_| {
+        let val = nc.fetch_add(1, Ordering::SeqCst);
+        if val < n_threads {
+            sb.wait();
+        } else {
+            eb.wait();
+        }
+    };
+
+    let conf = ThreadPoolBuilder::new()
+        .num_threads(n_threads)
+        .start_handler(start_handler)
+        .exit_handler(exit_handler)
+        .panic_handler(panic_handler);
+    {
+        let _ = conf.build().unwrap();
+
+        // Wait for all the threads to start, panic in the start handler,
+        // and been taken care of by the panic handler.
+        start_barrier.wait();
+
+        // Drop the pool so it stops the running threads.
+    }
+
+    // Wait for all the threads to exit, panic in the exit handler,
+    // and been taken care of by the panic handler.
+    exit_barrier.wait();
+
+    // The panic handler must have been called twice on every thread.
+    assert_eq!(n_called.load(Ordering::SeqCst), 2 * n_threads);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn check_config_build() {
+    let pool = ThreadPoolBuilder::new().num_threads(22).build().unwrap();
+    assert_eq!(pool.current_num_threads(), 22);
+}
+
+/// Helper used by check_error_send_sync to ensure ThreadPoolBuildError is Send + Sync
+fn _send_sync<T: Send + Sync>() {}
+
+#[test]
+fn check_error_send_sync() {
+    _send_sync::<ThreadPoolBuildError>();
+}
+
+#[allow(deprecated)]
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn configuration() {
+    let start_handler = move |_| {};
+    let exit_handler = move |_| {};
+    let panic_handler = move |_| {};
+    let thread_name = move |i| format!("thread_name_{}", i);
+
+    // Ensure we can call all public methods on Configuration
+    crate::Configuration::new()
+        .thread_name(thread_name)
+        .num_threads(5)
+        .panic_handler(panic_handler)
+        .stack_size(4e6 as usize)
+        .breadth_first()
+        .start_handler(start_handler)
+        .exit_handler(exit_handler)
+        .build()
+        .unwrap();
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn default_pool() {
+    ThreadPoolBuilder::default().build().unwrap();
+}
+
+/// Test that custom spawned threads get their `WorkerThread` cleared once
+/// the pool is done with them, allowing them to be used with rayon again
+/// later. e.g. WebAssembly want to have their own pool of available threads.
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn cleared_current_thread() -> Result<(), ThreadPoolBuildError> {
+    let n_threads = 5;
+    let mut handles = vec![];
+    let pool = ThreadPoolBuilder::new()
+        .num_threads(n_threads)
+        .spawn_handler(|thread| {
+            let handle = std::thread::spawn(move || {
+                thread.run();
+
+                // Afterward, the current thread shouldn't be set anymore.
+                assert_eq!(crate::current_thread_index(), None);
+            });
+            handles.push(handle);
+            Ok(())
+        })
+        .build()?;
+    assert_eq!(handles.len(), n_threads);
+
+    pool.install(|| assert!(crate::current_thread_index().is_some()));
+    drop(pool);
+
+    // Wait for all threads to make their assertions and exit
+    for handle in handles {
+        handle.join().unwrap();
+    }
+
+    Ok(())
+}
diff --git a/compiler/rustc_thread_pool/src/thread_pool/mod.rs b/compiler/rustc_thread_pool/src/thread_pool/mod.rs
new file mode 100644
index 00000000000..3294e2a77cb
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/thread_pool/mod.rs
@@ -0,0 +1,513 @@
+//! Contains support for user-managed thread pools, represented by the
+//! the [`ThreadPool`] type (see that struct for details).
+//!
+//! [`ThreadPool`]: struct.ThreadPool.html
+
+use std::error::Error;
+use std::fmt;
+use std::sync::Arc;
+
+use crate::broadcast::{self, BroadcastContext};
+use crate::registry::{Registry, ThreadSpawn, WorkerThread};
+use crate::scope::{do_in_place_scope, do_in_place_scope_fifo};
+use crate::{
+    Scope, ScopeFifo, ThreadPoolBuildError, ThreadPoolBuilder, join, scope, scope_fifo, spawn,
+};
+
+mod tests;
+
+/// Represents a user created [thread-pool].
+///
+/// Use a [`ThreadPoolBuilder`] to specify the number and/or names of threads
+/// in the pool. After calling [`ThreadPoolBuilder::build()`], you can then
+/// execute functions explicitly within this [`ThreadPool`] using
+/// [`ThreadPool::install()`]. By contrast, top level rayon functions
+/// (like `join()`) will execute implicitly within the current thread-pool.
+///
+///
+/// ## Creating a ThreadPool
+///
+/// ```rust
+/// # use rustc_thread_pool as rayon;
+/// let pool = rayon::ThreadPoolBuilder::new().num_threads(8).build().unwrap();
+/// ```
+///
+/// [`install()`][`ThreadPool::install()`] executes a closure in one of the `ThreadPool`'s
+/// threads. In addition, any other rayon operations called inside of `install()` will also
+/// execute in the context of the `ThreadPool`.
+///
+/// When the `ThreadPool` is dropped, that's a signal for the threads it manages to terminate,
+/// they will complete executing any remaining work that you have spawned, and automatically
+/// terminate.
+///
+///
+/// [thread-pool]: https://en.wikipedia.org/wiki/Thread_pool
+/// [`ThreadPool`]: struct.ThreadPool.html
+/// [`ThreadPool::new()`]: struct.ThreadPool.html#method.new
+/// [`ThreadPoolBuilder`]: struct.ThreadPoolBuilder.html
+/// [`ThreadPoolBuilder::build()`]: struct.ThreadPoolBuilder.html#method.build
+/// [`ThreadPool::install()`]: struct.ThreadPool.html#method.install
+pub struct ThreadPool {
+    registry: Arc<Registry>,
+}
+
+impl ThreadPool {
+    #[deprecated(note = "Use `ThreadPoolBuilder::build`")]
+    #[allow(deprecated)]
+    /// Deprecated in favor of `ThreadPoolBuilder::build`.
+    pub fn new(configuration: crate::Configuration) -> Result<ThreadPool, Box<dyn Error>> {
+        Self::build(configuration.into_builder()).map_err(Box::from)
+    }
+
+    pub(super) fn build<S>(
+        builder: ThreadPoolBuilder<S>,
+    ) -> Result<ThreadPool, ThreadPoolBuildError>
+    where
+        S: ThreadSpawn,
+    {
+        let registry = Registry::new(builder)?;
+        Ok(ThreadPool { registry })
+    }
+
+    /// Executes `op` within the threadpool. Any attempts to use
+    /// `join`, `scope`, or parallel iterators will then operate
+    /// within that threadpool.
+    ///
+    /// # Warning: thread-local data
+    ///
+    /// Because `op` is executing within the Rayon thread-pool,
+    /// thread-local data from the current thread will not be
+    /// accessible.
+    ///
+    /// # Warning: execution order
+    ///
+    /// If the current thread is part of a different thread pool, it will try to
+    /// keep busy while the `op` completes in its target pool, similar to
+    /// calling [`ThreadPool::yield_now()`] in a loop. Therefore, it may
+    /// potentially schedule other tasks to run on the current thread in the
+    /// meantime. For example
+    ///
+    /// ```rust
+    /// # use rustc_thread_pool as rayon;
+    /// fn main() {
+    ///     rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap();
+    ///     let pool = rustc_thread_pool::ThreadPoolBuilder::default().build().unwrap();
+    ///     let do_it = || {
+    ///         print!("one ");
+    ///         pool.install(||{});
+    ///         print!("two ");
+    ///     };
+    ///     rayon::join(|| do_it(), || do_it());
+    /// }
+    /// ```
+    ///
+    /// Since we configured just one thread in the global pool, one might
+    /// expect `do_it()` to run sequentially, producing:
+    ///
+    /// ```ascii
+    /// one two one two
+    /// ```
+    ///
+    /// However each call to `install()` yields implicitly, allowing rayon to
+    /// run multiple instances of `do_it()` concurrently on the single, global
+    /// thread. The following output would be equally valid:
+    ///
+    /// ```ascii
+    /// one one two two
+    /// ```
+    ///
+    /// # Panics
+    ///
+    /// If `op` should panic, that panic will be propagated.
+    ///
+    /// ## Using `install()`
+    ///
+    /// ```rust
+    ///    # use rustc_thread_pool as rayon;
+    ///    fn main() {
+    ///         let pool = rayon::ThreadPoolBuilder::new().num_threads(8).build().unwrap();
+    ///         let n = pool.install(|| fib(20));
+    ///         println!("{}", n);
+    ///    }
+    ///
+    ///    fn fib(n: usize) -> usize {
+    ///         if n == 0 || n == 1 {
+    ///             return n;
+    ///         }
+    ///         let (a, b) = rayon::join(|| fib(n - 1), || fib(n - 2)); // runs inside of `pool`
+    ///         return a + b;
+    ///     }
+    /// ```
+    pub fn install<OP, R>(&self, op: OP) -> R
+    where
+        OP: FnOnce() -> R + Send,
+        R: Send,
+    {
+        self.registry.in_worker(|_, _| op())
+    }
+
+    /// Executes `op` within every thread in the threadpool. Any attempts to use
+    /// `join`, `scope`, or parallel iterators will then operate within that
+    /// threadpool.
+    ///
+    /// Broadcasts are executed on each thread after they have exhausted their
+    /// local work queue, before they attempt work-stealing from other threads.
+    /// The goal of that strategy is to run everywhere in a timely manner
+    /// *without* being too disruptive to current work. There may be alternative
+    /// broadcast styles added in the future for more or less aggressive
+    /// injection, if the need arises.
+    ///
+    /// # Warning: thread-local data
+    ///
+    /// Because `op` is executing within the Rayon thread-pool,
+    /// thread-local data from the current thread will not be
+    /// accessible.
+    ///
+    /// # Panics
+    ///
+    /// If `op` should panic on one or more threads, exactly one panic
+    /// will be propagated, only after all threads have completed
+    /// (or panicked) their own `op`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    ///    # use rustc_thread_pool as rayon;
+    ///    use std::sync::atomic::{AtomicUsize, Ordering};
+    ///
+    ///    fn main() {
+    ///         let pool = rayon::ThreadPoolBuilder::new().num_threads(5).build().unwrap();
+    ///
+    ///         // The argument gives context, including the index of each thread.
+    ///         let v: Vec<usize> = pool.broadcast(|ctx| ctx.index() * ctx.index());
+    ///         assert_eq!(v, &[0, 1, 4, 9, 16]);
+    ///
+    ///         // The closure can reference the local stack
+    ///         let count = AtomicUsize::new(0);
+    ///         pool.broadcast(|_| count.fetch_add(1, Ordering::Relaxed));
+    ///         assert_eq!(count.into_inner(), 5);
+    ///    }
+    /// ```
+    pub fn broadcast<OP, R>(&self, op: OP) -> Vec<R>
+    where
+        OP: Fn(BroadcastContext<'_>) -> R + Sync,
+        R: Send,
+    {
+        // We assert that `self.registry` has not terminated.
+        unsafe { broadcast::broadcast_in(op, &self.registry) }
+    }
+
+    /// Returns the (current) number of threads in the thread pool.
+    ///
+    /// # Future compatibility note
+    ///
+    /// Note that unless this thread-pool was created with a
+    /// [`ThreadPoolBuilder`] that specifies the number of threads,
+    /// then this number may vary over time in future versions (see [the
+    /// `num_threads()` method for details][snt]).
+    ///
+    /// [snt]: struct.ThreadPoolBuilder.html#method.num_threads
+    /// [`ThreadPoolBuilder`]: struct.ThreadPoolBuilder.html
+    #[inline]
+    pub fn current_num_threads(&self) -> usize {
+        self.registry.num_threads()
+    }
+
+    /// If called from a Rayon worker thread in this thread-pool,
+    /// returns the index of that thread; if not called from a Rayon
+    /// thread, or called from a Rayon thread that belongs to a
+    /// different thread-pool, returns `None`.
+    ///
+    /// The index for a given thread will not change over the thread's
+    /// lifetime. However, multiple threads may share the same index if
+    /// they are in distinct thread-pools.
+    ///
+    /// # Future compatibility note
+    ///
+    /// Currently, every thread-pool (including the global
+    /// thread-pool) has a fixed number of threads, but this may
+    /// change in future Rayon versions (see [the `num_threads()` method
+    /// for details][snt]). In that case, the index for a
+    /// thread would not change during its lifetime, but thread
+    /// indices may wind up being reused if threads are terminated and
+    /// restarted.
+    ///
+    /// [snt]: struct.ThreadPoolBuilder.html#method.num_threads
+    #[inline]
+    pub fn current_thread_index(&self) -> Option<usize> {
+        let curr = self.registry.current_thread()?;
+        Some(curr.index())
+    }
+
+    /// Returns true if the current worker thread currently has "local
+    /// tasks" pending. This can be useful as part of a heuristic for
+    /// deciding whether to spawn a new task or execute code on the
+    /// current thread, particularly in breadth-first
+    /// schedulers. However, keep in mind that this is an inherently
+    /// racy check, as other worker threads may be actively "stealing"
+    /// tasks from our local deque.
+    ///
+    /// **Background:** Rayon's uses a [work-stealing] scheduler. The
+    /// key idea is that each thread has its own [deque] of
+    /// tasks. Whenever a new task is spawned -- whether through
+    /// `join()`, `Scope::spawn()`, or some other means -- that new
+    /// task is pushed onto the thread's *local* deque. Worker threads
+    /// have a preference for executing their own tasks; if however
+    /// they run out of tasks, they will go try to "steal" tasks from
+    /// other threads. This function therefore has an inherent race
+    /// with other active worker threads, which may be removing items
+    /// from the local deque.
+    ///
+    /// [work-stealing]: https://en.wikipedia.org/wiki/Work_stealing
+    /// [deque]: https://en.wikipedia.org/wiki/Double-ended_queue
+    #[inline]
+    pub fn current_thread_has_pending_tasks(&self) -> Option<bool> {
+        let curr = self.registry.current_thread()?;
+        Some(!curr.local_deque_is_empty())
+    }
+
+    /// Execute `oper_a` and `oper_b` in the thread-pool and return
+    /// the results. Equivalent to `self.install(|| join(oper_a,
+    /// oper_b))`.
+    pub fn join<A, B, RA, RB>(&self, oper_a: A, oper_b: B) -> (RA, RB)
+    where
+        A: FnOnce() -> RA + Send,
+        B: FnOnce() -> RB + Send,
+        RA: Send,
+        RB: Send,
+    {
+        self.install(|| join(oper_a, oper_b))
+    }
+
+    /// Creates a scope that executes within this thread-pool.
+    /// Equivalent to `self.install(|| scope(...))`.
+    ///
+    /// See also: [the `scope()` function][scope].
+    ///
+    /// [scope]: fn.scope.html
+    pub fn scope<'scope, OP, R>(&self, op: OP) -> R
+    where
+        OP: FnOnce(&Scope<'scope>) -> R + Send,
+        R: Send,
+    {
+        self.install(|| scope(op))
+    }
+
+    /// Creates a scope that executes within this thread-pool.
+    /// Spawns from the same thread are prioritized in relative FIFO order.
+    /// Equivalent to `self.install(|| scope_fifo(...))`.
+    ///
+    /// See also: [the `scope_fifo()` function][scope_fifo].
+    ///
+    /// [scope_fifo]: fn.scope_fifo.html
+    pub fn scope_fifo<'scope, OP, R>(&self, op: OP) -> R
+    where
+        OP: FnOnce(&ScopeFifo<'scope>) -> R + Send,
+        R: Send,
+    {
+        self.install(|| scope_fifo(op))
+    }
+
+    /// Creates a scope that spawns work into this thread-pool.
+    ///
+    /// See also: [the `in_place_scope()` function][in_place_scope].
+    ///
+    /// [in_place_scope]: fn.in_place_scope.html
+    pub fn in_place_scope<'scope, OP, R>(&self, op: OP) -> R
+    where
+        OP: FnOnce(&Scope<'scope>) -> R,
+    {
+        do_in_place_scope(Some(&self.registry), op)
+    }
+
+    /// Creates a scope that spawns work into this thread-pool in FIFO order.
+    ///
+    /// See also: [the `in_place_scope_fifo()` function][in_place_scope_fifo].
+    ///
+    /// [in_place_scope_fifo]: fn.in_place_scope_fifo.html
+    pub fn in_place_scope_fifo<'scope, OP, R>(&self, op: OP) -> R
+    where
+        OP: FnOnce(&ScopeFifo<'scope>) -> R,
+    {
+        do_in_place_scope_fifo(Some(&self.registry), op)
+    }
+
+    /// Spawns an asynchronous task in this thread-pool. This task will
+    /// run in the implicit, global scope, which means that it may outlast
+    /// the current stack frame -- therefore, it cannot capture any references
+    /// onto the stack (you will likely need a `move` closure).
+    ///
+    /// See also: [the `spawn()` function defined on scopes][spawn].
+    ///
+    /// [spawn]: struct.Scope.html#method.spawn
+    pub fn spawn<OP>(&self, op: OP)
+    where
+        OP: FnOnce() + Send + 'static,
+    {
+        // We assert that `self.registry` has not terminated.
+        unsafe { spawn::spawn_in(op, &self.registry) }
+    }
+
+    /// Spawns an asynchronous task in this thread-pool. This task will
+    /// run in the implicit, global scope, which means that it may outlast
+    /// the current stack frame -- therefore, it cannot capture any references
+    /// onto the stack (you will likely need a `move` closure).
+    ///
+    /// See also: [the `spawn_fifo()` function defined on scopes][spawn_fifo].
+    ///
+    /// [spawn_fifo]: struct.ScopeFifo.html#method.spawn_fifo
+    pub fn spawn_fifo<OP>(&self, op: OP)
+    where
+        OP: FnOnce() + Send + 'static,
+    {
+        // We assert that `self.registry` has not terminated.
+        unsafe { spawn::spawn_fifo_in(op, &self.registry) }
+    }
+
+    /// Spawns an asynchronous task on every thread in this thread-pool. This task
+    /// will run in the implicit, global scope, which means that it may outlast the
+    /// current stack frame -- therefore, it cannot capture any references onto the
+    /// stack (you will likely need a `move` closure).
+    pub fn spawn_broadcast<OP>(&self, op: OP)
+    where
+        OP: Fn(BroadcastContext<'_>) + Send + Sync + 'static,
+    {
+        // We assert that `self.registry` has not terminated.
+        unsafe { broadcast::spawn_broadcast_in(op, &self.registry) }
+    }
+
+    /// Cooperatively yields execution to Rayon.
+    ///
+    /// This is similar to the general [`yield_now()`], but only if the current
+    /// thread is part of *this* thread pool.
+    ///
+    /// Returns `Some(Yield::Executed)` if anything was executed, `Some(Yield::Idle)` if
+    /// nothing was available, or `None` if the current thread is not part this pool.
+    pub fn yield_now(&self) -> Option<Yield> {
+        let curr = self.registry.current_thread()?;
+        Some(curr.yield_now())
+    }
+
+    /// Cooperatively yields execution to local Rayon work.
+    ///
+    /// This is similar to the general [`yield_local()`], but only if the current
+    /// thread is part of *this* thread pool.
+    ///
+    /// Returns `Some(Yield::Executed)` if anything was executed, `Some(Yield::Idle)` if
+    /// nothing was available, or `None` if the current thread is not part this pool.
+    pub fn yield_local(&self) -> Option<Yield> {
+        let curr = self.registry.current_thread()?;
+        Some(curr.yield_local())
+    }
+
+    pub(crate) fn wait_until_stopped(self) {
+        let registry = Arc::clone(&self.registry);
+        drop(self);
+        registry.wait_until_stopped();
+    }
+}
+
+impl Drop for ThreadPool {
+    fn drop(&mut self) {
+        self.registry.terminate();
+    }
+}
+
+impl fmt::Debug for ThreadPool {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt.debug_struct("ThreadPool")
+            .field("num_threads", &self.current_num_threads())
+            .field("id", &self.registry.id())
+            .finish()
+    }
+}
+
+/// If called from a Rayon worker thread, returns the index of that
+/// thread within its current pool; if not called from a Rayon thread,
+/// returns `None`.
+///
+/// The index for a given thread will not change over the thread's
+/// lifetime. However, multiple threads may share the same index if
+/// they are in distinct thread-pools.
+///
+/// See also: [the `ThreadPool::current_thread_index()` method].
+///
+/// [m]: struct.ThreadPool.html#method.current_thread_index
+///
+/// # Future compatibility note
+///
+/// Currently, every thread-pool (including the global
+/// thread-pool) has a fixed number of threads, but this may
+/// change in future Rayon versions (see [the `num_threads()` method
+/// for details][snt]). In that case, the index for a
+/// thread would not change during its lifetime, but thread
+/// indices may wind up being reused if threads are terminated and
+/// restarted.
+///
+/// [snt]: struct.ThreadPoolBuilder.html#method.num_threads
+#[inline]
+pub fn current_thread_index() -> Option<usize> {
+    unsafe {
+        let curr = WorkerThread::current().as_ref()?;
+        Some(curr.index())
+    }
+}
+
+/// If called from a Rayon worker thread, indicates whether that
+/// thread's local deque still has pending tasks. Otherwise, returns
+/// `None`. For more information, see [the
+/// `ThreadPool::current_thread_has_pending_tasks()` method][m].
+///
+/// [m]: struct.ThreadPool.html#method.current_thread_has_pending_tasks
+#[inline]
+pub fn current_thread_has_pending_tasks() -> Option<bool> {
+    unsafe {
+        let curr = WorkerThread::current().as_ref()?;
+        Some(!curr.local_deque_is_empty())
+    }
+}
+
+/// Cooperatively yields execution to Rayon.
+///
+/// If the current thread is part of a rayon thread pool, this looks for a
+/// single unit of pending work in the pool, then executes it. Completion of
+/// that work might include nested work or further work stealing.
+///
+/// This is similar to [`std::thread::yield_now()`], but does not literally make
+/// that call. If you are implementing a polling loop, you may want to also
+/// yield to the OS scheduler yourself if no Rayon work was found.
+///
+/// Returns `Some(Yield::Executed)` if anything was executed, `Some(Yield::Idle)` if
+/// nothing was available, or `None` if this thread is not part of any pool at all.
+pub fn yield_now() -> Option<Yield> {
+    unsafe {
+        let thread = WorkerThread::current().as_ref()?;
+        Some(thread.yield_now())
+    }
+}
+
+/// Cooperatively yields execution to local Rayon work.
+///
+/// If the current thread is part of a rayon thread pool, this looks for a
+/// single unit of pending work in this thread's queue, then executes it.
+/// Completion of that work might include nested work or further work stealing.
+///
+/// This is similar to [`yield_now()`], but does not steal from other threads.
+///
+/// Returns `Some(Yield::Executed)` if anything was executed, `Some(Yield::Idle)` if
+/// nothing was available, or `None` if this thread is not part of any pool at all.
+pub fn yield_local() -> Option<Yield> {
+    unsafe {
+        let thread = WorkerThread::current().as_ref()?;
+        Some(thread.yield_local())
+    }
+}
+
+/// Result of [`yield_now()`] or [`yield_local()`].
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum Yield {
+    /// Work was found and executed.
+    Executed,
+    /// No available work was found.
+    Idle,
+}
diff --git a/compiler/rustc_thread_pool/src/thread_pool/tests.rs b/compiler/rustc_thread_pool/src/thread_pool/tests.rs
new file mode 100644
index 00000000000..42c99565088
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/thread_pool/tests.rs
@@ -0,0 +1,416 @@
+#![cfg(test)]
+
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::mpsc::channel;
+use std::sync::{Arc, Mutex};
+
+use crate::{Scope, ScopeFifo, ThreadPool, ThreadPoolBuilder, join};
+
+#[test]
+#[should_panic(expected = "Hello, world!")]
+fn panic_propagate() {
+    let thread_pool = ThreadPoolBuilder::new().build().unwrap();
+    thread_pool.install(|| {
+        panic!("Hello, world!");
+    });
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn workers_stop() {
+    let registry;
+
+    {
+        // once we exit this block, thread-pool will be dropped
+        let thread_pool = ThreadPoolBuilder::new().num_threads(22).build().unwrap();
+        registry = thread_pool.install(|| {
+            // do some work on these threads
+            join_a_lot(22);
+
+            Arc::clone(&thread_pool.registry)
+        });
+        assert_eq!(registry.num_threads(), 22);
+    }
+
+    // once thread-pool is dropped, registry should terminate, which
+    // should lead to worker threads stopping
+    registry.wait_until_stopped();
+}
+
+fn join_a_lot(n: usize) {
+    if n > 0 {
+        join(|| join_a_lot(n - 1), || join_a_lot(n - 1));
+    }
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn sleeper_stop() {
+    use std::{thread, time};
+
+    let registry;
+
+    {
+        // once we exit this block, thread-pool will be dropped
+        let thread_pool = ThreadPoolBuilder::new().num_threads(22).build().unwrap();
+        registry = Arc::clone(&thread_pool.registry);
+
+        // Give time for at least some of the thread pool to fall asleep.
+        thread::sleep(time::Duration::from_secs(1));
+    }
+
+    // once thread-pool is dropped, registry should terminate, which
+    // should lead to worker threads stopping
+    registry.wait_until_stopped();
+}
+
+/// Creates a start/exit handler that increments an atomic counter.
+fn count_handler() -> (Arc<AtomicUsize>, impl Fn(usize)) {
+    let count = Arc::new(AtomicUsize::new(0));
+    (Arc::clone(&count), move |_| {
+        count.fetch_add(1, Ordering::SeqCst);
+    })
+}
+
+/// Wait until a counter is no longer shared, then return its value.
+fn wait_for_counter(mut counter: Arc<AtomicUsize>) -> usize {
+    use std::{thread, time};
+
+    for _ in 0..60 {
+        counter = match Arc::try_unwrap(counter) {
+            Ok(counter) => return counter.into_inner(),
+            Err(counter) => {
+                thread::sleep(time::Duration::from_secs(1));
+                counter
+            }
+        };
+    }
+
+    // That's too long!
+    panic!("Counter is still shared!");
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn failed_thread_stack() {
+    // Note: we first tried to force failure with a `usize::MAX` stack, but
+    // macOS and Windows weren't fazed, or at least didn't fail the way we want.
+    // They work with `isize::MAX`, but 32-bit platforms may feasibly allocate a
+    // 2GB stack, so it might not fail until the second thread.
+    let stack_size = ::std::isize::MAX as usize;
+
+    let (start_count, start_handler) = count_handler();
+    let (exit_count, exit_handler) = count_handler();
+    let builder = ThreadPoolBuilder::new()
+        .num_threads(10)
+        .stack_size(stack_size)
+        .start_handler(start_handler)
+        .exit_handler(exit_handler);
+
+    let pool = builder.build();
+    assert!(pool.is_err(), "thread stack should have failed!");
+
+    // With such a huge stack, 64-bit will probably fail on the first thread;
+    // 32-bit might manage the first 2GB, but certainly fail the second.
+    let start_count = wait_for_counter(start_count);
+    assert!(start_count <= 1);
+    assert_eq!(start_count, wait_for_counter(exit_count));
+}
+
+#[test]
+#[cfg_attr(not(panic = "unwind"), ignore)]
+fn panic_thread_name() {
+    let (start_count, start_handler) = count_handler();
+    let (exit_count, exit_handler) = count_handler();
+    let builder = ThreadPoolBuilder::new()
+        .num_threads(10)
+        .start_handler(start_handler)
+        .exit_handler(exit_handler)
+        .thread_name(|i| {
+            if i >= 5 {
+                panic!();
+            }
+            format!("panic_thread_name#{}", i)
+        });
+
+    let pool = crate::unwind::halt_unwinding(|| builder.build());
+    assert!(pool.is_err(), "thread-name panic should propagate!");
+
+    // Assuming they're created in order, threads 0 through 4 should have
+    // been started already, and then terminated by the panic.
+    assert_eq!(5, wait_for_counter(start_count));
+    assert_eq!(5, wait_for_counter(exit_count));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn self_install() {
+    let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
+
+    // If the inner `install` blocks, then nothing will actually run it!
+    assert!(pool.install(|| pool.install(|| true)));
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn mutual_install() {
+    let pool1 = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
+    let pool2 = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
+
+    let ok = pool1.install(|| {
+        // This creates a dependency from `pool1` -> `pool2`
+        pool2.install(|| {
+            // This creates a dependency from `pool2` -> `pool1`
+            pool1.install(|| {
+                // If they blocked on inter-pool installs, there would be no
+                // threads left to run this!
+                true
+            })
+        })
+    });
+    assert!(ok);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn mutual_install_sleepy() {
+    use std::{thread, time};
+
+    let pool1 = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
+    let pool2 = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
+
+    let ok = pool1.install(|| {
+        // This creates a dependency from `pool1` -> `pool2`
+        pool2.install(|| {
+            // Give `pool1` time to fall asleep.
+            thread::sleep(time::Duration::from_secs(1));
+
+            // This creates a dependency from `pool2` -> `pool1`
+            pool1.install(|| {
+                // Give `pool2` time to fall asleep.
+                thread::sleep(time::Duration::from_secs(1));
+
+                // If they blocked on inter-pool installs, there would be no
+                // threads left to run this!
+                true
+            })
+        })
+    });
+    assert!(ok);
+}
+
+#[test]
+#[allow(deprecated)]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn check_thread_pool_new() {
+    let pool = ThreadPool::new(crate::Configuration::new().num_threads(22)).unwrap();
+    assert_eq!(pool.current_num_threads(), 22);
+}
+
+macro_rules! test_scope_order {
+    ($scope:ident => $spawn:ident) => {{
+        let builder = ThreadPoolBuilder::new().num_threads(1);
+        let pool = builder.build().unwrap();
+        pool.install(|| {
+            let vec = Mutex::new(vec![]);
+            pool.$scope(|scope| {
+                let vec = &vec;
+                for i in 0..10 {
+                    scope.$spawn(move |_| {
+                        vec.lock().unwrap().push(i);
+                    });
+                }
+            });
+            vec.into_inner().unwrap()
+        })
+    }};
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn scope_lifo_order() {
+    let vec = test_scope_order!(scope => spawn);
+    let expected: Vec<i32> = (0..10).rev().collect(); // LIFO -> reversed
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn scope_fifo_order() {
+    let vec = test_scope_order!(scope_fifo => spawn_fifo);
+    let expected: Vec<i32> = (0..10).collect(); // FIFO -> natural order
+    assert_eq!(vec, expected);
+}
+
+macro_rules! test_spawn_order {
+    ($spawn:ident) => {{
+        let builder = ThreadPoolBuilder::new().num_threads(1);
+        let pool = &builder.build().unwrap();
+        let (tx, rx) = channel();
+        pool.install(move || {
+            for i in 0..10 {
+                let tx = tx.clone();
+                pool.$spawn(move || {
+                    tx.send(i).unwrap();
+                });
+            }
+        });
+        rx.iter().collect::<Vec<i32>>()
+    }};
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_lifo_order() {
+    let vec = test_spawn_order!(spawn);
+    let expected: Vec<i32> = (0..10).rev().collect(); // LIFO -> reversed
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_fifo_order() {
+    let vec = test_spawn_order!(spawn_fifo);
+    let expected: Vec<i32> = (0..10).collect(); // FIFO -> natural order
+    assert_eq!(vec, expected);
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn nested_scopes() {
+    // Create matching scopes for every thread pool.
+    fn nest<'scope, OP>(pools: &[ThreadPool], scopes: Vec<&Scope<'scope>>, op: OP)
+    where
+        OP: FnOnce(&[&Scope<'scope>]) + Send,
+    {
+        if let Some((pool, tail)) = pools.split_first() {
+            pool.scope(move |s| {
+                // This move reduces the reference lifetimes by variance to match s,
+                // but the actual scopes are still tied to the invariant 'scope.
+                let mut scopes = scopes;
+                scopes.push(s);
+                nest(tail, scopes, op)
+            })
+        } else {
+            (op)(&scopes)
+        }
+    }
+
+    let pools: Vec<_> =
+        (0..10).map(|_| ThreadPoolBuilder::new().num_threads(1).build().unwrap()).collect();
+
+    let counter = AtomicUsize::new(0);
+    nest(&pools, vec![], |scopes| {
+        for &s in scopes {
+            s.spawn(|_| {
+                // Our 'scope lets us borrow the counter in every pool.
+                counter.fetch_add(1, Ordering::Relaxed);
+            });
+        }
+    });
+    assert_eq!(counter.into_inner(), pools.len());
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn nested_fifo_scopes() {
+    // Create matching fifo scopes for every thread pool.
+    fn nest<'scope, OP>(pools: &[ThreadPool], scopes: Vec<&ScopeFifo<'scope>>, op: OP)
+    where
+        OP: FnOnce(&[&ScopeFifo<'scope>]) + Send,
+    {
+        if let Some((pool, tail)) = pools.split_first() {
+            pool.scope_fifo(move |s| {
+                // This move reduces the reference lifetimes by variance to match s,
+                // but the actual scopes are still tied to the invariant 'scope.
+                let mut scopes = scopes;
+                scopes.push(s);
+                nest(tail, scopes, op)
+            })
+        } else {
+            (op)(&scopes)
+        }
+    }
+
+    let pools: Vec<_> =
+        (0..10).map(|_| ThreadPoolBuilder::new().num_threads(1).build().unwrap()).collect();
+
+    let counter = AtomicUsize::new(0);
+    nest(&pools, vec![], |scopes| {
+        for &s in scopes {
+            s.spawn_fifo(|_| {
+                // Our 'scope lets us borrow the counter in every pool.
+                counter.fetch_add(1, Ordering::Relaxed);
+            });
+        }
+    });
+    assert_eq!(counter.into_inner(), pools.len());
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn in_place_scope_no_deadlock() {
+    let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
+    let (tx, rx) = channel();
+    let rx_ref = &rx;
+    pool.in_place_scope(move |s| {
+        // With regular scopes this closure would never run because this scope op
+        // itself would block the only worker thread.
+        s.spawn(move |_| {
+            tx.send(()).unwrap();
+        });
+        rx_ref.recv().unwrap();
+    });
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn in_place_scope_fifo_no_deadlock() {
+    let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
+    let (tx, rx) = channel();
+    let rx_ref = &rx;
+    pool.in_place_scope_fifo(move |s| {
+        // With regular scopes this closure would never run because this scope op
+        // itself would block the only worker thread.
+        s.spawn_fifo(move |_| {
+            tx.send(()).unwrap();
+        });
+        rx_ref.recv().unwrap();
+    });
+}
+
+#[test]
+fn yield_now_to_spawn() {
+    let (tx, rx) = channel();
+
+    // Queue a regular spawn.
+    crate::spawn(move || tx.send(22).unwrap());
+
+    // The single-threaded fallback mode (for wasm etc.) won't
+    // get a chance to run the spawn if we never yield to it.
+    crate::registry::in_worker(move |_, _| {
+        crate::yield_now();
+    });
+
+    // The spawn **must** have started by now, but we still might have to wait
+    // for it to finish if a different thread stole it first.
+    assert_eq!(22, rx.recv().unwrap());
+}
+
+#[test]
+fn yield_local_to_spawn() {
+    let (tx, rx) = channel();
+
+    // Queue a regular spawn.
+    crate::spawn(move || tx.send(22).unwrap());
+
+    // The single-threaded fallback mode (for wasm etc.) won't
+    // get a chance to run the spawn if we never yield to it.
+    crate::registry::in_worker(move |_, _| {
+        crate::yield_local();
+    });
+
+    // The spawn **must** have started by now, but we still might have to wait
+    // for it to finish if a different thread stole it first.
+    assert_eq!(22, rx.recv().unwrap());
+}
diff --git a/compiler/rustc_thread_pool/src/tlv.rs b/compiler/rustc_thread_pool/src/tlv.rs
new file mode 100644
index 00000000000..b5f63479e2f
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/tlv.rs
@@ -0,0 +1,32 @@
+//! Allows access to the Rayon's thread local value
+//! which is preserved when moving jobs across threads
+
+use std::cell::Cell;
+use std::ptr;
+
+thread_local!(pub static TLV: Cell<*const ()> = const { Cell::new(ptr::null()) });
+
+#[derive(Copy, Clone)]
+pub(crate) struct Tlv(pub(crate) *const ());
+
+impl Tlv {
+    #[inline]
+    pub(crate) fn null() -> Self {
+        Self(ptr::null())
+    }
+}
+
+unsafe impl Sync for Tlv {}
+unsafe impl Send for Tlv {}
+
+/// Sets the current thread-local value
+#[inline]
+pub(crate) fn set(value: Tlv) {
+    TLV.with(|tlv| tlv.set(value.0));
+}
+
+/// Returns the current thread-local value
+#[inline]
+pub(crate) fn get() -> Tlv {
+    TLV.with(|tlv| Tlv(tlv.get()))
+}
diff --git a/compiler/rustc_thread_pool/src/unwind.rs b/compiler/rustc_thread_pool/src/unwind.rs
new file mode 100644
index 00000000000..9671fa57821
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/unwind.rs
@@ -0,0 +1,31 @@
+//! Package up unwind recovery. Note that if you are in some sensitive
+//! place, you can use the `AbortIfPanic` helper to protect against
+//! accidental panics in the rayon code itself.
+
+use std::any::Any;
+use std::panic::{self, AssertUnwindSafe};
+use std::thread;
+
+/// Executes `f` and captures any panic, translating that panic into a
+/// `Err` result. The assumption is that any panic will be propagated
+/// later with `resume_unwinding`, and hence `f` can be treated as
+/// exception safe.
+pub(super) fn halt_unwinding<F, R>(func: F) -> thread::Result<R>
+where
+    F: FnOnce() -> R,
+{
+    panic::catch_unwind(AssertUnwindSafe(func))
+}
+
+pub(super) fn resume_unwinding(payload: Box<dyn Any + Send>) -> ! {
+    panic::resume_unwind(payload)
+}
+
+pub(super) struct AbortIfPanic;
+
+impl Drop for AbortIfPanic {
+    fn drop(&mut self) {
+        eprintln!("Rayon: detected unexpected panic; aborting");
+        ::std::process::abort();
+    }
+}
diff --git a/compiler/rustc_thread_pool/src/worker_local.rs b/compiler/rustc_thread_pool/src/worker_local.rs
new file mode 100644
index 00000000000..d108c91f9ee
--- /dev/null
+++ b/compiler/rustc_thread_pool/src/worker_local.rs
@@ -0,0 +1,75 @@
+use std::fmt;
+use std::ops::Deref;
+use std::sync::Arc;
+
+use crate::registry::{Registry, WorkerThread};
+
+#[repr(align(64))]
+#[derive(Debug)]
+struct CacheAligned<T>(T);
+
+/// Holds worker-locals values for each thread in a thread pool.
+/// You can only access the worker local value through the Deref impl
+/// on the thread pool it was constructed on. It will panic otherwise
+pub struct WorkerLocal<T> {
+    locals: Vec<CacheAligned<T>>,
+    registry: Arc<Registry>,
+}
+
+/// We prevent concurrent access to the underlying value in the
+/// Deref impl, thus any values safe to send across threads can
+/// be used with WorkerLocal.
+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 thread pool.
+    #[inline]
+    pub fn new<F: FnMut(usize) -> T>(mut initial: F) -> WorkerLocal<T> {
+        let registry = Registry::current();
+        WorkerLocal {
+            locals: (0..registry.num_threads()).map(|i| CacheAligned(initial(i))).collect(),
+            registry,
+        }
+    }
+
+    /// Returns the worker-local value for each thread
+    #[inline]
+    pub fn into_inner(self) -> Vec<T> {
+        self.locals.into_iter().map(|c| c.0).collect()
+    }
+
+    fn current(&self) -> &T {
+        unsafe {
+            let worker_thread = WorkerThread::current();
+            if worker_thread.is_null()
+                || &*(*worker_thread).registry as *const _ != &*self.registry as *const _
+            {
+                panic!("WorkerLocal can only be used on the thread pool it was created on")
+            }
+            &self.locals[(*worker_thread).index].0
+        }
+    }
+}
+
+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: fmt::Debug> fmt::Debug for WorkerLocal<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("WorkerLocal").field("registry", &self.registry.id()).finish()
+    }
+}
+
+impl<T> Deref for WorkerLocal<T> {
+    type Target = T;
+
+    #[inline(always)]
+    fn deref(&self) -> &T {
+        self.current()
+    }
+}
diff --git a/compiler/rustc_thread_pool/tests/double_init_fail.rs b/compiler/rustc_thread_pool/tests/double_init_fail.rs
new file mode 100644
index 00000000000..85e509518d4
--- /dev/null
+++ b/compiler/rustc_thread_pool/tests/double_init_fail.rs
@@ -0,0 +1,15 @@
+#![allow(unused_crate_dependencies)]
+
+use std::error::Error;
+
+use rustc_thread_pool::ThreadPoolBuilder;
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn double_init_fail() {
+    let result1 = ThreadPoolBuilder::new().build_global();
+    assert!(result1.is_ok());
+    let err = ThreadPoolBuilder::new().build_global().unwrap_err();
+    assert!(err.source().is_none());
+    assert_eq!(err.to_string(), "The global thread pool has already been initialized.",);
+}
diff --git a/compiler/rustc_thread_pool/tests/init_zero_threads.rs b/compiler/rustc_thread_pool/tests/init_zero_threads.rs
new file mode 100644
index 00000000000..261493fcb7b
--- /dev/null
+++ b/compiler/rustc_thread_pool/tests/init_zero_threads.rs
@@ -0,0 +1,9 @@
+#![allow(unused_crate_dependencies)]
+
+use rustc_thread_pool::ThreadPoolBuilder;
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn init_zero_threads() {
+    ThreadPoolBuilder::new().num_threads(0).build_global().unwrap();
+}
diff --git a/compiler/rustc_thread_pool/tests/scope_join.rs b/compiler/rustc_thread_pool/tests/scope_join.rs
new file mode 100644
index 00000000000..83468da81c0
--- /dev/null
+++ b/compiler/rustc_thread_pool/tests/scope_join.rs
@@ -0,0 +1,47 @@
+#![allow(unused_crate_dependencies)]
+
+/// Test that one can emulate join with `scope`:
+fn pseudo_join<F, G>(f: F, g: G)
+where
+    F: FnOnce() + Send,
+    G: FnOnce() + Send,
+{
+    rustc_thread_pool::scope(|s| {
+        s.spawn(|_| g());
+        f();
+    });
+}
+
+fn quick_sort<T: PartialOrd + Send>(v: &mut [T]) {
+    if v.len() <= 1 {
+        return;
+    }
+
+    let mid = partition(v);
+    let (lo, hi) = v.split_at_mut(mid);
+    pseudo_join(|| quick_sort(lo), || quick_sort(hi));
+}
+
+fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
+    let pivot = v.len() - 1;
+    let mut i = 0;
+    for j in 0..pivot {
+        if v[j] <= v[pivot] {
+            v.swap(i, j);
+            i += 1;
+        }
+    }
+    v.swap(i, pivot);
+    i
+}
+
+fn is_sorted<T: Send + Ord>(v: &[T]) -> bool {
+    (1..v.len()).all(|i| v[i - 1] <= v[i])
+}
+
+#[test]
+fn scope_join() {
+    let mut v: Vec<i32> = (0..256).rev().collect();
+    quick_sort(&mut v);
+    assert!(is_sorted(&v));
+}
diff --git a/compiler/rustc_thread_pool/tests/scoped_threadpool.rs b/compiler/rustc_thread_pool/tests/scoped_threadpool.rs
new file mode 100644
index 00000000000..295da650e88
--- /dev/null
+++ b/compiler/rustc_thread_pool/tests/scoped_threadpool.rs
@@ -0,0 +1,99 @@
+#![allow(unused_crate_dependencies)]
+
+use crossbeam_utils::thread;
+use rustc_thread_pool::ThreadPoolBuilder;
+
+#[derive(PartialEq, Eq, Debug)]
+struct Local(i32);
+
+scoped_tls::scoped_thread_local!(static LOCAL: Local);
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn missing_scoped_tls() {
+    LOCAL.set(&Local(42), || {
+        let pool = ThreadPoolBuilder::new().build().expect("thread pool created");
+
+        // `LOCAL` is not set in the pool.
+        pool.install(|| {
+            assert!(!LOCAL.is_set());
+        });
+    });
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn spawn_scoped_tls_threadpool() {
+    LOCAL.set(&Local(42), || {
+        LOCAL.with(|x| {
+            thread::scope(|scope| {
+                let pool = ThreadPoolBuilder::new()
+                    .spawn_handler(move |thread| {
+                        scope
+                            .builder()
+                            .spawn(move |_| {
+                                // Borrow the same local value in the thread pool.
+                                LOCAL.set(x, || thread.run())
+                            })
+                            .map(|_| ())
+                    })
+                    .build()
+                    .expect("thread pool created");
+
+                // The pool matches our local value.
+                pool.install(|| {
+                    assert!(LOCAL.is_set());
+                    LOCAL.with(|y| {
+                        assert_eq!(x, y);
+                    });
+                });
+
+                // If we change our local value, the pool is not affected.
+                LOCAL.set(&Local(-1), || {
+                    pool.install(|| {
+                        assert!(LOCAL.is_set());
+                        LOCAL.with(|y| {
+                            assert_eq!(x, y);
+                        });
+                    });
+                });
+            })
+            .expect("scope threads ok");
+            // `thread::scope` will wait for the threads to exit before returning.
+        });
+    });
+}
+
+#[test]
+#[cfg_attr(any(target_os = "emscripten", target_family = "wasm"), ignore)]
+fn build_scoped_tls_threadpool() {
+    LOCAL.set(&Local(42), || {
+        LOCAL.with(|x| {
+            ThreadPoolBuilder::new()
+                .build_scoped(
+                    move |thread| LOCAL.set(x, || thread.run()),
+                    |pool| {
+                        // The pool matches our local value.
+                        pool.install(|| {
+                            assert!(LOCAL.is_set());
+                            LOCAL.with(|y| {
+                                assert_eq!(x, y);
+                            });
+                        });
+
+                        // If we change our local value, the pool is not affected.
+                        LOCAL.set(&Local(-1), || {
+                            pool.install(|| {
+                                assert!(LOCAL.is_set());
+                                LOCAL.with(|y| {
+                                    assert_eq!(x, y);
+                                });
+                            });
+                        });
+                    },
+                )
+                .expect("thread pool created");
+            // Internally, `std::thread::scope` will wait for the threads to exit before returning.
+        });
+    });
+}
diff --git a/compiler/rustc_thread_pool/tests/simple_panic.rs b/compiler/rustc_thread_pool/tests/simple_panic.rs
new file mode 100644
index 00000000000..b35b4d632d2
--- /dev/null
+++ b/compiler/rustc_thread_pool/tests/simple_panic.rs
@@ -0,0 +1,9 @@
+#![allow(unused_crate_dependencies)]
+
+use rustc_thread_pool::join;
+
+#[test]
+#[should_panic(expected = "should panic")]
+fn simple_panic() {
+    join(|| {}, || panic!("should panic"));
+}
diff --git a/compiler/rustc_thread_pool/tests/stack_overflow_crash.rs b/compiler/rustc_thread_pool/tests/stack_overflow_crash.rs
new file mode 100644
index 00000000000..805b6d8ee3f
--- /dev/null
+++ b/compiler/rustc_thread_pool/tests/stack_overflow_crash.rs
@@ -0,0 +1,87 @@
+#![allow(unused_crate_dependencies)]
+
+use std::env;
+#[cfg(target_os = "linux")]
+use std::os::unix::process::ExitStatusExt;
+use std::process::{Command, ExitStatus, Stdio};
+
+use rustc_thread_pool::ThreadPoolBuilder;
+
+fn force_stack_overflow(depth: u32) {
+    let mut buffer = [0u8; 1024 * 1024];
+    #[allow(clippy::incompatible_msrv)]
+    std::hint::black_box(&mut buffer);
+    if depth > 0 {
+        force_stack_overflow(depth - 1);
+    }
+}
+
+#[cfg(unix)]
+fn disable_core() {
+    unsafe {
+        libc::setrlimit(libc::RLIMIT_CORE, &libc::rlimit { rlim_cur: 0, rlim_max: 0 });
+    }
+}
+
+#[cfg(unix)]
+fn overflow_code() -> Option<i32> {
+    None
+}
+
+#[cfg(windows)]
+fn overflow_code() -> Option<i32> {
+    use std::os::windows::process::ExitStatusExt;
+
+    ExitStatus::from_raw(0xc00000fd /*STATUS_STACK_OVERFLOW*/).code()
+}
+
+#[test]
+#[cfg_attr(not(any(unix, windows)), ignore)]
+fn stack_overflow_crash() {
+    // First check that the recursive call actually causes a stack overflow,
+    // and does not get optimized away.
+    let status = run_ignored("run_with_small_stack");
+    assert!(!status.success());
+    #[cfg(any(unix, windows))]
+    assert_eq!(status.code(), overflow_code());
+    #[cfg(target_os = "linux")]
+    assert!(matches!(status.signal(), Some(libc::SIGABRT | libc::SIGSEGV)));
+
+    // Now run with a larger stack and verify correct operation.
+    let status = run_ignored("run_with_large_stack");
+    assert_eq!(status.code(), Some(0));
+    #[cfg(target_os = "linux")]
+    assert_eq!(status.signal(), None);
+}
+
+fn run_ignored(test: &str) -> ExitStatus {
+    Command::new(env::current_exe().unwrap())
+        .arg("--ignored")
+        .arg("--exact")
+        .arg(test)
+        .stdout(Stdio::null())
+        .stderr(Stdio::null())
+        .status()
+        .unwrap()
+}
+
+#[test]
+#[ignore]
+fn run_with_small_stack() {
+    run_with_stack(8);
+}
+
+#[test]
+#[ignore]
+fn run_with_large_stack() {
+    run_with_stack(48);
+}
+
+fn run_with_stack(stack_size_in_mb: usize) {
+    let pool = ThreadPoolBuilder::new().stack_size(stack_size_in_mb * 1024 * 1024).build().unwrap();
+    pool.install(|| {
+        #[cfg(unix)]
+        disable_core();
+        force_stack_overflow(32);
+    });
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 0c88bd3dcbc..db2517a8379 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1850,7 +1850,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let trait_def_id = trait_pred.def_id();
         let trait_name = self.tcx.item_name(trait_def_id);
         let crate_name = self.tcx.crate_name(trait_def_id.krate);
-        if let Some(other_trait_def_id) = self.tcx.all_traits().find(|def_id| {
+        if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| {
             trait_name == self.tcx.item_name(trait_def_id)
                 && trait_def_id.krate != def_id.krate
                 && crate_name == self.tcx.crate_name(def_id.krate)
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index ee5a5b247ce..2bbf90ed3ed 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -2721,6 +2721,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             ObligationCauseCode::TupleElem => {
                 err.note("only the last element of a tuple may have a dynamically sized type");
             }
+            ObligationCauseCode::DynCompatible(span) => {
+                err.multipart_suggestion(
+                    "you might have meant to use `Self` to refer to the implementing type",
+                    vec![(span, "Self".into())],
+                    Applicability::MachineApplicable,
+                );
+            }
             ObligationCauseCode::WhereClause(item_def_id, span)
             | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)
             | ObligationCauseCode::HostEffectInExpr(item_def_id, span, ..)
@@ -2872,13 +2879,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         _ => (),
                     }
                 }
-                let descr = format!("required by {a} bound in `{item_name}`");
-                if span.is_visible(sm) {
-                    let msg = format!("required by {this} in `{short_item_name}`");
-                    multispan.push_span_label(span, msg);
-                    err.span_note(multispan, descr);
+
+                // If this is from a format string literal desugaring,
+                // we've already said "required by this formatting parameter"
+                let is_in_fmt_lit = if let Some(s) = err.span.primary_span() {
+                    matches!(s.desugaring_kind(), Some(DesugaringKind::FormatLiteral { .. }))
                 } else {
-                    err.span_note(tcx.def_span(item_def_id), descr);
+                    false
+                };
+                if !is_in_fmt_lit {
+                    let descr = format!("required by {a} bound in `{item_name}`");
+                    if span.is_visible(sm) {
+                        let msg = format!("required by {this} in `{short_item_name}`");
+                        multispan.push_span_label(span, msg);
+                        err.span_note(multispan, descr);
+                    } else {
+                        err.span_note(tcx.def_span(item_def_id), descr);
+                    }
                 }
                 if let Some(note) = note {
                     err.note(note);
@@ -3575,11 +3592,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
             ObligationCauseCode::TrivialBound => {
                 err.help("see issue #48214");
-                tcx.disabled_nightly_features(
-                    err,
-                    Some(tcx.local_def_id_to_hir_id(body_id)),
-                    [(String::new(), sym::trivial_bounds)],
-                );
+                tcx.disabled_nightly_features(err, [(String::new(), sym::trivial_bounds)]);
             }
             ObligationCauseCode::OpaqueReturnType(expr_info) => {
                 let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info {
@@ -3977,7 +3990,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             ) = expr.kind
             {
                 if Some(*span) != err.span.primary_span() {
-                    err.span_label(*span, "required by a bound introduced by this call");
+                    let msg = if span.is_desugaring(DesugaringKind::FormatLiteral { source: true })
+                    {
+                        "required by this formatting parameter"
+                    } else if span.is_desugaring(DesugaringKind::FormatLiteral { source: false }) {
+                        "required by a formatting parameter in this expression"
+                    } else {
+                        "required by a bound introduced by this call"
+                    };
+                    err.span_label(*span, msg);
                 }
             }
 
diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
index 84e7686fdd3..ec3c1ba4a45 100644
--- a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
+++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
@@ -163,12 +163,14 @@ impl RegionExplanation<'_> {
 
 impl Subdiagnostic for RegionExplanation<'_> {
     fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
+        diag.store_args();
         diag.arg("pref_kind", self.prefix);
         diag.arg("suff_kind", self.suffix);
         diag.arg("desc_kind", self.desc.kind);
         diag.arg("desc_arg", self.desc.arg);
 
         let msg = diag.eagerly_translate(fluent::trait_selection_region_explanation);
+        diag.restore_args();
         if let Some(span) = self.desc.span {
             diag.span_note(span, msg);
         } else {
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index ed99c678a4d..d56042a5ca2 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -14,7 +14,7 @@ use rustc_middle::ty::{
 };
 use rustc_next_trait_solver::delegate::SolverDelegate as _;
 use rustc_next_trait_solver::solve::{
-    GenerateProofTree, GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt as _,
+    GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt as _,
 };
 use rustc_span::Span;
 use thin_vec::ThinVec;
@@ -106,14 +106,11 @@ impl<'tcx> ObligationStorage<'tcx> {
             self.overflowed.extend(
                 ExtractIf::new(&mut self.pending, |(o, stalled_on)| {
                     let goal = o.as_goal();
-                    let result = <&SolverDelegate<'tcx>>::from(infcx)
-                        .evaluate_root_goal(
-                            goal,
-                            GenerateProofTree::No,
-                            o.cause.span,
-                            stalled_on.take(),
-                        )
-                        .0;
+                    let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
+                        goal,
+                        o.cause.span,
+                        stalled_on.take(),
+                    );
                     matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
                 })
                 .map(|(o, _)| o),
@@ -207,14 +204,7 @@ where
                     continue;
                 }
 
-                let result = delegate
-                    .evaluate_root_goal(
-                        goal,
-                        GenerateProofTree::No,
-                        obligation.cause.span,
-                        stalled_on,
-                    )
-                    .0;
+                let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on);
                 self.inspect_evaluated_obligation(infcx, &obligation, &result);
                 let GoalEvaluation { certainty, has_changed, stalled_on } = match result {
                     Ok(result) => result,
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
index 36a8ae675c0..fe248b033bb 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
@@ -11,9 +11,7 @@ use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
-use rustc_next_trait_solver::solve::{
-    GenerateProofTree, GoalEvaluation, SolverDelegateEvalExt as _,
-};
+use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt as _};
 use tracing::{instrument, trace};
 
 use crate::solve::delegate::SolverDelegate;
@@ -90,15 +88,11 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
     root_obligation: PredicateObligation<'tcx>,
 ) -> FulfillmentError<'tcx> {
     let (code, refine_obligation) = infcx.probe(|_| {
-        match <&SolverDelegate<'tcx>>::from(infcx)
-            .evaluate_root_goal(
-                root_obligation.as_goal(),
-                GenerateProofTree::No,
-                root_obligation.cause.span,
-                None,
-            )
-            .0
-        {
+        match <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
+            root_obligation.as_goal(),
+            root_obligation.cause.span,
+            None,
+        ) {
             Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => {
                 (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
             }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index b0c8fa1f217..80df0fab2d8 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -20,7 +20,7 @@ use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit};
 use rustc_middle::{bug, ty};
 use rustc_next_trait_solver::resolve::eager_resolve_vars;
 use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state};
-use rustc_next_trait_solver::solve::{GenerateProofTree, MaybeCause, SolverDelegateEvalExt as _};
+use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _};
 use rustc_span::Span;
 use tracing::instrument;
 
@@ -248,9 +248,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
                     // considering the constrained RHS, and pass the resulting certainty to
                     // `InspectGoal::new` so that the goal has the right result (and maintains
                     // the impression that we don't do this normalizes-to infer hack at all).
-                    let (nested, proof_tree) =
-                        infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes, None);
-                    let proof_tree = proof_tree.unwrap();
+                    let (nested, proof_tree) = infcx.evaluate_root_goal_for_proof_tree(goal, span);
                     let nested_goals_result = nested.and_then(|(nested, _)| {
                         normalizes_to_term_hack.constrain_and(
                             infcx,
@@ -284,9 +282,8 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
                 // into another candidate who ends up with different inference
                 // constraints, we get an ICE if we already applied the constraints
                 // from the chosen candidate.
-                let proof_tree = infcx
-                    .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes, span, None).1)
-                    .unwrap();
+                let proof_tree =
+                    infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, span).1);
                 InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
             }
         }
@@ -488,13 +485,8 @@ impl<'tcx> InferCtxt<'tcx> {
         depth: usize,
         visitor: &mut V,
     ) -> V::Result {
-        let (_, proof_tree) = <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(
-            goal,
-            GenerateProofTree::Yes,
-            visitor.span(),
-            None,
-        );
-        let proof_tree = proof_tree.unwrap();
+        let (_, proof_tree) = <&SolverDelegate<'tcx>>::from(self)
+            .evaluate_root_goal_for_proof_tree(goal, visitor.span());
         visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc))
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index ee30956295a..bdfe48a3928 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -31,7 +31,7 @@ use crate::traits::{
 ///
 /// Currently that is `Self` in supertraits. This is needed
 /// because `dyn_compatibility_violations` can't be used during
-/// type collection.
+/// type collection, as type collection is needed for `dyn_compatiblity_violations` itself.
 #[instrument(level = "debug", skip(tcx), ret)]
 pub fn hir_ty_lowering_dyn_compatibility_violations(
     tcx: TyCtxt<'_>,
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index b275cd382ab..60f8bd9d83a 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -226,7 +226,11 @@ fn recurse_build<'tcx>(
         ExprKind::Yield { .. } => {
             error(GenericConstantTooComplexSub::YieldNotSupported(node.span))?
         }
-        ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => {
+        ExprKind::Continue { .. }
+        | ExprKind::ConstContinue { .. }
+        | ExprKind::Break { .. }
+        | ExprKind::Loop { .. }
+        | ExprKind::LoopMatch { .. } => {
             error(GenericConstantTooComplexSub::LoopNotSupported(node.span))?
         }
         ExprKind::Box { .. } => error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?,
@@ -329,6 +333,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
             | thir::ExprKind::NeverToAny { .. }
             | thir::ExprKind::PointerCoercion { .. }
             | thir::ExprKind::Loop { .. }
+            | thir::ExprKind::LoopMatch { .. }
             | thir::ExprKind::Let { .. }
             | thir::ExprKind::Match { .. }
             | thir::ExprKind::Block { .. }
@@ -342,6 +347,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
             | thir::ExprKind::RawBorrow { .. }
             | thir::ExprKind::Break { .. }
             | thir::ExprKind::Continue { .. }
+            | thir::ExprKind::ConstContinue { .. }
             | thir::ExprKind::Return { .. }
             | thir::ExprKind::Become { .. }
             | thir::ExprKind::Array { .. }
diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs
index fa5e8d43702..d88c88fc6f3 100644
--- a/compiler/rustc_type_ir/src/fast_reject.rs
+++ b/compiler/rustc_type_ir/src/fast_reject.rs
@@ -240,6 +240,10 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_
         self.types_may_unify_inner(lhs, rhs, Self::STARTING_DEPTH)
     }
 
+    pub fn types_may_unify_with_depth(self, lhs: I::Ty, rhs: I::Ty, depth_limit: usize) -> bool {
+        self.types_may_unify_inner(lhs, rhs, depth_limit)
+    }
+
     fn args_may_unify_inner(
         self,
         obligation_args: I::GenericArgs,
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 033d2579678..ffc2903ad1c 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -271,6 +271,13 @@ pub trait Interner:
         def_id: Self::DefId,
     ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>;
 
+    /// This is equivalent to computing the super-predicates of the trait for this impl
+    /// and filtering them to the outlives predicates. This is purely for performance.
+    fn impl_super_outlives(
+        self,
+        impl_def_id: Self::DefId,
+    ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
+
     fn impl_is_const(self, def_id: Self::DefId) -> bool;
     fn fn_is_const(self, def_id: Self::DefId) -> bool;
     fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool;
diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs
index a2442660259..1b99cc820f1 100644
--- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs
+++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs
@@ -2,6 +2,7 @@ use derive_where::derive_where;
 
 use super::{AvailableDepth, Cx, NestedGoals};
 use crate::data_structures::HashMap;
+use crate::search_graph::EvaluationResult;
 
 struct Success<X: Cx> {
     required_depth: usize,
@@ -43,28 +44,26 @@ impl<X: Cx> GlobalCache<X> {
         &mut self,
         cx: X,
         input: X::Input,
-
-        origin_result: X::Result,
+        evaluation_result: EvaluationResult<X>,
         dep_node: X::DepNodeIndex,
-
-        required_depth: usize,
-        encountered_overflow: bool,
-        nested_goals: NestedGoals<X>,
     ) {
-        let result = cx.mk_tracked(origin_result, dep_node);
+        let EvaluationResult { encountered_overflow, required_depth, heads, nested_goals, result } =
+            evaluation_result;
+        debug_assert!(heads.is_empty());
+        let result = cx.mk_tracked(result, dep_node);
         let entry = self.map.entry(input).or_default();
         if encountered_overflow {
             let with_overflow = WithOverflow { nested_goals, result };
             let prev = entry.with_overflow.insert(required_depth, with_overflow);
             if let Some(prev) = &prev {
                 assert!(cx.evaluation_is_concurrent());
-                assert_eq!(cx.get_tracked(&prev.result), origin_result);
+                assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result);
             }
         } else {
             let prev = entry.success.replace(Success { required_depth, nested_goals, result });
             if let Some(prev) = &prev {
                 assert!(cx.evaluation_is_concurrent());
-                assert_eq!(cx.get_tracked(&prev.result), origin_result);
+                assert_eq!(cx.get_tracked(&prev.result), evaluation_result.result);
             }
         }
     }
diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs
index b59b4f92854..a857da2fcd5 100644
--- a/compiler/rustc_type_ir/src/search_graph/mod.rs
+++ b/compiler/rustc_type_ir/src/search_graph/mod.rs
@@ -1,16 +1,16 @@
-/// The search graph is responsible for caching and cycle detection in the trait
-/// solver. Making sure that caching doesn't result in soundness bugs or unstable
-/// query results is very challenging and makes this one of the most-involved
-/// self-contained components of the compiler.
-///
-/// We added fuzzing support to test its correctness. The fuzzers used to verify
-/// the current implementation can be found in https://github.com/lcnr/search_graph_fuzz.
-///
-/// This is just a quick overview of the general design, please check out the relevant
-/// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for
-/// more details. Caching is split between a global cache and the per-cycle `provisional_cache`.
-/// The global cache has to be completely unobservable, while the per-cycle cache may impact
-/// behavior as long as the resulting behavior is still correct.
+//! The search graph is responsible for caching and cycle detection in the trait
+//! solver. Making sure that caching doesn't result in soundness bugs or unstable
+//! query results is very challenging and makes this one of the most-involved
+//! self-contained components of the compiler.
+//!
+//! We added fuzzing support to test its correctness. The fuzzers used to verify
+//! the current implementation can be found in <https://github.com/lcnr/search_graph_fuzz>.
+//!
+//! This is just a quick overview of the general design, please check out the relevant
+//! [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for
+//! more details. Caching is split between a global cache and the per-cycle `provisional_cache`.
+//! The global cache has to be completely unobservable, while the per-cycle cache may impact
+//! behavior as long as the resulting behavior is still correct.
 use std::cmp::Ordering;
 use std::collections::BTreeMap;
 use std::collections::hash_map::Entry;
@@ -381,18 +381,16 @@ impl PathsToNested {
 /// The nested goals of each stack entry and the path from the
 /// stack entry to that nested goal.
 ///
+/// They are used when checking whether reevaluating a global cache
+/// would encounter a cycle or use a provisional cache entry given the
+/// currentl search graph state. We need to disable the global cache
+/// in this case as it could otherwise result in behaviorial differences.
+/// Cycles can impact behavior. The cycle ABA may have different final
+/// results from a the cycle BAB depending on the cycle root.
+///
 /// We only start tracking nested goals once we've either encountered
 /// overflow or a solver cycle. This is a performance optimization to
 /// avoid tracking nested goals on the happy path.
-///
-/// We use nested goals for two reasons:
-/// - when rebasing provisional cache entries
-/// - when checking whether we have to ignore a global cache entry as reevaluating
-///   it would encounter a cycle or use a provisional cache entry.
-///
-/// We need to disable the global cache if using it would hide a cycle, as
-/// cycles can impact behavior. The cycle ABA may have different final
-/// results from a the cycle BAB depending on the cycle root.
 #[derive_where(Debug, Default, Clone; X: Cx)]
 struct NestedGoals<X: Cx> {
     nested_goals: HashMap<X::Input, PathsToNested>,
@@ -450,6 +448,43 @@ struct ProvisionalCacheEntry<X: Cx> {
     result: X::Result,
 }
 
+/// The final result of evaluating a goal.
+///
+/// We reset `encountered_overflow` when reevaluating a goal,
+/// but need to track whether we've hit the recursion limit at
+/// all for correctness.
+///
+/// We've previously simply returned the final `StackEntry` but this
+/// made it easy to accidentally drop information from the previous
+/// evaluation.
+#[derive_where(Debug; X: Cx)]
+struct EvaluationResult<X: Cx> {
+    encountered_overflow: bool,
+    required_depth: usize,
+    heads: CycleHeads,
+    nested_goals: NestedGoals<X>,
+    result: X::Result,
+}
+
+impl<X: Cx> EvaluationResult<X> {
+    fn finalize(
+        final_entry: StackEntry<X>,
+        encountered_overflow: bool,
+        result: X::Result,
+    ) -> EvaluationResult<X> {
+        EvaluationResult {
+            encountered_overflow,
+            // Unlike `encountered_overflow`, we share `heads`, `required_depth`,
+            // and `nested_goals` between evaluations.
+            required_depth: final_entry.required_depth,
+            heads: final_entry.heads,
+            nested_goals: final_entry.nested_goals,
+            // We only care about the final result.
+            result,
+        }
+    }
+}
+
 pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
     root_depth: AvailableDepth,
     /// The stack of goals currently being computed.
@@ -562,7 +597,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         input: X::Input,
         step_kind_from_parent: PathKind,
         inspect: &mut D::ProofTreeBuilder,
-        mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
+        evaluate_goal: impl Fn(&mut Self, X, X::Input, &mut D::ProofTreeBuilder) -> X::Result + Copy,
     ) -> X::Result {
         let Some(available_depth) =
             AvailableDepth::allowed_depth_for_nested::<D>(self.root_depth, &self.stack)
@@ -616,12 +651,12 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
             input,
             step_kind_from_parent,
             available_depth,
+            provisional_result: None,
             required_depth: 0,
             heads: Default::default(),
             encountered_overflow: false,
             has_been_used: None,
             nested_goals: Default::default(),
-            provisional_result: None,
         });
 
         // This is for global caching, so we properly track query dependencies.
@@ -630,35 +665,41 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         // not tracked by the cache key and from outside of this anon task, it
         // must not be added to the global cache. Notably, this is the case for
         // trait solver cycles participants.
-        let ((final_entry, result), dep_node) = cx.with_cached_task(|| {
-            self.evaluate_goal_in_task(cx, input, inspect, &mut evaluate_goal)
-        });
+        let (evaluation_result, dep_node) =
+            cx.with_cached_task(|| self.evaluate_goal_in_task(cx, input, inspect, evaluate_goal));
 
         // We've finished computing the goal and have popped it from the stack,
         // lazily update its parent goal.
         Self::update_parent_goal(
             &mut self.stack,
-            final_entry.step_kind_from_parent,
-            final_entry.required_depth,
-            &final_entry.heads,
-            final_entry.encountered_overflow,
-            UpdateParentGoalCtxt::Ordinary(&final_entry.nested_goals),
+            step_kind_from_parent,
+            evaluation_result.required_depth,
+            &evaluation_result.heads,
+            evaluation_result.encountered_overflow,
+            UpdateParentGoalCtxt::Ordinary(&evaluation_result.nested_goals),
         );
+        let result = evaluation_result.result;
 
         // We're now done with this goal. We only add the root of cycles to the global cache.
         // In case this goal is involved in a larger cycle add it to the provisional cache.
-        if final_entry.heads.is_empty() {
+        if evaluation_result.heads.is_empty() {
             if let Some((_scope, expected)) = validate_cache {
                 // Do not try to move a goal into the cache again if we're testing
                 // the global cache.
-                assert_eq!(result, expected, "input={input:?}");
+                assert_eq!(evaluation_result.result, expected, "input={input:?}");
             } else if D::inspect_is_noop(inspect) {
-                self.insert_global_cache(cx, final_entry, result, dep_node)
+                self.insert_global_cache(cx, input, evaluation_result, dep_node)
             }
         } else if D::ENABLE_PROVISIONAL_CACHE {
             debug_assert!(validate_cache.is_none(), "unexpected non-root: {input:?}");
             let entry = self.provisional_cache.entry(input).or_default();
-            let StackEntry { heads, encountered_overflow, .. } = final_entry;
+            let EvaluationResult {
+                encountered_overflow,
+                required_depth: _,
+                heads,
+                nested_goals: _,
+                result,
+            } = evaluation_result;
             let path_from_head = Self::cycle_path_kind(
                 &self.stack,
                 step_kind_from_parent,
@@ -1023,19 +1064,25 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         cx: X,
         input: X::Input,
         inspect: &mut D::ProofTreeBuilder,
-        mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
-    ) -> (StackEntry<X>, X::Result) {
+        evaluate_goal: impl Fn(&mut Self, X, X::Input, &mut D::ProofTreeBuilder) -> X::Result + Copy,
+    ) -> EvaluationResult<X> {
+        // We reset `encountered_overflow` each time we rerun this goal
+        // but need to make sure we currently propagate it to the global
+        // cache even if only some of the evaluations actually reach the
+        // recursion limit.
+        let mut encountered_overflow = false;
         let mut i = 0;
         loop {
-            let result = evaluate_goal(self, inspect);
+            let result = evaluate_goal(self, cx, input, inspect);
             let stack_entry = self.stack.pop();
+            encountered_overflow |= stack_entry.encountered_overflow;
             debug_assert_eq!(stack_entry.input, input);
 
             // If the current goal is not the root of a cycle, we are done.
             //
             // There are no provisional cache entries which depend on this goal.
             let Some(usage_kind) = stack_entry.has_been_used else {
-                return (stack_entry, result);
+                return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
             };
 
             // If it is a cycle head, we have to keep trying to prove it until
@@ -1051,7 +1098,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
             // final result is equal to the initial response for that case.
             if self.reached_fixpoint(cx, &stack_entry, usage_kind, result) {
                 self.rebase_provisional_cache_entries(&stack_entry, |_, result| result);
-                return (stack_entry, result);
+                return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
             }
 
             // If computing this goal results in ambiguity with no constraints,
@@ -1070,7 +1117,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                 self.rebase_provisional_cache_entries(&stack_entry, |input, _| {
                     D::propagate_ambiguity(cx, input, result)
                 });
-                return (stack_entry, result);
+                return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
             };
 
             // If we've reached the fixpoint step limit, we bail with overflow and taint all
@@ -1082,7 +1129,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                 self.rebase_provisional_cache_entries(&stack_entry, |input, _| {
                     D::on_fixpoint_overflow(cx, input)
                 });
-                return (stack_entry, result);
+                return EvaluationResult::finalize(stack_entry, encountered_overflow, result);
             }
 
             // Clear all provisional cache entries which depend on a previous provisional
@@ -1091,9 +1138,22 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
 
             debug!(?result, "fixpoint changed provisional results");
             self.stack.push(StackEntry {
-                has_been_used: None,
+                input,
+                step_kind_from_parent: stack_entry.step_kind_from_parent,
+                available_depth: stack_entry.available_depth,
                 provisional_result: Some(result),
-                ..stack_entry
+                // We can keep these goals from previous iterations as they are only
+                // ever read after finalizing this evaluation.
+                required_depth: stack_entry.required_depth,
+                heads: stack_entry.heads,
+                nested_goals: stack_entry.nested_goals,
+                // We reset these two fields when rerunning this goal. We could
+                // keep `encountered_overflow` as it's only used as a performance
+                // optimization. However, given that the proof tree will likely look
+                // similar to the previous iterations when reevaluating, it's better
+                // for caching if the reevaluation also starts out with `false`.
+                encountered_overflow: false,
+                has_been_used: None,
             });
         }
     }
@@ -1109,21 +1169,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
     fn insert_global_cache(
         &mut self,
         cx: X,
-        final_entry: StackEntry<X>,
-        result: X::Result,
+        input: X::Input,
+        evaluation_result: EvaluationResult<X>,
         dep_node: X::DepNodeIndex,
     ) {
-        debug!(?final_entry, ?result, "insert global cache");
-        cx.with_global_cache(|cache| {
-            cache.insert(
-                cx,
-                final_entry.input,
-                result,
-                dep_node,
-                final_entry.required_depth,
-                final_entry.encountered_overflow,
-                final_entry.nested_goals,
-            )
-        })
+        debug!(?evaluation_result, "insert global cache");
+        cx.with_global_cache(|cache| cache.insert(cx, input, evaluation_result, dep_node))
     }
 }
diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs
index 8bb247bf055..e0fd934df69 100644
--- a/compiler/rustc_type_ir/src/search_graph/stack.rs
+++ b/compiler/rustc_type_ir/src/search_graph/stack.rs
@@ -26,6 +26,10 @@ pub(super) struct StackEntry<X: Cx> {
     /// The available depth of a given goal, immutable.
     pub available_depth: AvailableDepth,
 
+    /// Starts out as `None` and gets set when rerunning this
+    /// goal in case we encounter a cycle.
+    pub provisional_result: Option<X::Result>,
+
     /// The maximum depth required while evaluating this goal.
     pub required_depth: usize,
 
@@ -42,10 +46,6 @@ pub(super) struct StackEntry<X: Cx> {
 
     /// The nested goals of this goal, see the doc comment of the type.
     pub nested_goals: NestedGoals<X>,
-
-    /// Starts out as `None` and gets set when rerunning this
-    /// goal in case we encounter a cycle.
-    pub provisional_result: Option<X::Result>,
 }
 
 #[derive_where(Default; X: Cx)]
diff --git a/library/Cargo.lock b/library/Cargo.lock
index 1bd97e7b527..c681c5935df 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -141,9 +141,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.172"
+version = "0.2.174"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
 dependencies = [
  "rustc-std-workspace-core",
 ]
@@ -219,21 +219,19 @@ dependencies = [
 
 [[package]]
 name = "r-efi"
-version = "5.2.0"
+version = "5.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
 [[package]]
 name = "r-efi-alloc"
-version = "2.0.0"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e43c53ff1a01d423d1cb762fd991de07d32965ff0ca2e4f80444ac7804198203"
+checksum = "dc2f58ef3ca9bb0f9c44d9aa8537601bcd3df94cc9314a40178cadf7d4466354"
 dependencies = [
- "compiler_builtins",
  "r-efi",
  "rustc-std-workspace-core",
 ]
@@ -273,10 +271,11 @@ dependencies = [
 
 [[package]]
 name = "rustc-literal-escaper"
-version = "0.0.2"
+version = "0.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
+checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b"
 dependencies = [
+ "rustc-std-workspace-core",
  "rustc-std-workspace-std",
 ]
 
diff --git a/library/Cargo.toml b/library/Cargo.toml
index 35480b9319d..a79c17fc4f7 100644
--- a/library/Cargo.toml
+++ b/library/Cargo.toml
@@ -1,3 +1,5 @@
+cargo-features = ["profile-rustflags"]
+
 [workspace]
 resolver = "1"
 members = [
@@ -44,9 +46,16 @@ object.debug = 0
 rustc-demangle.debug = 0
 rustc-demangle.opt-level = "s"
 
+# panic_abort must always be compiled with panic=abort, even when the rest of the
+# sysroot is panic=unwind.
+[profile.dev.package.panic_abort]
+rustflags = ["-Cpanic=abort"]
+
+[profile.release.package.panic_abort]
+rustflags = ["-Cpanic=abort"]
+
 [patch.crates-io]
-# See comments in `library/rustc-std-workspace-core/README.md` for what's going on
-# here
+# See comments in `library/rustc-std-workspace-core/README.md` for what's going on here
 rustc-std-workspace-core = { path = 'rustc-std-workspace-core' }
 rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' }
 rustc-std-workspace-std = { path = 'rustc-std-workspace-std' }
diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs
index 410e67d3fdb..3d6c740e80b 100644
--- a/library/alloc/src/collections/linked_list/tests.rs
+++ b/library/alloc/src/collections/linked_list/tests.rs
@@ -1,4 +1,3 @@
-use std::cell::Cell;
 use std::panic::{AssertUnwindSafe, catch_unwind};
 use std::thread;
 
@@ -6,6 +5,7 @@ use rand::RngCore;
 
 use super::*;
 use crate::testing::crash_test::{CrashTestDummy, Panic};
+use crate::testing::macros::struct_with_counted_drop;
 use crate::vec::Vec;
 
 #[test]
@@ -1010,22 +1010,6 @@ fn extract_if_drop_panic_leak() {
     assert_eq!(d7.dropped(), 1);
 }
 
-macro_rules! struct_with_counted_drop {
-    ($struct_name:ident$(($elt_ty:ty))?, $drop_counter:ident $(=> $drop_stmt:expr)?) => {
-        thread_local! {static $drop_counter: Cell<u32> = Cell::new(0);}
-
-        struct $struct_name$(($elt_ty))?;
-
-        impl Drop for $struct_name {
-            fn drop(&mut self) {
-                $drop_counter.set($drop_counter.get() + 1);
-
-                $($drop_stmt(self))?
-            }
-        }
-    };
-}
-
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn extract_if_pred_panic_leak() {
diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs
index c90679f1797..ad76cb14deb 100644
--- a/library/alloc/src/collections/vec_deque/tests.rs
+++ b/library/alloc/src/collections/vec_deque/tests.rs
@@ -1,9 +1,7 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
 use core::iter::TrustedLen;
 
 use super::*;
+use crate::testing::macros::struct_with_counted_drop;
 
 #[bench]
 fn bench_push_back_100(b: &mut test::Bencher) {
@@ -1086,36 +1084,24 @@ fn test_clone_from() {
 
 #[test]
 fn test_vec_deque_truncate_drop() {
-    static mut DROPS: u32 = 0;
-    #[derive(Clone)]
-    struct Elem(#[allow(dead_code)] i32);
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem, DROPS);
 
-    let v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)];
-    for push_front in 0..=v.len() {
-        let v = v.clone();
-        let mut tester = VecDeque::with_capacity(5);
-        for (index, elem) in v.into_iter().enumerate() {
+    const LEN: usize = 5;
+    for push_front in 0..=LEN {
+        let mut tester = VecDeque::with_capacity(LEN);
+        for index in 0..LEN {
             if index < push_front {
-                tester.push_front(elem);
+                tester.push_front(Elem);
             } else {
-                tester.push_back(elem);
+                tester.push_back(Elem);
             }
         }
-        assert_eq!(unsafe { DROPS }, 0);
+        assert_eq!(DROPS.get(), 0);
         tester.truncate(3);
-        assert_eq!(unsafe { DROPS }, 2);
+        assert_eq!(DROPS.get(), 2);
         tester.truncate(0);
-        assert_eq!(unsafe { DROPS }, 5);
-        unsafe {
-            DROPS = 0;
-        }
+        assert_eq!(DROPS.get(), 5);
+        DROPS.set(0);
     }
 }
 
diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs
index 48849bf7536..93bdad75380 100644
--- a/library/alloc/src/ffi/c_str.rs
+++ b/library/alloc/src/ffi/c_str.rs
@@ -1099,6 +1099,46 @@ impl From<&CStr> for CString {
     }
 }
 
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<CStr> for CString {
+    #[inline]
+    fn eq(&self, other: &CStr) -> bool {
+        **self == *other
+    }
+
+    #[inline]
+    fn ne(&self, other: &CStr) -> bool {
+        **self != *other
+    }
+}
+
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<&CStr> for CString {
+    #[inline]
+    fn eq(&self, other: &&CStr) -> bool {
+        **self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &&CStr) -> bool {
+        **self != **other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<Cow<'_, CStr>> for CString {
+    #[inline]
+    fn eq(&self, other: &Cow<'_, CStr>) -> bool {
+        **self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &Cow<'_, CStr>) -> bool {
+        **self != **other
+    }
+}
+
 #[stable(feature = "cstring_asref", since = "1.7.0")]
 impl ops::Index<ops::RangeFull> for CString {
     type Output = CStr;
@@ -1181,6 +1221,75 @@ impl CStr {
     }
 }
 
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<CString> for CStr {
+    #[inline]
+    fn eq(&self, other: &CString) -> bool {
+        *self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &CString) -> bool {
+        *self != **other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<Cow<'_, Self>> for CStr {
+    #[inline]
+    fn eq(&self, other: &Cow<'_, Self>) -> bool {
+        *self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &Cow<'_, Self>) -> bool {
+        *self != **other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<CStr> for Cow<'_, CStr> {
+    #[inline]
+    fn eq(&self, other: &CStr) -> bool {
+        **self == *other
+    }
+
+    #[inline]
+    fn ne(&self, other: &CStr) -> bool {
+        **self != *other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<&CStr> for Cow<'_, CStr> {
+    #[inline]
+    fn eq(&self, other: &&CStr) -> bool {
+        **self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &&CStr) -> bool {
+        **self != **other
+    }
+}
+
+#[cfg(not(no_global_oom_handling))]
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<CString> for Cow<'_, CStr> {
+    #[inline]
+    fn eq(&self, other: &CString) -> bool {
+        **self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &CString) -> bool {
+        **self != **other
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl core::error::Error for NulError {
     #[allow(deprecated)]
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index 7c71594c430..5f69f699867 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -1105,6 +1105,7 @@ impl String {
     /// ```
     #[cfg(not(no_global_oom_handling))]
     #[inline]
+    #[track_caller]
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_confusables("append", "push")]
     #[rustc_diagnostic_item = "string_push_str"]
@@ -1135,6 +1136,7 @@ impl String {
     /// ```
     #[cfg(not(no_global_oom_handling))]
     #[stable(feature = "string_extend_from_within", since = "1.87.0")]
+    #[track_caller]
     pub fn extend_from_within<R>(&mut self, src: R)
     where
         R: RangeBounds<usize>,
@@ -1206,6 +1208,7 @@ impl String {
     /// ```
     #[cfg(not(no_global_oom_handling))]
     #[inline]
+    #[track_caller]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn reserve(&mut self, additional: usize) {
         self.vec.reserve(additional)
@@ -1257,6 +1260,7 @@ impl String {
     #[cfg(not(no_global_oom_handling))]
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[track_caller]
     pub fn reserve_exact(&mut self, additional: usize) {
         self.vec.reserve_exact(additional)
     }
@@ -1352,6 +1356,7 @@ impl String {
     /// ```
     #[cfg(not(no_global_oom_handling))]
     #[inline]
+    #[track_caller]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn shrink_to_fit(&mut self) {
         self.vec.shrink_to_fit()
@@ -1379,6 +1384,7 @@ impl String {
     /// ```
     #[cfg(not(no_global_oom_handling))]
     #[inline]
+    #[track_caller]
     #[stable(feature = "shrink_to", since = "1.56.0")]
     pub fn shrink_to(&mut self, min_capacity: usize) {
         self.vec.shrink_to(min_capacity)
@@ -1400,6 +1406,7 @@ impl String {
     #[cfg(not(no_global_oom_handling))]
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[track_caller]
     pub fn push(&mut self, ch: char) {
         let len = self.len();
         let ch_len = ch.len_utf8();
@@ -1456,6 +1463,7 @@ impl String {
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[track_caller]
     pub fn truncate(&mut self, new_len: usize) {
         if new_len <= self.len() {
             assert!(self.is_char_boundary(new_len));
@@ -1511,6 +1519,7 @@ impl String {
     /// ```
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[track_caller]
     #[rustc_confusables("delete", "take")]
     pub fn remove(&mut self, idx: usize) -> char {
         let ch = match self[idx..].chars().next() {
@@ -1705,6 +1714,7 @@ impl String {
     /// ```
     #[cfg(not(no_global_oom_handling))]
     #[inline]
+    #[track_caller]
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_confusables("set")]
     pub fn insert(&mut self, idx: usize, ch: char) {
@@ -1761,6 +1771,7 @@ impl String {
     /// ```
     #[cfg(not(no_global_oom_handling))]
     #[inline]
+    #[track_caller]
     #[stable(feature = "insert_str", since = "1.16.0")]
     #[rustc_diagnostic_item = "string_insert_str"]
     pub fn insert_str(&mut self, idx: usize, string: &str) {
@@ -1889,6 +1900,7 @@ impl String {
     /// ```
     #[cfg(not(no_global_oom_handling))]
     #[inline]
+    #[track_caller]
     #[stable(feature = "string_split_off", since = "1.16.0")]
     #[must_use = "use `.truncate()` if you don't need the other half"]
     pub fn split_off(&mut self, at: usize) -> String {
@@ -1953,6 +1965,7 @@ impl String {
     /// assert_eq!(s, "");
     /// ```
     #[stable(feature = "drain", since = "1.6.0")]
+    #[track_caller]
     pub fn drain<R>(&mut self, range: R) -> Drain<'_>
     where
         R: RangeBounds<usize>,
@@ -2052,6 +2065,7 @@ impl String {
     /// ```
     #[cfg(not(no_global_oom_handling))]
     #[stable(feature = "splice", since = "1.27.0")]
+    #[track_caller]
     pub fn replace_range<R>(&mut self, range: R, replace_with: &str)
     where
         R: RangeBounds<usize>,
@@ -2101,6 +2115,7 @@ impl String {
     #[stable(feature = "box_str", since = "1.4.0")]
     #[must_use = "`self` will be dropped if the result is not used"]
     #[inline]
+    #[track_caller]
     pub fn into_boxed_str(self) -> Box<str> {
         let slice = self.vec.into_boxed_slice();
         unsafe { from_boxed_utf8_unchecked(slice) }
@@ -2288,6 +2303,7 @@ impl Error for FromUtf16Error {
 #[cfg(not(no_global_oom_handling))]
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Clone for String {
+    #[track_caller]
     fn clone(&self) -> Self {
         String { vec: self.vec.clone() }
     }
@@ -2296,6 +2312,7 @@ impl Clone for String {
     ///
     /// This method is preferred over simply assigning `source.clone()` to `self`,
     /// as it avoids reallocation if possible.
+    #[track_caller]
     fn clone_from(&mut self, source: &Self) {
         self.vec.clone_from(&source.vec);
     }
@@ -2469,11 +2486,14 @@ impl<'a> Extend<Cow<'a, str>> for String {
 #[cfg(not(no_global_oom_handling))]
 #[unstable(feature = "ascii_char", issue = "110998")]
 impl Extend<core::ascii::Char> for String {
+    #[inline]
+    #[track_caller]
     fn extend<I: IntoIterator<Item = core::ascii::Char>>(&mut self, iter: I) {
         self.vec.extend(iter.into_iter().map(|c| c.to_u8()));
     }
 
     #[inline]
+    #[track_caller]
     fn extend_one(&mut self, c: core::ascii::Char) {
         self.vec.push(c.to_u8());
     }
@@ -2482,11 +2502,14 @@ impl Extend<core::ascii::Char> for String {
 #[cfg(not(no_global_oom_handling))]
 #[unstable(feature = "ascii_char", issue = "110998")]
 impl<'a> Extend<&'a core::ascii::Char> for String {
+    #[inline]
+    #[track_caller]
     fn extend<I: IntoIterator<Item = &'a core::ascii::Char>>(&mut self, iter: I) {
         self.extend(iter.into_iter().cloned());
     }
 
     #[inline]
+    #[track_caller]
     fn extend_one(&mut self, c: &'a core::ascii::Char) {
         self.vec.push(c.to_u8());
     }
@@ -2841,7 +2864,7 @@ macro_rules! impl_to_string {
         impl SpecToString for $signed {
             #[inline]
             fn spec_to_string(&self) -> String {
-                const SIZE: usize = $signed::MAX.ilog(10) as usize + 1;
+                const SIZE: usize = $signed::MAX.ilog10() as usize + 1;
                 let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); SIZE];
                 // Only difference between signed and unsigned are these 8 lines.
                 let mut out;
@@ -2861,7 +2884,7 @@ macro_rules! impl_to_string {
         impl SpecToString for $unsigned {
             #[inline]
             fn spec_to_string(&self) -> String {
-                const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1;
+                const SIZE: usize = $unsigned::MAX.ilog10() as usize + 1;
                 let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); SIZE];
 
                 self._fmt(&mut buf).to_string()
@@ -2877,6 +2900,7 @@ impl_to_string! {
     i32, u32,
     i64, u64,
     isize, usize,
+    i128, u128,
 }
 
 #[cfg(not(no_global_oom_handling))]
diff --git a/library/alloctests/testing/macros.rs b/library/alloctests/testing/macros.rs
new file mode 100644
index 00000000000..2433e53ca89
--- /dev/null
+++ b/library/alloctests/testing/macros.rs
@@ -0,0 +1,37 @@
+macro_rules! struct_with_counted_drop {
+    ($struct_name:ident $(( $( $elt_ty:ty ),+ ))?, $drop_counter:ident $( => $drop_stmt:expr )? ) => {
+        thread_local! {static $drop_counter: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);}
+
+        #[derive(Clone, Debug, PartialEq)]
+        struct $struct_name $(( $( $elt_ty ),+ ))?;
+
+        impl ::std::ops::Drop for $struct_name {
+            fn drop(&mut self) {
+                $drop_counter.set($drop_counter.get() + 1);
+
+                $($drop_stmt(self))?
+            }
+        }
+    };
+    ($struct_name:ident $(( $( $elt_ty:ty ),+ ))?, $drop_counter:ident[ $drop_key:expr,$key_ty:ty ] $( => $drop_stmt:expr )? ) => {
+        thread_local! {
+            static $drop_counter: ::core::cell::RefCell<::std::collections::HashMap<$key_ty, u32>> =
+                ::core::cell::RefCell::new(::std::collections::HashMap::new());
+        }
+
+        #[derive(Clone, Debug, PartialEq)]
+        struct $struct_name $(( $( $elt_ty ),+ ))?;
+
+        impl ::std::ops::Drop for $struct_name {
+            fn drop(&mut self) {
+                $drop_counter.with_borrow_mut(|counter| {
+                    *counter.entry($drop_key(self)).or_default() += 1;
+                });
+
+                $($drop_stmt(self))?
+            }
+        }
+    };
+}
+
+pub(crate) use struct_with_counted_drop;
diff --git a/library/alloctests/testing/mod.rs b/library/alloctests/testing/mod.rs
index c8457daf93e..66a4f6682b9 100644
--- a/library/alloctests/testing/mod.rs
+++ b/library/alloctests/testing/mod.rs
@@ -1,3 +1,4 @@
 pub(crate) mod crash_test;
+pub(crate) mod macros;
 pub(crate) mod ord_chaos;
 pub(crate) mod rng;
diff --git a/library/alloctests/tests/fmt.rs b/library/alloctests/tests/fmt.rs
index dbcf0c3a114..0989a56b554 100644
--- a/library/alloctests/tests/fmt.rs
+++ b/library/alloctests/tests/fmt.rs
@@ -1,6 +1,4 @@
 #![deny(warnings)]
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
 #![allow(unnecessary_transmutes)]
 
 use std::cell::RefCell;
@@ -285,19 +283,32 @@ fn test_format_args() {
     t!(s, "args were: hello world");
 }
 
+macro_rules! counter_fn {
+    ($name:ident) => {
+        fn $name() -> u32 {
+            thread_local! {static COUNTER: ::core::cell::Cell<u32> = ::core::cell::Cell::new(0);}
+
+            COUNTER.set(COUNTER.get() + 1);
+            COUNTER.get()
+        }
+    };
+}
+
 #[test]
 fn test_order() {
-    // Make sure format!() arguments are always evaluated in a left-to-right
-    // ordering
-    fn foo() -> isize {
-        static mut FOO: isize = 0;
-        unsafe {
-            FOO += 1;
-            FOO
-        }
-    }
+    // Make sure format!() arguments are always evaluated in a left-to-right ordering
+    counter_fn!(count);
+
     assert_eq!(
-        format!("{} {} {a} {b} {} {c}", foo(), foo(), foo(), a = foo(), b = foo(), c = foo()),
+        format!(
+            "{} {} {a} {b} {} {c}",
+            count(),
+            count(),
+            count(),
+            a = count(),
+            b = count(),
+            c = count()
+        ),
         "1 2 4 5 3 6".to_string()
     );
 }
@@ -306,14 +317,9 @@ fn test_order() {
 fn test_once() {
     // Make sure each argument are evaluated only once even though it may be
     // formatted multiple times
-    fn foo() -> isize {
-        static mut FOO: isize = 0;
-        unsafe {
-            FOO += 1;
-            FOO
-        }
-    }
-    assert_eq!(format!("{0} {0} {0} {a} {a} {a}", foo(), a = foo()), "1 1 1 2 2 2".to_string());
+    counter_fn!(count);
+
+    assert_eq!(format!("{0} {0} {0} {a} {a} {a}", count(), a = count()), "1 1 1 2 2 2".to_string());
 }
 
 #[test]
diff --git a/library/alloctests/tests/num.rs b/library/alloctests/tests/num.rs
index 3c76e68c606..a169bbec8e0 100644
--- a/library/alloctests/tests/num.rs
+++ b/library/alloctests/tests/num.rs
@@ -52,6 +52,8 @@ int_to_s!(
     i32,
     test_i64_to_string,
     i64,
+    test_isize_to_string,
+    isize,
     test_i128_to_string,
     i128,
 );
@@ -64,6 +66,8 @@ uint_to_s!(
     u32,
     test_u64_to_string,
     u64,
+    test_usize_to_string,
+    usize,
     test_u128_to_string,
     u128,
 );
diff --git a/library/alloctests/tests/testing/mod.rs b/library/alloctests/tests/testing/mod.rs
index 30275a5125e..08856e39ded 100644
--- a/library/alloctests/tests/testing/mod.rs
+++ b/library/alloctests/tests/testing/mod.rs
@@ -1,2 +1,4 @@
 #[path = "../../testing/crash_test.rs"]
 pub mod crash_test;
+#[path = "../../testing/macros.rs"]
+pub mod macros;
diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs
index 51b49b8edb3..00f640cd17e 100644
--- a/library/alloctests/tests/vec.rs
+++ b/library/alloctests/tests/vec.rs
@@ -1,6 +1,3 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
 use core::alloc::{Allocator, Layout};
 use core::num::NonZero;
 use core::ptr::NonNull;
@@ -20,6 +17,8 @@ use std::rc::Rc;
 use std::sync::atomic::{AtomicU32, Ordering};
 use std::vec::{Drain, IntoIter};
 
+use crate::testing::macros::struct_with_counted_drop;
+
 struct DropCounter<'a> {
     count: &'a mut u32,
 }
@@ -548,32 +547,25 @@ fn test_cmp() {
 
 #[test]
 fn test_vec_truncate_drop() {
-    static mut DROPS: u32 = 0;
-    struct Elem(#[allow(dead_code)] i32);
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem(i32), DROPS);
 
     let mut v = vec![Elem(1), Elem(2), Elem(3), Elem(4), Elem(5)];
-    assert_eq!(unsafe { DROPS }, 0);
+
+    assert_eq!(DROPS.get(), 0);
     v.truncate(3);
-    assert_eq!(unsafe { DROPS }, 2);
+    assert_eq!(DROPS.get(), 2);
     v.truncate(0);
-    assert_eq!(unsafe { DROPS }, 5);
+    assert_eq!(DROPS.get(), 5);
 }
 
 #[test]
 #[should_panic]
 fn test_vec_truncate_fail() {
     struct BadElem(i32);
+
     impl Drop for BadElem {
         fn drop(&mut self) {
-            let BadElem(ref mut x) = *self;
-            if *x == 0xbadbeef {
+            if let BadElem(0xbadbeef) = self {
                 panic!("BadElem panic: 0xbadbeef")
             }
         }
@@ -812,22 +804,7 @@ fn test_drain_end_overflow() {
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn test_drain_leak() {
-    static mut DROPS: i32 = 0;
-
-    #[derive(Debug, PartialEq)]
-    struct D(u32, bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.1 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(u32, bool), DROPS => |this: &D| if this.1 { panic!("panic in `drop`"); });
 
     let mut v = vec![
         D(0, false),
@@ -844,7 +821,7 @@ fn test_drain_leak() {
     }))
     .ok();
 
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
     assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]);
 }
 
@@ -1057,27 +1034,13 @@ fn test_into_iter_clone() {
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn test_into_iter_leak() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); });
 
     let v = vec![D(false), D(true), D(false)];
 
     catch_unwind(move || drop(v.into_iter())).ok();
 
-    assert_eq!(unsafe { DROPS }, 3);
+    assert_eq!(DROPS.get(), 3);
 }
 
 #[test]
@@ -1274,55 +1237,31 @@ fn test_from_iter_specialization_panic_during_iteration_drops() {
 
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#[allow(static_mut_refs)]
 fn test_from_iter_specialization_panic_during_drop_doesnt_leak() {
-    static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5];
-    static mut DROP_COUNTER_NEW: [usize; 2] = [0; 2];
-
-    #[derive(Debug)]
-    struct Old(usize);
-
-    impl Drop for Old {
-        fn drop(&mut self) {
-            unsafe {
-                DROP_COUNTER_OLD[self.0] += 1;
-            }
-
-            if self.0 == 3 {
-                panic!();
-            }
-
-            println!("Dropped Old: {}", self.0);
-        }
-    }
-
-    #[derive(Debug)]
-    struct New(usize);
-
-    impl Drop for New {
-        fn drop(&mut self) {
-            unsafe {
-                DROP_COUNTER_NEW[self.0] += 1;
+    struct_with_counted_drop!(
+        Old(usize), DROP_COUNTER_OLD[|this: &Old| this.0, usize] =>
+            |this: &Old| {
+                if this.0 == 3 { panic!(); } println!("Dropped Old: {}", this.0)
             }
-
-            println!("Dropped New: {}", self.0);
-        }
-    }
+    );
+    struct_with_counted_drop!(
+        New(usize), DROP_COUNTER_NEW[|this: &New| this.0, usize] =>
+            |this: &New| println!("Dropped New: {}", this.0)
+    );
 
     let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
         let v = vec![Old(0), Old(1), Old(2), Old(3), Old(4)];
         let _ = v.into_iter().map(|x| New(x.0)).take(2).collect::<Vec<_>>();
     }));
 
-    assert_eq!(unsafe { DROP_COUNTER_OLD[0] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_OLD[1] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_OLD[2] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_OLD[3] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_OLD[4] }, 1);
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&0), Some(&1)));
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&1), Some(&1)));
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&2), Some(&1)));
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&3), Some(&1)));
+    DROP_COUNTER_OLD.with_borrow(|c| assert_eq!(c.get(&4), Some(&1)));
 
-    assert_eq!(unsafe { DROP_COUNTER_NEW[0] }, 1);
-    assert_eq!(unsafe { DROP_COUNTER_NEW[1] }, 1);
+    DROP_COUNTER_NEW.with_borrow(|c| assert_eq!(c.get(&0), Some(&1)));
+    DROP_COUNTER_NEW.with_borrow(|c| assert_eq!(c.get(&1), Some(&1)));
 }
 
 // regression test for issue #85322. Peekable previously implemented InPlaceIterable,
diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs
index b77ea3a312b..a82906d55e5 100644
--- a/library/alloctests/tests/vec_deque.rs
+++ b/library/alloctests/tests/vec_deque.rs
@@ -1,6 +1,3 @@
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#![allow(static_mut_refs)]
-
 use core::num::NonZero;
 use std::assert_matches::assert_matches;
 use std::collections::TryReserveErrorKind::*;
@@ -14,6 +11,7 @@ use Taggy::*;
 use Taggypar::*;
 
 use crate::hash;
+use crate::testing::macros::struct_with_counted_drop;
 
 #[test]
 fn test_simple() {
@@ -719,15 +717,7 @@ fn test_show() {
 
 #[test]
 fn test_drop() {
-    static mut DROPS: i32 = 0;
-    struct Elem;
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem, DROPS);
 
     let mut ring = VecDeque::new();
     ring.push_back(Elem);
@@ -736,20 +726,12 @@ fn test_drop() {
     ring.push_front(Elem);
     drop(ring);
 
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
 }
 
 #[test]
 fn test_drop_with_pop() {
-    static mut DROPS: i32 = 0;
-    struct Elem;
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem, DROPS);
 
     let mut ring = VecDeque::new();
     ring.push_back(Elem);
@@ -759,23 +741,15 @@ fn test_drop_with_pop() {
 
     drop(ring.pop_back());
     drop(ring.pop_front());
-    assert_eq!(unsafe { DROPS }, 2);
+    assert_eq!(DROPS.get(), 2);
 
     drop(ring);
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
 }
 
 #[test]
 fn test_drop_clear() {
-    static mut DROPS: i32 = 0;
-    struct Elem;
-    impl Drop for Elem {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-        }
-    }
+    struct_with_counted_drop!(Elem, DROPS);
 
     let mut ring = VecDeque::new();
     ring.push_back(Elem);
@@ -783,30 +757,16 @@ fn test_drop_clear() {
     ring.push_back(Elem);
     ring.push_front(Elem);
     ring.clear();
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
 
     drop(ring);
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
 }
 
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn test_drop_panic() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
 
     let mut q = VecDeque::new();
     q.push_back(D(false));
@@ -820,7 +780,7 @@ fn test_drop_panic() {
 
     catch_unwind(move || drop(q)).ok();
 
-    assert_eq!(unsafe { DROPS }, 8);
+    assert_eq!(DROPS.get(), 8);
 }
 
 #[test]
@@ -1655,21 +1615,7 @@ fn test_try_rfold_moves_iter() {
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn truncate_leak() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
 
     let mut q = VecDeque::new();
     q.push_back(D(false));
@@ -1683,27 +1629,13 @@ fn truncate_leak() {
 
     catch_unwind(AssertUnwindSafe(|| q.truncate(1))).ok();
 
-    assert_eq!(unsafe { DROPS }, 7);
+    assert_eq!(DROPS.get(), 7);
 }
 
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn truncate_front_leak() {
-    static mut DROPS: i32 = 0;
-
-    struct D(bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.0 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } );
 
     let mut q = VecDeque::new();
     q.push_back(D(false));
@@ -1717,28 +1649,13 @@ fn truncate_front_leak() {
 
     catch_unwind(AssertUnwindSafe(|| q.truncate_front(1))).ok();
 
-    assert_eq!(unsafe { DROPS }, 7);
+    assert_eq!(DROPS.get(), 7);
 }
 
 #[test]
 #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
 fn test_drain_leak() {
-    static mut DROPS: i32 = 0;
-
-    #[derive(Debug, PartialEq)]
-    struct D(u32, bool);
-
-    impl Drop for D {
-        fn drop(&mut self) {
-            unsafe {
-                DROPS += 1;
-            }
-
-            if self.1 {
-                panic!("panic in `drop`");
-            }
-        }
-    }
+    struct_with_counted_drop!(D(u32, bool), DROPS => |this: &D| if this.1 { panic!("panic in `drop`"); } );
 
     let mut v = VecDeque::new();
     v.push_back(D(4, false));
@@ -1754,10 +1671,10 @@ fn test_drain_leak() {
     }))
     .ok();
 
-    assert_eq!(unsafe { DROPS }, 4);
+    assert_eq!(DROPS.get(), 4);
     assert_eq!(v.len(), 3);
     drop(v);
-    assert_eq!(unsafe { DROPS }, 7);
+    assert_eq!(DROPS.get(), 7);
 }
 
 #[test]
diff --git a/library/core/src/ascii.rs b/library/core/src/ascii.rs
index d3c6c046e71..f5e62803043 100644
--- a/library/core/src/ascii.rs
+++ b/library/core/src/ascii.rs
@@ -15,6 +15,7 @@ use crate::iter::FusedIterator;
 use crate::num::NonZero;
 
 mod ascii_char;
+#[doc(alias("AsciiChar"))]
 #[unstable(feature = "ascii_char", issue = "110998")]
 pub use ascii_char::AsciiChar as Char;
 
diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index af2edf141b2..b0752a85faf 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -395,6 +395,7 @@ impl char {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_stable(feature = "const_char_convert", since = "1.67.0")]
+    #[rustc_diagnostic_item = "char_to_digit"]
     #[must_use = "this returns the result of the operation, \
                   without modifying the original"]
     #[inline]
diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs
index 57de507a73e..a34d1b4a064 100644
--- a/library/core/src/clone.rs
+++ b/library/core/src/clone.rs
@@ -36,7 +36,7 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
-use crate::marker::PointeeSized;
+use crate::marker::{Destruct, PointeeSized};
 
 mod uninit;
 
@@ -157,6 +157,8 @@ mod uninit;
 #[lang = "clone"]
 #[rustc_diagnostic_item = "Clone"]
 #[rustc_trivial_field_reads]
+#[rustc_const_unstable(feature = "const_clone", issue = "142757")]
+#[const_trait]
 pub trait Clone: Sized {
     /// Returns a duplicate of the value.
     ///
@@ -208,7 +210,10 @@ pub trait Clone: Sized {
     /// allocations.
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
-    fn clone_from(&mut self, source: &Self) {
+    fn clone_from(&mut self, source: &Self)
+    where
+        Self: ~const Destruct,
+    {
         *self = source.clone()
     }
 }
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index 5cb1a148477..0aa8f47462d 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -247,6 +247,8 @@ use crate::ops::ControlFlow;
     append_const_msg
 )]
 #[rustc_diagnostic_item = "PartialEq"]
+#[const_trait]
+#[rustc_const_unstable(feature = "const_trait_impl", issue = "67792")]
 pub trait PartialEq<Rhs: PointeeSized = Self>: PointeeSized {
     /// Tests for `self` and `other` values to be equal, and is used by `==`.
     #[must_use]
@@ -1811,7 +1813,8 @@ mod impls {
     macro_rules! partial_eq_impl {
         ($($t:ty)*) => ($(
             #[stable(feature = "rust1", since = "1.0.0")]
-            impl PartialEq for $t {
+            #[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
+            impl const PartialEq for $t {
                 #[inline]
                 fn eq(&self, other: &Self) -> bool { *self == *other }
                 #[inline]
@@ -2018,9 +2021,10 @@ mod impls {
     // & pointers
 
     #[stable(feature = "rust1", since = "1.0.0")]
-    impl<A: PointeeSized, B: PointeeSized> PartialEq<&B> for &A
+    #[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
+    impl<A: PointeeSized, B: PointeeSized> const PartialEq<&B> for &A
     where
-        A: PartialEq<B>,
+        A: ~const PartialEq<B>,
     {
         #[inline]
         fn eq(&self, other: &&B) -> bool {
@@ -2089,9 +2093,10 @@ mod impls {
     // &mut pointers
 
     #[stable(feature = "rust1", since = "1.0.0")]
-    impl<A: PointeeSized, B: PointeeSized> PartialEq<&mut B> for &mut A
+    #[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
+    impl<A: PointeeSized, B: PointeeSized> const PartialEq<&mut B> for &mut A
     where
-        A: PartialEq<B>,
+        A: ~const PartialEq<B>,
     {
         #[inline]
         fn eq(&self, other: &&mut B) -> bool {
@@ -2158,9 +2163,10 @@ mod impls {
     impl<A: PointeeSized> Eq for &mut A where A: Eq {}
 
     #[stable(feature = "rust1", since = "1.0.0")]
-    impl<A: PointeeSized, B: PointeeSized> PartialEq<&mut B> for &A
+    #[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
+    impl<A: PointeeSized, B: PointeeSized> const PartialEq<&mut B> for &A
     where
-        A: PartialEq<B>,
+        A: ~const PartialEq<B>,
     {
         #[inline]
         fn eq(&self, other: &&mut B) -> bool {
@@ -2173,9 +2179,10 @@ mod impls {
     }
 
     #[stable(feature = "rust1", since = "1.0.0")]
-    impl<A: PointeeSized, B: PointeeSized> PartialEq<&B> for &mut A
+    #[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
+    impl<A: PointeeSized, B: PointeeSized> const PartialEq<&B> for &mut A
     where
-        A: PartialEq<B>,
+        A: ~const PartialEq<B>,
     {
         #[inline]
         fn eq(&self, other: &&B) -> bool {
diff --git a/library/core/src/default.rs b/library/core/src/default.rs
index 044997a81a9..0a15cedfb55 100644
--- a/library/core/src/default.rs
+++ b/library/core/src/default.rs
@@ -103,7 +103,6 @@ use crate::ascii::Char as AsciiChar;
 /// ```
 #[rustc_diagnostic_item = "Default"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_trivial_field_reads]
 pub trait Default: Sized {
     /// Returns the "default value" for a type.
     ///
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index f7a21072f53..881a7a24083 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -467,7 +467,7 @@ impl CStr {
     /// // 💀 this violates `CStr::from_ptr`'s safety contract
     /// // 💀 leading to a dereference of a dangling pointer,
     /// // 💀 which is immediate undefined behavior.
-    /// // 💀 *BOOM*, you're dead, you're entire program has no meaning.
+    /// // 💀 *BOOM*, you're dead, your entire program has no meaning.
     /// dbg!(unsafe { CStr::from_ptr(ptr) });
     /// ```
     ///
@@ -660,6 +660,19 @@ impl CStr {
     }
 }
 
+#[stable(feature = "c_string_eq_c_str", since = "CURRENT_RUSTC_VERSION")]
+impl PartialEq<&Self> for CStr {
+    #[inline]
+    fn eq(&self, other: &&Self) -> bool {
+        *self == **other
+    }
+
+    #[inline]
+    fn ne(&self, other: &&Self) -> bool {
+        *self != **other
+    }
+}
+
 // `.to_bytes()` representations are compared instead of the inner `[c_char]`s,
 // because `c_char` is `i8` (not `u8`) on some platforms.
 // That is why this is implemented manually and not derived.
@@ -670,6 +683,7 @@ impl PartialOrd for CStr {
         self.to_bytes().partial_cmp(&other.to_bytes())
     }
 }
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Ord for CStr {
     #[inline]
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index c20b3d4817f..2be8d37bbee 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -855,11 +855,13 @@ impl Display for Arguments<'_> {
 #[rustc_on_unimplemented(
     on(
         crate_local,
-        label = "`{Self}` cannot be formatted using `{{:?}}`",
         note = "add `#[derive(Debug)]` to `{Self}` or manually `impl {This} for {Self}`"
     ),
-    message = "`{Self}` doesn't implement `{This}`",
-    label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{This}`"
+    on(
+        from_desugaring = "FormatLiteral",
+        label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{This}`"
+    ),
+    message = "`{Self}` doesn't implement `{This}`"
 )]
 #[doc(alias = "{:?}")]
 #[rustc_diagnostic_item = "Debug"]
@@ -986,11 +988,14 @@ pub use macros::Debug;
         any(Self = "std::path::Path", Self = "std::path::PathBuf"),
         label = "`{Self}` cannot be formatted with the default formatter; call `.display()` on it",
         note = "call `.display()` or `.to_string_lossy()` to safely print paths, \
-                as they may contain non-Unicode data"
+                as they may contain non-Unicode data",
+    ),
+    on(
+        from_desugaring = "FormatLiteral",
+        note = "in format strings you may be able to use `{{:?}}` (or {{:#?}} for pretty-print) instead",
+        label = "`{Self}` cannot be formatted with the default formatter",
     ),
-    message = "`{Self}` doesn't implement `{This}`",
-    label = "`{Self}` cannot be formatted with the default formatter",
-    note = "in format strings you may be able to use `{{:?}}` (or {{:#?}} for pretty-print) instead"
+    message = "`{Self}` doesn't implement `{This}`"
 )]
 #[doc(alias = "{}")]
 #[rustc_diagnostic_item = "Display"]
diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs
index ce878f2da4b..42af595ae41 100644
--- a/library/core/src/fmt/num.rs
+++ b/library/core/src/fmt/num.rs
@@ -208,7 +208,7 @@ macro_rules! impl_Display {
             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                 #[cfg(not(feature = "optimize_for_size"))]
                 {
-                    const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1;
+                    const MAX_DEC_N: usize = $unsigned::MAX.ilog10() as usize + 1;
                     // Buffer decimals for $unsigned with right alignment.
                     let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
 
@@ -226,7 +226,7 @@ macro_rules! impl_Display {
             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                 #[cfg(not(feature = "optimize_for_size"))]
                 {
-                    const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1;
+                    const MAX_DEC_N: usize = $unsigned::MAX.ilog10() as usize + 1;
                     // Buffer decimals for $unsigned with right alignment.
                     let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
 
@@ -323,7 +323,7 @@ macro_rules! impl_Display {
 
         #[cfg(feature = "optimize_for_size")]
         fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1;
+            const MAX_DEC_N: usize = $u::MAX.ilog10() as usize + 1;
             let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
             let mut curr = MAX_DEC_N;
             let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
@@ -565,123 +565,134 @@ mod imp {
 }
 impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
 
+const U128_MAX_DEC_N: usize = u128::MAX.ilog10() as usize + 1;
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for u128 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt_u128(*self, true, f)
+        let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
+
+        f.pad_integral(true, "", self._fmt(&mut buf))
     }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for i128 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt_u128(self.unsigned_abs(), *self >= 0, f)
+        // This is not a typo, we use the maximum number of digits of `u128`, hence why we use
+        // `U128_MAX_DEC_N`.
+        let mut buf = [MaybeUninit::<u8>::uninit(); U128_MAX_DEC_N];
+
+        let is_nonnegative = *self >= 0;
+        f.pad_integral(is_nonnegative, "", self.unsigned_abs()._fmt(&mut buf))
     }
 }
 
-/// Format optimized for u128. Computation of 128 bits is limited by proccessing
-/// in batches of 16 decimals at a time.
-fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-    // Optimize common-case zero, which would also need special treatment due to
-    // its "leading" zero.
-    if n == 0 {
-        return f.pad_integral(true, "", "0");
-    }
+impl u128 {
+    /// Format optimized for u128. Computation of 128 bits is limited by proccessing
+    /// in batches of 16 decimals at a time.
+    #[doc(hidden)]
+    #[unstable(
+        feature = "fmt_internals",
+        reason = "specialized method meant to only be used by `SpecToString` implementation",
+        issue = "none"
+    )]
+    pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit<u8>]) -> &'a str {
+        // Optimize common-case zero, which would also need special treatment due to
+        // its "leading" zero.
+        if self == 0 {
+            return "0";
+        }
 
-    // U128::MAX has 39 significant-decimals.
-    const MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
-    // Buffer decimals with right alignment.
-    let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
-
-    // Take the 16 least-significant decimals.
-    let (quot_1e16, mod_1e16) = div_rem_1e16(n);
-    let (mut remain, mut offset) = if quot_1e16 == 0 {
-        (mod_1e16, MAX_DEC_N)
-    } else {
-        // Write digits at buf[23..39].
-        enc_16lsd::<{ MAX_DEC_N - 16 }>(&mut buf, mod_1e16);
-
-        // Take another 16 decimals.
-        let (quot2, mod2) = div_rem_1e16(quot_1e16);
-        if quot2 == 0 {
-            (mod2, MAX_DEC_N - 16)
+        // Take the 16 least-significant decimals.
+        let (quot_1e16, mod_1e16) = div_rem_1e16(self);
+        let (mut remain, mut offset) = if quot_1e16 == 0 {
+            (mod_1e16, U128_MAX_DEC_N)
         } else {
-            // Write digits at buf[7..23].
-            enc_16lsd::<{ MAX_DEC_N - 32 }>(&mut buf, mod2);
-            // Quot2 has at most 7 decimals remaining after two 1e16 divisions.
-            (quot2 as u64, MAX_DEC_N - 32)
-        }
-    };
+            // Write digits at buf[23..39].
+            enc_16lsd::<{ U128_MAX_DEC_N - 16 }>(buf, mod_1e16);
 
-    // Format per four digits from the lookup table.
-    while remain > 999 {
-        // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
-        // and the while condition ensures at least 4 more decimals.
-        unsafe { core::hint::assert_unchecked(offset >= 4) }
-        // SAFETY: The offset counts down from its initial buf.len()
-        // without underflow due to the previous precondition.
-        unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
-        offset -= 4;
+            // Take another 16 decimals.
+            let (quot2, mod2) = div_rem_1e16(quot_1e16);
+            if quot2 == 0 {
+                (mod2, U128_MAX_DEC_N - 16)
+            } else {
+                // Write digits at buf[7..23].
+                enc_16lsd::<{ U128_MAX_DEC_N - 32 }>(buf, mod2);
+                // Quot2 has at most 7 decimals remaining after two 1e16 divisions.
+                (quot2 as u64, U128_MAX_DEC_N - 32)
+            }
+        };
 
-        // pull two pairs
-        let quad = remain % 1_00_00;
-        remain /= 1_00_00;
-        let pair1 = (quad / 100) as usize;
-        let pair2 = (quad % 100) as usize;
-        buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
-        buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
-        buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
-        buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
-    }
+        // Format per four digits from the lookup table.
+        while remain > 999 {
+            // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
+            // and the while condition ensures at least 4 more decimals.
+            unsafe { core::hint::assert_unchecked(offset >= 4) }
+            // SAFETY: The offset counts down from its initial buf.len()
+            // without underflow due to the previous precondition.
+            unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
+            offset -= 4;
+
+            // pull two pairs
+            let quad = remain % 1_00_00;
+            remain /= 1_00_00;
+            let pair1 = (quad / 100) as usize;
+            let pair2 = (quad % 100) as usize;
+            buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
+            buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
+            buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
+            buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
+        }
 
-    // Format per two digits from the lookup table.
-    if remain > 9 {
-        // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
-        // and the if condition ensures at least 2 more decimals.
-        unsafe { core::hint::assert_unchecked(offset >= 2) }
-        // SAFETY: The offset counts down from its initial buf.len()
-        // without underflow due to the previous precondition.
-        unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
-        offset -= 2;
-
-        let pair = (remain % 100) as usize;
-        remain /= 100;
-        buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
-        buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
-    }
+        // Format per two digits from the lookup table.
+        if remain > 9 {
+            // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
+            // and the if condition ensures at least 2 more decimals.
+            unsafe { core::hint::assert_unchecked(offset >= 2) }
+            // SAFETY: The offset counts down from its initial buf.len()
+            // without underflow due to the previous precondition.
+            unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
+            offset -= 2;
+
+            let pair = (remain % 100) as usize;
+            remain /= 100;
+            buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
+            buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
+        }
 
-    // Format the last remaining digit, if any.
-    if remain != 0 {
-        // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
-        // and the if condition ensures (at least) 1 more decimals.
-        unsafe { core::hint::assert_unchecked(offset >= 1) }
-        // SAFETY: The offset counts down from its initial buf.len()
-        // without underflow due to the previous precondition.
-        unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
-        offset -= 1;
-
-        // Either the compiler sees that remain < 10, or it prevents
-        // a boundary check up next.
-        let last = (remain & 15) as usize;
-        buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
-        // not used: remain = 0;
-    }
+        // Format the last remaining digit, if any.
+        if remain != 0 {
+            // SAFETY: All of the decimals fit in buf due to U128_MAX_DEC_N
+            // and the if condition ensures (at least) 1 more decimals.
+            unsafe { core::hint::assert_unchecked(offset >= 1) }
+            // SAFETY: The offset counts down from its initial buf.len()
+            // without underflow due to the previous precondition.
+            unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
+            offset -= 1;
+
+            // Either the compiler sees that remain < 10, or it prevents
+            // a boundary check up next.
+            let last = (remain & 15) as usize;
+            buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
+            // not used: remain = 0;
+        }
 
-    // SAFETY: All buf content since offset is set.
-    let written = unsafe { buf.get_unchecked(offset..) };
-    // SAFETY: Writes use ASCII from the lookup table exclusively.
-    let as_str = unsafe {
-        str::from_utf8_unchecked(slice::from_raw_parts(
-            MaybeUninit::slice_as_ptr(written),
-            written.len(),
-        ))
-    };
-    f.pad_integral(is_nonnegative, "", as_str)
+        // SAFETY: All buf content since offset is set.
+        let written = unsafe { buf.get_unchecked(offset..) };
+        // SAFETY: Writes use ASCII from the lookup table exclusively.
+        unsafe {
+            str::from_utf8_unchecked(slice::from_raw_parts(
+                MaybeUninit::slice_as_ptr(written),
+                written.len(),
+            ))
+        }
+    }
 }
 
 /// Encodes the 16 least-significant decimals of n into `buf[OFFSET .. OFFSET +
 /// 16 ]`.
-fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>; 39], n: u64) {
+fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], n: u64) {
     // Consume the least-significant decimals from a working copy.
     let mut remain = n;
 
diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs
index 7fd8d3e5b53..fb858a05726 100644
--- a/library/core/src/fmt/rt.rs
+++ b/library/core/src/fmt/rt.rs
@@ -183,38 +183,6 @@ impl Argument<'_> {
             ArgumentType::Placeholder { .. } => None,
         }
     }
-
-    /// Used by `format_args` when all arguments are gone after inlining,
-    /// when using `&[]` would incorrectly allow for a bigger lifetime.
-    ///
-    /// This fails without format argument inlining, and that shouldn't be different
-    /// when the argument is inlined:
-    ///
-    /// ```compile_fail,E0716
-    /// let f = format_args!("{}", "a");
-    /// println!("{f}");
-    /// ```
-    #[inline]
-    pub const fn none() -> [Self; 0] {
-        []
-    }
-}
-
-/// This struct represents the unsafety of constructing an `Arguments`.
-/// It exists, rather than an unsafe function, in order to simplify the expansion
-/// of `format_args!(..)` and reduce the scope of the `unsafe` block.
-#[lang = "format_unsafe_arg"]
-pub struct UnsafeArg {
-    _private: (),
-}
-
-impl UnsafeArg {
-    /// See documentation where `UnsafeArg` is required to know when it is safe to
-    /// create and use `UnsafeArg`.
-    #[inline]
-    pub const unsafe fn new() -> Self {
-        Self { _private: () }
-    }
 }
 
 /// Used by the format_args!() macro to create a fmt::Arguments object.
@@ -248,8 +216,7 @@ impl<'a> Arguments<'a> {
 
     /// Specifies nonstandard formatting parameters.
     ///
-    /// An `rt::UnsafeArg` is required because the following invariants must be held
-    /// in order for this function to be safe:
+    /// SAFETY: the following invariants must be held:
     /// 1. The `pieces` slice must be at least as long as `fmt`.
     /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
     /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
@@ -261,11 +228,10 @@ impl<'a> Arguments<'a> {
     /// const _: () = if false { panic!("a {:1}", "a") };
     /// ```
     #[inline]
-    pub fn new_v1_formatted(
+    pub unsafe fn new_v1_formatted(
         pieces: &'a [&'static str],
         args: &'a [rt::Argument<'a>],
         fmt: &'a [rt::Placeholder],
-        _unsafe_arg: rt::UnsafeArg,
     ) -> Arguments<'a> {
         Arguments { pieces, fmt: Some(fmt), args }
     }
diff --git a/library/core/src/iter/adapters/fuse.rs b/library/core/src/iter/adapters/fuse.rs
index e9765f911a2..0072a95e8df 100644
--- a/library/core/src/iter/adapters/fuse.rs
+++ b/library/core/src/iter/adapters/fuse.rs
@@ -198,8 +198,30 @@ impl<I: Default> Default for Fuse<I> {
     /// let iter: Fuse<slice::Iter<'_, u8>> = Default::default();
     /// assert_eq!(iter.len(), 0);
     /// ```
+    ///
+    /// This is equivalent to `I::default().fuse()`[^fuse_note]; e.g. if
+    /// `I::default()` is not an empty iterator, then this will not be
+    /// an empty iterator.
+    ///
+    /// ```
+    /// # use std::iter::Fuse;
+    /// #[derive(Default)]
+    /// struct Fourever;
+    ///
+    /// impl Iterator for Fourever {
+    ///     type Item = u32;
+    ///     fn next(&mut self) -> Option<u32> {
+    ///         Some(4)
+    ///     }
+    /// }
+    ///
+    /// let mut iter: Fuse<Fourever> = Default::default();
+    /// assert_eq!(iter.next(), Some(4));
+    /// ```
+    ///
+    /// [^fuse_note]: if `I` does not override `Iterator::fuse`'s default implementation
     fn default() -> Self {
-        Fuse { iter: Default::default() }
+        Fuse { iter: Some(I::default()) }
     }
 }
 
diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs
index 4fa719de5eb..9e43d5688ce 100644
--- a/library/core/src/iter/range.rs
+++ b/library/core/src/iter/range.rs
@@ -20,6 +20,7 @@ unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u6
 ///
 /// The *successor* operation moves towards values that compare greater.
 /// The *predecessor* operation moves towards values that compare lesser.
+#[rustc_diagnostic_item = "range_step"]
 #[unstable(feature = "step_trait", issue = "42168")]
 pub trait Step: Clone + PartialOrd + Sized {
     /// Returns the bounds on the number of *successor* steps required to get from `start` to `end`
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 6231c3425c8..39d5399101d 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -103,6 +103,7 @@
 #![feature(cfg_select)]
 #![feature(cfg_target_has_reliable_f16_f128)]
 #![feature(const_carrying_mul_add)]
+#![feature(const_destruct)]
 #![feature(const_eval_select)]
 #![feature(core_intrinsics)]
 #![feature(coverage_attribute)]
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index b21845a1c16..8035dccc632 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -1109,45 +1109,6 @@ pub(crate) mod builtin {
         ($name:expr $(,)?) => {{ /* compiler built-in */ }};
     }
 
-    /// Concatenates identifiers into one identifier.
-    ///
-    /// This macro takes any number of comma-separated identifiers, and
-    /// concatenates them all into one, yielding an expression which is a new
-    /// identifier. Note that hygiene makes it such that this macro cannot
-    /// capture local variables. Also, as a general rule, macros are only
-    /// allowed in item, statement or expression position. That means while
-    /// you may use this macro for referring to existing variables, functions or
-    /// modules etc, you cannot define a new one with it.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(concat_idents)]
-    ///
-    /// # fn main() {
-    /// fn foobar() -> u32 { 23 }
-    ///
-    /// let f = concat_idents!(foo, bar);
-    /// println!("{}", f());
-    ///
-    /// // fn concat_idents!(new, fun, name) { } // not usable in this way!
-    /// # }
-    /// ```
-    #[unstable(
-        feature = "concat_idents",
-        issue = "29599",
-        reason = "`concat_idents` is not stable enough for use and is subject to change"
-    )]
-    #[deprecated(
-        since = "1.88.0",
-        note = "use `${concat(...)}` with the `macro_metavar_expr_concat` feature instead"
-    )]
-    #[rustc_builtin_macro]
-    #[macro_export]
-    macro_rules! concat_idents {
-        ($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }};
-    }
-
     /// Concatenates literals into a byte slice.
     ///
     /// This macro takes any number of comma-separated literals, and concatenates them all into
@@ -1192,6 +1153,7 @@ pub(crate) mod builtin {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
+    #[rustc_diagnostic_item = "macro_concat"]
     #[macro_export]
     macro_rules! concat {
         ($($e:expr),* $(,)?) => {{ /* compiler built-in */ }};
diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs
index f9638fea225..55fdacb014e 100644
--- a/library/core/src/marker/variance.rs
+++ b/library/core/src/marker/variance.rs
@@ -18,7 +18,7 @@ macro_rules! phantom_type {
         pub struct $name:ident <$t:ident> ($($inner:tt)*);
     )*) => {$(
         $(#[$attr])*
-        pub struct $name<$t>($($inner)*) where T: ?Sized;
+        pub struct $name<$t>($($inner)*) where $t: ?Sized;
 
         impl<T> $name<T>
             where T: ?Sized
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 73d9a8ee26d..b93f854b9dc 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -481,6 +481,7 @@ pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize {
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_promotable]
 #[rustc_const_stable(feature = "const_align_of", since = "1.24.0")]
+#[rustc_diagnostic_item = "mem_align_of"]
 pub const fn align_of<T>() -> usize {
     intrinsics::align_of::<T>()
 }
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 3a7bc902f93..5683d5ec92d 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -29,8 +29,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN, ", stringify!($Min), ");")]
         /// ```
@@ -42,8 +40,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($Max), ");")]
         /// ```
@@ -64,8 +60,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0b100_0000", stringify!($SelfT), ";")]
         ///
@@ -85,8 +79,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 1);")]
         /// ```
@@ -106,8 +98,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = -1", stringify!($SelfT), ";")]
         ///
@@ -127,8 +117,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = -4", stringify!($SelfT), ";")]
         ///
@@ -147,8 +135,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = -1", stringify!($SelfT), ";")]
         ///
@@ -167,8 +153,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 3", stringify!($SelfT), ";")]
         ///
@@ -188,8 +172,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(isolate_most_least_significant_one)]
         ///
@@ -211,8 +193,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(isolate_most_least_significant_one)]
         ///
@@ -236,8 +216,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = -1", stringify!($SelfT), ";")]
         ///
@@ -259,8 +237,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", $rot_op, stringify!($SelfT), ";")]
         #[doc = concat!("let m = ", $rot_result, ";")]
@@ -284,8 +260,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", $rot_result, stringify!($SelfT), ";")]
         #[doc = concat!("let m = ", $rot_op, ";")]
@@ -305,8 +279,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")]
         ///
@@ -328,8 +300,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")]
         /// let m = n.reverse_bits();
@@ -352,8 +322,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")]
         ///
@@ -384,8 +352,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")]
         ///
@@ -416,8 +382,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")]
         ///
@@ -449,8 +413,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")]
         ///
@@ -481,8 +443,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), Some(", stringify!($SelfT), "::MAX - 1));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);")]
@@ -508,8 +468,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).strict_add(1), ", stringify!($SelfT), "::MAX - 1);")]
@@ -576,8 +534,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_add_unsigned(2), Some(3));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add_unsigned(3), None);")]
@@ -603,8 +559,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".strict_add_unsigned(2), 3);")]
@@ -631,8 +585,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(1), Some(", stringify!($SelfT), "::MIN + 1));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub(3), None);")]
@@ -658,8 +610,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).strict_sub(1), ", stringify!($SelfT), "::MIN + 1);")]
@@ -726,8 +676,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_sub_unsigned(2), Some(-1));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub_unsigned(3), None);")]
@@ -753,8 +701,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".strict_sub_unsigned(2), -1);")]
@@ -781,8 +727,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(1), Some(", stringify!($SelfT), "::MAX));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);")]
@@ -808,8 +752,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.strict_mul(1), ", stringify!($SelfT), "::MAX);")]
@@ -876,8 +818,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_div(-1), Some(", stringify!($Max), "));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_div(-1), None);")]
@@ -914,8 +854,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).strict_div(-1), ", stringify!($Max), ");")]
@@ -949,8 +887,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_div_euclid(-1), Some(", stringify!($Max), "));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_div_euclid(-1), None);")]
@@ -987,8 +923,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).strict_div_euclid(-1), ", stringify!($Max), ");")]
@@ -1023,8 +957,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(exact_div)]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_exact_div(-1), Some(", stringify!($Max), "));")]
@@ -1063,8 +995,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(exact_div)]
         #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")]
@@ -1126,8 +1056,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1));")]
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);")]
@@ -1163,8 +1091,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".strict_rem(2), 1);")]
@@ -1198,8 +1124,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1));")]
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None);")]
@@ -1235,8 +1159,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".strict_rem_euclid(2), 1);")]
@@ -1269,8 +1191,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_neg(), Some(-5));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_neg(), None);")]
@@ -1328,8 +1248,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".strict_neg(), -5);")]
@@ -1356,8 +1274,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10));")]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(129), None);")]
@@ -1389,8 +1305,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".strict_shl(4), 0x10);")]
@@ -1453,7 +1367,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
         /// ```
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")]
@@ -1478,8 +1391,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1));")]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(128), None);")]
@@ -1510,8 +1421,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".strict_shr(4), 0x1);")]
@@ -1575,7 +1484,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
         /// ```
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(4), 0x1);")]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(129), 0);")]
@@ -1605,8 +1513,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").checked_abs(), Some(5));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_abs(), None);")]
@@ -1635,8 +1541,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").strict_abs(), 5);")]
@@ -1666,8 +1570,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(8", stringify!($SelfT), ".checked_pow(2), Some(64));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")]
@@ -1709,8 +1611,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(8", stringify!($SelfT), ".strict_pow(2), 64);")]
@@ -1753,7 +1653,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
         /// ```
         #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".checked_isqrt(), Some(3));")]
         /// ```
@@ -1801,8 +1700,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(100), ", stringify!($SelfT), "::MAX);")]
@@ -1823,8 +1720,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".saturating_add_unsigned(2), 3);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_add_unsigned(100), ", stringify!($SelfT), "::MAX);")]
@@ -1848,8 +1743,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub(100), ", stringify!($SelfT), "::MIN);")]
@@ -1869,8 +1762,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_sub_unsigned(127), -27);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub_unsigned(100), ", stringify!($SelfT), "::MIN);")]
@@ -1894,8 +1785,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_neg(), -100);")]
         #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").saturating_neg(), 100);")]
@@ -1917,8 +1806,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_abs(), 100);")]
         #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").saturating_abs(), 100);")]
@@ -1944,8 +1831,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".saturating_mul(12), 120);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_mul(10), ", stringify!($SelfT), "::MAX);")]
@@ -1976,8 +1861,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".saturating_div(2), 2);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_div(-1), ", stringify!($SelfT), "::MIN + 1);")]
@@ -2001,8 +1884,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!((-4", stringify!($SelfT), ").saturating_pow(3), -64);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(2), ", stringify!($SelfT), "::MAX);")]
@@ -2026,8 +1907,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_add(27), 127);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add(2), ", stringify!($SelfT), "::MIN + 1);")]
@@ -2046,8 +1925,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_add_unsigned(27), 127);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add_unsigned(2), ", stringify!($SelfT), "::MIN + 1);")]
@@ -2066,8 +1943,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".wrapping_sub(127), -127);")]
         #[doc = concat!("assert_eq!((-2", stringify!($SelfT), ").wrapping_sub(", stringify!($SelfT), "::MAX), ", stringify!($SelfT), "::MAX);")]
@@ -2086,8 +1961,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".wrapping_sub_unsigned(127), -127);")]
         #[doc = concat!("assert_eq!((-2", stringify!($SelfT), ").wrapping_sub_unsigned(", stringify!($UnsignedT), "::MAX), -1);")]
@@ -2106,8 +1979,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".wrapping_mul(12), 120);")]
         /// assert_eq!(11i8.wrapping_mul(12), -124);
@@ -2134,8 +2005,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);")]
         /// assert_eq!((-128i8).wrapping_div(-1), -128);
@@ -2162,8 +2031,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10);")]
         /// assert_eq!((-128i8).wrapping_div_euclid(-1), -128);
@@ -2190,8 +2057,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);")]
         /// assert_eq!((-128i8).wrapping_rem(-1), 0);
@@ -2217,8 +2082,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0);")]
         /// assert_eq!((-128i8).wrapping_rem_euclid(-1), 0);
@@ -2241,8 +2104,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_neg(), -100);")]
         #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").wrapping_neg(), 100);")]
@@ -2267,8 +2128,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128);")]
         #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);")]
@@ -2296,8 +2155,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1);")]
         /// assert_eq!((-128i16).wrapping_shr(64), -128);
@@ -2324,8 +2181,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_abs(), 100);")]
         #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").wrapping_abs(), 100);")]
@@ -2352,8 +2207,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".unsigned_abs(), 100", stringify!($UnsignedT), ");")]
         #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").unsigned_abs(), 100", stringify!($UnsignedT), ");")]
@@ -2373,8 +2226,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(4), 81);")]
         /// assert_eq!(3i8.wrapping_pow(5), -13);
@@ -2432,8 +2283,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (", stringify!($SelfT), "::MIN, true));")]
@@ -2513,8 +2362,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".overflowing_add_unsigned(2), (3, false));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN).overflowing_add_unsigned(", stringify!($UnsignedT), "::MAX), (", stringify!($SelfT), "::MAX, false));")]
@@ -2538,8 +2385,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));")]
@@ -2620,8 +2465,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".overflowing_sub_unsigned(2), (-1, false));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX).overflowing_sub_unsigned(", stringify!($UnsignedT), "::MAX), (", stringify!($SelfT), "::MIN, false));")]
@@ -2645,8 +2488,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_mul(2), (10, false));")]
         /// assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));
@@ -2671,8 +2512,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// Please note that this example is shared between integer types.
         /// Which explains why `i32` is used here.
         ///
@@ -2704,8 +2543,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// Please note that this example is shared between integer types.
         /// Which explains why `i32` is used here.
         ///
@@ -2744,8 +2581,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// Please note that this example is shared between integer types.
         /// Which explains why `i32` is used here.
         ///
@@ -2780,8 +2615,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div(-1), (", stringify!($SelfT), "::MIN, true));")]
@@ -2811,8 +2644,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div_euclid(-1), (", stringify!($SelfT), "::MIN, true));")]
@@ -2842,8 +2673,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem(-1), (0, true));")]
@@ -2873,8 +2702,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem_euclid(-1), (0, true));")]
@@ -2902,8 +2729,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2, false));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.overflowing_neg(), (", stringify!($SelfT), "::MIN, true));")]
@@ -2930,8 +2755,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT),".overflowing_shl(4), (0x10, false));")]
         /// assert_eq!(0x1i32.overflowing_shl(36), (0x10, true));
@@ -2954,8 +2777,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false));")]
         /// assert_eq!(0x10i32.overflowing_shr(36), (0x1, true));
@@ -2979,8 +2800,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".overflowing_abs(), (10, false));")]
         #[doc = concat!("assert_eq!((-10", stringify!($SelfT), ").overflowing_abs(), (10, false));")]
@@ -3002,8 +2821,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(4), (81, false));")]
         /// assert_eq!(3i8.overflowing_pow(5), (-13, true));
@@ -3045,8 +2862,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let x: ", stringify!($SelfT), " = 2; // or any other integer type")]
         ///
@@ -3106,7 +2921,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
         /// ```
         #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".isqrt(), 3);")]
         /// ```
@@ -3142,8 +2956,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let a: ", stringify!($SelfT), " = 7; // or any other integer type")]
         /// let b = 4;
@@ -3181,8 +2993,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let a: ", stringify!($SelfT), " = 7; // or any other integer type")]
         /// let b = 4;
@@ -3230,8 +3040,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(int_roundings)]
         #[doc = concat!("let a: ", stringify!($SelfT)," = 8;")]
@@ -3274,8 +3082,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(int_roundings)]
         #[doc = concat!("let a: ", stringify!($SelfT)," = 8;")]
@@ -3321,8 +3127,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(int_roundings)]
         #[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".next_multiple_of(8), 16);")]
@@ -3367,8 +3171,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(int_roundings)]
         #[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".checked_next_multiple_of(8), Some(16));")]
@@ -3582,8 +3384,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".abs(), 10);")]
         #[doc = concat!("assert_eq!((-10", stringify!($SelfT), ").abs(), 10);")]
@@ -3613,8 +3413,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(80), 20", stringify!($UnsignedT), ");")]
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(110), 10", stringify!($UnsignedT), ");")]
@@ -3656,8 +3454,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".signum(), 1);")]
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".signum(), 0);")]
@@ -3682,8 +3478,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert!(10", stringify!($SelfT), ".is_positive());")]
         #[doc = concat!("assert!(!(-10", stringify!($SelfT), ").is_positive());")]
@@ -3699,8 +3493,6 @@ macro_rules! int_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert!((-10", stringify!($SelfT), ").is_negative());")]
         #[doc = concat!("assert!(!10", stringify!($SelfT), ".is_negative());")]
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index ab2fcff61cd..faa41ddf13c 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -1399,7 +1399,6 @@ macro_rules! from_str_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
             /// ```
             /// use std::str::FromStr;
             ///
@@ -1445,7 +1444,6 @@ macro_rules! from_str_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
             /// ```
             #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")]
             /// ```
@@ -1478,7 +1476,6 @@ macro_rules! from_str_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
             /// ```
             /// #![feature(int_from_ascii)]
             ///
@@ -1523,7 +1520,6 @@ macro_rules! from_str_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
             /// ```
             /// #![feature(int_from_ascii)]
             ///
diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs
index 3f4791e163e..4460e430aec 100644
--- a/library/core/src/num/saturating.rs
+++ b/library/core/src/num/saturating.rs
@@ -300,8 +300,6 @@ macro_rules! saturating_impl {
 
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// use std::num::Saturating;
         ///
@@ -490,8 +488,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -504,8 +500,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -518,8 +512,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -532,8 +524,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -556,8 +546,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -576,8 +564,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -603,8 +589,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -631,8 +615,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -654,8 +636,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -683,8 +663,6 @@ macro_rules! saturating_int_impl {
             /// Please note that this example is shared between integer types.
             /// Which explains why `i16` is used here.
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -712,8 +690,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -740,8 +716,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -768,8 +742,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -797,8 +769,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -823,8 +793,6 @@ macro_rules! saturating_int_impl {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -860,8 +828,6 @@ macro_rules! saturating_int_impl_signed {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -883,8 +849,6 @@ macro_rules! saturating_int_impl_signed {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -911,8 +875,6 @@ macro_rules! saturating_int_impl_signed {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -934,8 +896,6 @@ macro_rules! saturating_int_impl_signed {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -955,8 +915,6 @@ macro_rules! saturating_int_impl_signed {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -994,8 +952,6 @@ macro_rules! saturating_int_impl_unsigned {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
@@ -1016,8 +972,6 @@ macro_rules! saturating_int_impl_unsigned {
             ///
             /// # Examples
             ///
-            /// Basic usage:
-            ///
             /// ```
             /// use std::num::Saturating;
             ///
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 4ee0e7326b3..079032f2f96 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -27,8 +27,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN, 0);")]
         /// ```
@@ -40,8 +38,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX, ", stringify!($MaxV), ");")]
         /// ```
@@ -62,8 +58,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0b01001100", stringify!($SelfT), ";")]
         /// assert_eq!(n.count_ones(), 3);
@@ -89,8 +83,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let zero = 0", stringify!($SelfT), ";")]
         #[doc = concat!("assert_eq!(zero.count_zeros(), ", stringify!($BITS), ");")]
@@ -114,8 +106,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", stringify!($SelfT), "::MAX >> 2;")]
         /// assert_eq!(n.leading_zeros(), 2);
@@ -141,8 +131,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0b0101000", stringify!($SelfT), ";")]
         /// assert_eq!(n.trailing_zeros(), 3);
@@ -166,8 +154,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = !(", stringify!($SelfT), "::MAX >> 2);")]
         /// assert_eq!(n.leading_ones(), 2);
@@ -192,8 +178,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0b1010111", stringify!($SelfT), ";")]
         /// assert_eq!(n.trailing_ones(), 3);
@@ -219,8 +203,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(uint_bit_width)]
         ///
@@ -242,8 +224,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(isolate_most_least_significant_one)]
         ///
@@ -265,8 +245,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(isolate_most_least_significant_one)]
         ///
@@ -290,8 +268,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", stringify!($SelfT), "::MAX;")]
         ///
@@ -313,8 +289,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", $rot_op, stringify!($SelfT), ";")]
         #[doc = concat!("let m = ", $rot_result, ";")]
@@ -338,8 +312,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", $rot_result, stringify!($SelfT), ";")]
         #[doc = concat!("let m = ", $rot_op, ";")]
@@ -359,8 +331,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")]
         /// let m = n.swap_bytes();
@@ -381,8 +351,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = ", $swap_op, stringify!($SelfT), ";")]
         /// let m = n.reverse_bits();
@@ -406,8 +374,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")]
         ///
@@ -439,8 +405,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")]
         ///
@@ -472,8 +436,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")]
         ///
@@ -506,8 +468,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("let n = 0x1A", stringify!($SelfT), ";")]
         ///
@@ -538,8 +498,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!(
             "assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(1), ",
@@ -579,8 +537,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).strict_add(1), ", stringify!($SelfT), "::MAX - 1);")]
@@ -647,8 +603,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_add_signed(2), Some(3));")]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_add_signed(-2), None);")]
@@ -675,8 +629,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".strict_add_signed(2), 3);")]
@@ -708,8 +660,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_sub(1), Some(0));")]
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".checked_sub(1), None);")]
@@ -744,8 +694,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".strict_sub(1), 0);")]
@@ -837,8 +785,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(mixed_integer_ops_unsigned_sub)]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_sub_signed(2), None);")]
@@ -866,8 +812,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(unsigned_signed_diff)]
         #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".checked_signed_diff(2), Some(8));")]
@@ -925,8 +869,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_mul(1), Some(5));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);")]
@@ -952,8 +894,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".strict_mul(1), 5);")]
@@ -1020,8 +960,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".checked_div(2), Some(64));")]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_div(0), None);")]
@@ -1053,8 +991,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".strict_div(10), 10);")]
@@ -1080,8 +1016,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".checked_div_euclid(2), Some(64));")]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_div_euclid(0), None);")]
@@ -1113,8 +1047,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".strict_div_euclid(10), 10);")]
@@ -1142,8 +1074,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(exact_div)]
         #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")]
@@ -1184,8 +1114,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(exact_div)]
         #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), 32);")]
@@ -1241,8 +1169,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(2), Some(1));")]
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem(0), None);")]
@@ -1275,8 +1201,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".strict_rem(10), 0);")]
@@ -1302,8 +1226,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(2), Some(1));")]
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_rem_euclid(0), None);")]
@@ -1336,8 +1258,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".strict_rem_euclid(10), 0);")]
@@ -1583,8 +1503,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".checked_neg(), Some(0));")]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_neg(), None);")]
@@ -1612,8 +1530,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".strict_neg(), 0);")]
@@ -1640,8 +1556,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10));")]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shl(129), None);")]
@@ -1673,8 +1587,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".strict_shl(4), 0x10);")]
@@ -1737,7 +1649,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
         /// ```
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")]
@@ -1762,8 +1673,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(4), Some(0x1));")]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shr(129), None);")]
@@ -1794,8 +1703,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".strict_shr(4), 0x1);")]
@@ -1858,7 +1765,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
         /// ```
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(4), 0x1);")]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(129), 0);")]
@@ -1883,8 +1789,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_pow(5), Some(32));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")]
@@ -1925,8 +1829,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(strict_overflow_ops)]
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".strict_pow(5), 32);")]
@@ -1968,8 +1870,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(127), ", stringify!($SelfT), "::MAX);")]
@@ -1988,8 +1888,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".saturating_add_signed(2), 3);")]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".saturating_add_signed(-2), 0);")]
@@ -2016,8 +1914,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73);")]
         #[doc = concat!("assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);")]
@@ -2036,8 +1932,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(mixed_integer_ops_unsigned_sub)]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".saturating_sub_signed(2), 0);")]
@@ -2065,8 +1959,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".saturating_mul(10), 20);")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX).saturating_mul(10), ", stringify!($SelfT),"::MAX);")]
@@ -2092,8 +1984,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".saturating_div(2), 2);")]
         ///
@@ -2114,8 +2004,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(4", stringify!($SelfT), ".saturating_pow(3), 64);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_pow(2), ", stringify!($SelfT), "::MAX);")]
@@ -2137,8 +2025,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(200", stringify!($SelfT), ".wrapping_add(55), 255);")]
         #[doc = concat!("assert_eq!(200", stringify!($SelfT), ".wrapping_add(", stringify!($SelfT), "::MAX), 199);")]
@@ -2157,8 +2043,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_add_signed(2), 3);")]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_add_signed(-2), ", stringify!($SelfT), "::MAX);")]
@@ -2178,8 +2062,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_sub(100), 0);")]
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_sub(", stringify!($SelfT), "::MAX), 101);")]
@@ -2198,8 +2080,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(mixed_integer_ops_unsigned_sub)]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_sub_signed(2), ", stringify!($SelfT), "::MAX);")]
@@ -2219,8 +2099,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// Please note that this example is shared between integer types.
         /// Which explains why `u8` is used here.
         ///
@@ -2249,8 +2127,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div(10), 10);")]
         /// ```
@@ -2278,8 +2154,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_div_euclid(10), 10);")]
         /// ```
@@ -2306,8 +2180,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem(10), 0);")]
         /// ```
@@ -2336,8 +2208,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_rem_euclid(10), 0);")]
         /// ```
@@ -2363,8 +2233,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".wrapping_neg(), 0);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_neg(), 1);")]
@@ -2393,8 +2261,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128);")]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);")]
@@ -2425,8 +2291,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1);")]
         #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);")]
@@ -2449,8 +2313,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(5), 243);")]
         /// assert_eq!(3u8.wrapping_pow(6), 217);
@@ -2507,8 +2369,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_add(2), (7, false));")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.overflowing_add(1), (0, true));")]
@@ -2587,8 +2447,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".overflowing_add_signed(2), (3, false));")]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".overflowing_add_signed(-2), (", stringify!($SelfT), "::MAX, true));")]
@@ -2612,8 +2470,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_sub(2), (3, false));")]
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_sub(1), (", stringify!($SelfT), "::MAX, true));")]
@@ -2683,8 +2539,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(mixed_integer_ops_unsigned_sub)]
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".overflowing_sub_signed(2), (", stringify!($SelfT), "::MAX, true));")]
@@ -2705,8 +2559,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(80), 20", stringify!($SelfT), ");")]
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(110), 10", stringify!($SelfT), ");")]
@@ -2738,8 +2590,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// Please note that this example is shared between integer types.
         /// Which explains why `u32` is used here.
         ///
@@ -2767,8 +2617,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// Please note that this example is shared between integer types.
         /// Which explains why `u32` is used here.
         ///
@@ -2800,8 +2648,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// Please note that this example is shared between integer types.
         /// Which explains why `u32` is used here.
         ///
@@ -2888,8 +2734,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// Please note that this example is shared between integer types,
         /// which explains why `u32` is used here.
         ///
@@ -2955,8 +2799,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")]
         /// ```
@@ -2986,8 +2828,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")]
         /// ```
@@ -3014,8 +2854,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")]
         /// ```
@@ -3045,8 +2883,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")]
         /// ```
@@ -3069,8 +2905,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false));")]
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), ", true));")]
@@ -3094,8 +2928,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(4), (0x10, false));")]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(132), (0x10, true));")]
@@ -3120,8 +2952,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(4), (0x1, false));")]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shr(132), (0x1, true));")]
@@ -3142,8 +2972,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(5), (243, false));")]
         /// assert_eq!(3u8.overflowing_pow(6), (217, true));
@@ -3185,8 +3013,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".pow(5), 32);")]
         /// ```
@@ -3240,7 +3066,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
         /// ```
         #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".isqrt(), 3);")]
         /// ```
@@ -3282,8 +3107,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(7", stringify!($SelfT), ".div_euclid(4), 1); // or any other integer type")]
         /// ```
@@ -3310,8 +3133,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(7", stringify!($SelfT), ".rem_euclid(4), 3); // or any other integer type")]
         /// ```
@@ -3336,8 +3157,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(int_roundings)]
         #[doc = concat!("assert_eq!(7_", stringify!($SelfT), ".div_floor(4), 1);")]
@@ -3359,8 +3178,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(7_", stringify!($SelfT), ".div_ceil(4), 2);")]
         /// ```
@@ -3394,8 +3211,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".next_multiple_of(8), 16);")]
         #[doc = concat!("assert_eq!(23_", stringify!($SelfT), ".next_multiple_of(8), 24);")]
@@ -3419,8 +3234,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".checked_next_multiple_of(8), Some(16));")]
         #[doc = concat!("assert_eq!(23_", stringify!($SelfT), ".checked_next_multiple_of(8), Some(24));")]
@@ -3448,8 +3261,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert!(6_", stringify!($SelfT), ".is_multiple_of(2));")]
         #[doc = concat!("assert!(!5_", stringify!($SelfT), ".is_multiple_of(2));")]
@@ -3473,8 +3284,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert!(16", stringify!($SelfT), ".is_power_of_two());")]
         #[doc = concat!("assert!(!10", stringify!($SelfT), ".is_power_of_two());")]
@@ -3517,8 +3326,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".next_power_of_two(), 2);")]
         #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".next_power_of_two(), 4);")]
@@ -3540,8 +3347,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_next_power_of_two(), Some(2));")]
         #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".checked_next_power_of_two(), Some(4));")]
@@ -3562,8 +3367,6 @@ macro_rules! uint_impl {
         ///
         /// # Examples
         ///
-        /// Basic usage:
-        ///
         /// ```
         /// #![feature(wrapping_next_power_of_two)]
         ///
diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs
index 098ce4531f0..7ffde233da3 100644
--- a/library/core/src/ops/arith.rs
+++ b/library/core/src/ops/arith.rs
@@ -179,12 +179,14 @@ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128
 /// ```
 #[lang = "sub"]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
 #[rustc_on_unimplemented(
     message = "cannot subtract `{Rhs}` from `{Self}`",
     label = "no implementation for `{Self} - {Rhs}`",
     append_const_msg
 )]
 #[doc(alias = "-")]
+#[const_trait]
 pub trait Sub<Rhs = Self> {
     /// The resulting type after applying the `-` operator.
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -206,7 +208,8 @@ pub trait Sub<Rhs = Self> {
 macro_rules! sub_impl {
     ($($t:ty)*) => ($(
         #[stable(feature = "rust1", since = "1.0.0")]
-        impl Sub for $t {
+        #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+        impl const Sub for $t {
             type Output = $t;
 
             #[inline]
@@ -310,11 +313,13 @@ sub_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128
 /// ```
 #[lang = "mul"]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
 #[diagnostic::on_unimplemented(
     message = "cannot multiply `{Self}` by `{Rhs}`",
     label = "no implementation for `{Self} * {Rhs}`"
 )]
 #[doc(alias = "*")]
+#[const_trait]
 pub trait Mul<Rhs = Self> {
     /// The resulting type after applying the `*` operator.
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -336,7 +341,8 @@ pub trait Mul<Rhs = Self> {
 macro_rules! mul_impl {
     ($($t:ty)*) => ($(
         #[stable(feature = "rust1", since = "1.0.0")]
-        impl Mul for $t {
+        #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+        impl const Mul for $t {
             type Output = $t;
 
             #[inline]
@@ -444,11 +450,13 @@ mul_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128
 /// ```
 #[lang = "div"]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
 #[diagnostic::on_unimplemented(
     message = "cannot divide `{Self}` by `{Rhs}`",
     label = "no implementation for `{Self} / {Rhs}`"
 )]
 #[doc(alias = "/")]
+#[const_trait]
 pub trait Div<Rhs = Self> {
     /// The resulting type after applying the `/` operator.
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -476,7 +484,8 @@ macro_rules! div_impl_integer {
         ///
         #[doc = $panic]
         #[stable(feature = "rust1", since = "1.0.0")]
-        impl Div for $t {
+        #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+        impl const Div for $t {
             type Output = $t;
 
             #[inline]
@@ -496,7 +505,8 @@ div_impl_integer! {
 macro_rules! div_impl_float {
     ($($t:ty)*) => ($(
         #[stable(feature = "rust1", since = "1.0.0")]
-        impl Div for $t {
+        #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+        impl const Div for $t {
             type Output = $t;
 
             #[inline]
@@ -546,11 +556,13 @@ div_impl_float! { f16 f32 f64 f128 }
 /// ```
 #[lang = "rem"]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
 #[diagnostic::on_unimplemented(
     message = "cannot calculate the remainder of `{Self}` divided by `{Rhs}`",
     label = "no implementation for `{Self} % {Rhs}`"
 )]
 #[doc(alias = "%")]
+#[const_trait]
 pub trait Rem<Rhs = Self> {
     /// The resulting type after applying the `%` operator.
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -578,7 +590,8 @@ macro_rules! rem_impl_integer {
         ///
         #[doc = $panic]
         #[stable(feature = "rust1", since = "1.0.0")]
-        impl Rem for $t {
+        #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+        impl const Rem for $t {
             type Output = $t;
 
             #[inline]
@@ -613,6 +626,7 @@ macro_rules! rem_impl_float {
         /// assert_eq!(x % y, remainder);
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
+        #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
         impl Rem for $t {
             type Output = $t;
 
diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs
index 8f1b5275871..7b9e04920d5 100644
--- a/library/core/src/prelude/v1.rs
+++ b/library/core/src/prelude/v1.rs
@@ -58,10 +58,9 @@ pub use crate::fmt::macros::Debug;
 pub use crate::hash::macros::Hash;
 
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
-#[allow(deprecated)]
 #[doc(no_inline)]
 pub use crate::{
-    assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args,
+    assert, cfg, column, compile_error, concat, env, file, format_args,
     format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env,
     stringify, trace_macros,
 };
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index c26bbad087a..3a3f44c6b85 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -2764,6 +2764,89 @@ impl<T> [T] {
         None
     }
 
+    /// Returns a subslice with the optional prefix removed.
+    ///
+    /// If the slice starts with `prefix`, returns the subslice after the prefix.  If `prefix`
+    /// is empty or the slice does not start with `prefix`, simply returns the original slice.
+    /// If `prefix` is equal to the original slice, returns an empty slice.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(trim_prefix_suffix)]
+    ///
+    /// let v = &[10, 40, 30];
+    ///
+    /// // Prefix present - removes it
+    /// assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]);
+    /// assert_eq!(v.trim_prefix(&[10, 40]), &[30][..]);
+    /// assert_eq!(v.trim_prefix(&[10, 40, 30]), &[][..]);
+    ///
+    /// // Prefix absent - returns original slice
+    /// assert_eq!(v.trim_prefix(&[50]), &[10, 40, 30][..]);
+    /// assert_eq!(v.trim_prefix(&[10, 50]), &[10, 40, 30][..]);
+    ///
+    /// let prefix : &str = "he";
+    /// assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref());
+    /// ```
+    #[must_use = "returns the subslice without modifying the original"]
+    #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+    pub fn trim_prefix<P: SlicePattern<Item = T> + ?Sized>(&self, prefix: &P) -> &[T]
+    where
+        T: PartialEq,
+    {
+        // This function will need rewriting if and when SlicePattern becomes more sophisticated.
+        let prefix = prefix.as_slice();
+        let n = prefix.len();
+        if n <= self.len() {
+            let (head, tail) = self.split_at(n);
+            if head == prefix {
+                return tail;
+            }
+        }
+        self
+    }
+
+    /// Returns a subslice with the optional suffix removed.
+    ///
+    /// If the slice ends with `suffix`, returns the subslice before the suffix.  If `suffix`
+    /// is empty or the slice does not end with `suffix`, simply returns the original slice.
+    /// If `suffix` is equal to the original slice, returns an empty slice.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(trim_prefix_suffix)]
+    ///
+    /// let v = &[10, 40, 30];
+    ///
+    /// // Suffix present - removes it
+    /// assert_eq!(v.trim_suffix(&[30]), &[10, 40][..]);
+    /// assert_eq!(v.trim_suffix(&[40, 30]), &[10][..]);
+    /// assert_eq!(v.trim_suffix(&[10, 40, 30]), &[][..]);
+    ///
+    /// // Suffix absent - returns original slice
+    /// assert_eq!(v.trim_suffix(&[50]), &[10, 40, 30][..]);
+    /// assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]);
+    /// ```
+    #[must_use = "returns the subslice without modifying the original"]
+    #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+    pub fn trim_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> &[T]
+    where
+        T: PartialEq,
+    {
+        // This function will need rewriting if and when SlicePattern becomes more sophisticated.
+        let suffix = suffix.as_slice();
+        let (len, n) = (self.len(), suffix.len());
+        if n <= len {
+            let (head, tail) = self.split_at(len - n);
+            if tail == suffix {
+                return head;
+            }
+        }
+        self
+    }
+
     /// Binary searches this slice for a given element.
     /// If the slice is not sorted, the returned result is unspecified and
     /// meaningless.
diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs
index eba2f89a169..80b2176933d 100644
--- a/library/core/src/slice/raw.rs
+++ b/library/core/src/slice/raw.rs
@@ -198,6 +198,7 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m
 /// Converts a reference to T into a slice of length 1 (without copying).
 #[stable(feature = "from_ref", since = "1.28.0")]
 #[rustc_const_stable(feature = "const_slice_from_ref_shared", since = "1.63.0")]
+#[rustc_diagnostic_item = "slice_from_ref"]
 #[must_use]
 pub const fn from_ref<T>(s: &T) -> &[T] {
     array::from_ref(s)
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index 41834793d22..d250e1478d8 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -1495,6 +1495,9 @@ impl str {
     /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
     /// function or closure that determines if a character matches.
     ///
+    /// If there are no matches the full string slice is returned as the only
+    /// item in the iterator.
+    ///
     /// [`char`]: prim@char
     /// [pattern]: self::pattern
     ///
@@ -1526,6 +1529,9 @@ impl str {
     /// let v: Vec<&str> = "lion::tiger::leopard".split("::").collect();
     /// assert_eq!(v, ["lion", "tiger", "leopard"]);
     ///
+    /// let v: Vec<&str> = "AABBCC".split("DD").collect();
+    /// assert_eq!(v, ["AABBCC"]);
+    ///
     /// let v: Vec<&str> = "abc1def2ghi".split(char::is_numeric).collect();
     /// assert_eq!(v, ["abc", "def", "ghi"]);
     ///
@@ -2426,6 +2432,83 @@ impl str {
         suffix.strip_suffix_of(self)
     }
 
+    /// Returns a string slice with the optional prefix removed.
+    ///
+    /// If the string starts with the pattern `prefix`, returns the substring after the prefix.
+    /// Unlike [`strip_prefix`], this method always returns `&str` for easy method chaining,
+    /// instead of returning [`Option<&str>`].
+    ///
+    /// If the string does not start with `prefix`, returns the original string unchanged.
+    ///
+    /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
+    /// function or closure that determines if a character matches.
+    ///
+    /// [`char`]: prim@char
+    /// [pattern]: self::pattern
+    /// [`strip_prefix`]: Self::strip_prefix
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(trim_prefix_suffix)]
+    ///
+    /// // Prefix present - removes it
+    /// assert_eq!("foo:bar".trim_prefix("foo:"), "bar");
+    /// assert_eq!("foofoo".trim_prefix("foo"), "foo");
+    ///
+    /// // Prefix absent - returns original string
+    /// assert_eq!("foo:bar".trim_prefix("bar"), "foo:bar");
+    ///
+    /// // Method chaining example
+    /// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/");
+    /// ```
+    #[must_use = "this returns the remaining substring as a new slice, \
+                  without modifying the original"]
+    #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+    pub fn trim_prefix<P: Pattern>(&self, prefix: P) -> &str {
+        prefix.strip_prefix_of(self).unwrap_or(self)
+    }
+
+    /// Returns a string slice with the optional suffix removed.
+    ///
+    /// If the string ends with the pattern `suffix`, returns the substring before the suffix.
+    /// Unlike [`strip_suffix`], this method always returns `&str` for easy method chaining,
+    /// instead of returning [`Option<&str>`].
+    ///
+    /// If the string does not end with `suffix`, returns the original string unchanged.
+    ///
+    /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
+    /// function or closure that determines if a character matches.
+    ///
+    /// [`char`]: prim@char
+    /// [pattern]: self::pattern
+    /// [`strip_suffix`]: Self::strip_suffix
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(trim_prefix_suffix)]
+    ///
+    /// // Suffix present - removes it
+    /// assert_eq!("bar:foo".trim_suffix(":foo"), "bar");
+    /// assert_eq!("foofoo".trim_suffix("foo"), "foo");
+    ///
+    /// // Suffix absent - returns original string
+    /// assert_eq!("bar:foo".trim_suffix("bar"), "bar:foo");
+    ///
+    /// // Method chaining example
+    /// assert_eq!("<https://example.com/>".trim_prefix('<').trim_suffix('>'), "https://example.com/");
+    /// ```
+    #[must_use = "this returns the remaining substring as a new slice, \
+                  without modifying the original"]
+    #[unstable(feature = "trim_prefix_suffix", issue = "142312")]
+    pub fn trim_suffix<P: Pattern>(&self, suffix: P) -> &str
+    where
+        for<'a> P::Searcher<'a>: ReverseSearcher<'a>,
+    {
+        suffix.strip_suffix_of(self).unwrap_or(self)
+    }
+
     /// Returns a string slice with all suffixes that match a pattern
     /// repeatedly removed.
     ///
diff --git a/library/coretests/tests/atomic.rs b/library/coretests/tests/atomic.rs
index e0c0fe4790c..b1ab443aa6e 100644
--- a/library/coretests/tests/atomic.rs
+++ b/library/coretests/tests/atomic.rs
@@ -228,24 +228,20 @@ fn static_init() {
 }
 
 #[test]
-// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
-#[allow(static_mut_refs)]
 fn atomic_access_bool() {
-    static mut ATOMIC: AtomicBool = AtomicBool::new(false);
-
-    unsafe {
-        assert_eq!(*ATOMIC.get_mut(), false);
-        ATOMIC.store(true, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), true);
-        ATOMIC.fetch_or(false, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), true);
-        ATOMIC.fetch_and(false, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), false);
-        ATOMIC.fetch_nand(true, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), true);
-        ATOMIC.fetch_xor(true, SeqCst);
-        assert_eq!(*ATOMIC.get_mut(), false);
-    }
+    let mut atom = AtomicBool::new(false);
+
+    assert_eq!(*atom.get_mut(), false);
+    atom.store(true, SeqCst);
+    assert_eq!(*atom.get_mut(), true);
+    atom.fetch_or(false, SeqCst);
+    assert_eq!(*atom.get_mut(), true);
+    atom.fetch_and(false, SeqCst);
+    assert_eq!(*atom.get_mut(), false);
+    atom.fetch_nand(true, SeqCst);
+    assert_eq!(*atom.get_mut(), true);
+    atom.fetch_xor(true, SeqCst);
+    assert_eq!(*atom.get_mut(), false);
 }
 
 #[test]
diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs
index d9060fe903d..16f116d2590 100644
--- a/library/coretests/tests/fmt/mod.rs
+++ b/library/coretests/tests/fmt/mod.rs
@@ -3,6 +3,21 @@ mod float;
 mod num;
 
 #[test]
+fn test_lifetime() {
+    // Trigger all different forms of expansion,
+    // and check that each of them can be stored as a variable.
+    let a = format_args!("hello");
+    let a = format_args!("hello {a}");
+    let a = format_args!("hello {a:1}");
+    let a = format_args!("hello {a} {a:?}");
+    assert_eq!(a.to_string(), "hello hello hello hello hello hello hello");
+
+    // Without arguments, it should also work in consts.
+    const A: std::fmt::Arguments<'static> = format_args!("hello");
+    assert_eq!(A.to_string(), "hello");
+}
+
+#[test]
 fn test_format_flags() {
     // No residual flags left by pointer formatting
     let p = "".as_ptr();
diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml
index 1d79246356a..8ea92088a84 100644
--- a/library/proc_macro/Cargo.toml
+++ b/library/proc_macro/Cargo.toml
@@ -9,7 +9,7 @@ std = { path = "../std" }
 # `core` when resolving doc links. Without this line a different `core` will be
 # loaded from sysroot causing duplicate lang items and other similar errors.
 core = { path = "../core" }
-rustc-literal-escaper = { version = "0.0.2", features = ["rustc-dep-of-std"] }
+rustc-literal-escaper = { version = "0.0.4", features = ["rustc-dep-of-std"] }
 
 [features]
 default = ["rustc-dep-of-std"]
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index b0084edf2f8..ff326342989 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -56,7 +56,7 @@ use std::{error, fmt};
 pub use diagnostic::{Diagnostic, Level, MultiSpan};
 #[unstable(feature = "proc_macro_value", issue = "136652")]
 pub use rustc_literal_escaper::EscapeError;
-use rustc_literal_escaper::{MixedUnit, Mode, byte_from_char, unescape_mixed, unescape_unicode};
+use rustc_literal_escaper::{MixedUnit, unescape_byte_str, unescape_c_str, unescape_str};
 #[unstable(feature = "proc_macro_totokens", issue = "130977")]
 pub use to_tokens::ToTokens;
 
@@ -1440,10 +1440,9 @@ impl Literal {
                     // Force-inlining here is aggressive but the closure is
                     // called on every char in the string, so it can be hot in
                     // programs with many long strings containing escapes.
-                    unescape_unicode(
+                    unescape_str(
                         symbol,
-                        Mode::Str,
-                        &mut #[inline(always)]
+                        #[inline(always)]
                         |_, c| match c {
                             Ok(c) => buf.push(c),
                             Err(err) => {
@@ -1472,7 +1471,7 @@ impl Literal {
                 let mut error = None;
                 let mut buf = Vec::with_capacity(symbol.len());
 
-                unescape_mixed(symbol, Mode::CStr, &mut |_span, c| match c {
+                unescape_c_str(symbol, |_span, c| match c {
                     Ok(MixedUnit::Char(c)) => {
                         buf.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes())
                     }
@@ -1511,8 +1510,8 @@ impl Literal {
                 let mut buf = Vec::with_capacity(symbol.len());
                 let mut error = None;
 
-                unescape_unicode(symbol, Mode::ByteStr, &mut |_, c| match c {
-                    Ok(c) => buf.push(byte_from_char(c)),
+                unescape_byte_str(symbol, |_, res| match res {
+                    Ok(b) => buf.push(b),
                     Err(err) => {
                         if err.is_fatal() {
                             error = Some(ConversionErrorKind::FailedToUnescape(err));
diff --git a/library/rustc-std-workspace-core/Cargo.toml b/library/rustc-std-workspace-core/Cargo.toml
index bd318fc2f9e..1ddc112380f 100644
--- a/library/rustc-std-workspace-core/Cargo.toml
+++ b/library/rustc-std-workspace-core/Cargo.toml
@@ -1,3 +1,5 @@
+cargo-features = ["public-dependency"]
+
 [package]
 name = "rustc-std-workspace-core"
 version = "1.99.0"
@@ -11,5 +13,7 @@ edition = "2024"
 path = "lib.rs"
 
 [dependencies]
-core = { path = "../core" }
-compiler_builtins = { path = "../compiler-builtins/compiler-builtins", features = ["compiler-builtins"] }
+core = { path = "../core", public = true }
+compiler_builtins = { path = "../compiler-builtins/compiler-builtins", features = [
+  "compiler-builtins",
+] }
diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs
index ba765a6203f..d43976ecc9e 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -219,6 +219,7 @@ struct Custom {
 /// the recognized error kinds and fail in those cases.
 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "io_errorkind")]
 #[allow(deprecated)]
 #[non_exhaustive]
 pub enum ErrorKind {
@@ -562,6 +563,7 @@ impl Error {
     /// let eof_error = Error::from(ErrorKind::UnexpectedEof);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")]
     #[inline(never)]
     pub fn new<E>(kind: ErrorKind, error: E) -> Error
     where
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 2bb7a63772d..13fb08a9210 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -281,7 +281,6 @@
 #![feature(cfg_target_thread_local)]
 #![feature(cfi_encoding)]
 #![feature(char_max_len)]
-#![feature(concat_idents)]
 #![feature(core_float_math)]
 #![feature(decl_macro)]
 #![feature(deprecated_suggestion)]
@@ -717,10 +716,9 @@ pub use core::primitive;
 pub use core::todo;
 // Re-export built-in macros defined through core.
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
-#[allow(deprecated)]
 pub use core::{
-    assert, assert_matches, cfg, column, compile_error, concat, concat_idents, const_format_args,
-    env, file, format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax,
+    assert, assert_matches, cfg, column, compile_error, concat, const_format_args, env, file,
+    format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax,
     module_path, option_env, stringify, trace_macros,
 };
 // Re-export macros defined in core.
diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs
index f008d42804c..25e2b7ea137 100644
--- a/library/std/src/macros.rs
+++ b/library/std/src/macros.rs
@@ -363,7 +363,14 @@ macro_rules! dbg {
         match $val {
             tmp => {
                 $crate::eprintln!("[{}:{}:{}] {} = {:#?}",
-                    $crate::file!(), $crate::line!(), $crate::column!(), $crate::stringify!($val), &tmp);
+                    $crate::file!(),
+                    $crate::line!(),
+                    $crate::column!(),
+                    $crate::stringify!($val),
+                    // The `&T: Debug` check happens here (not in the format literal desugaring)
+                    // to avoid format literal related messages and suggestions.
+                    &&tmp as &dyn $crate::fmt::Debug,
+                );
                 tmp
             }
         }
diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs
index 6a951426407..10685b49319 100644
--- a/library/std/src/net/tcp.rs
+++ b/library/std/src/net/tcp.rs
@@ -53,6 +53,12 @@ use crate::time::Duration;
 ///     Ok(())
 /// } // the stream is closed here
 /// ```
+///
+/// # Platform-specific Behavior
+///
+/// On Unix, writes to the underlying socket in `SOCK_STREAM` mode are made with
+/// `MSG_NOSIGNAL` flag. This suppresses the emission of the  `SIGPIPE` signal when writing
+/// to disconnected socket. In some cases, getting a `SIGPIPE` would trigger process termination.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct TcpStream(net_imp::TcpStream);
 
diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs
index 5802b653965..78c957270c4 100644
--- a/library/std/src/os/unix/mod.rs
+++ b/library/std/src/os/unix/mod.rs
@@ -116,6 +116,9 @@ pub mod prelude {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
     #[doc(no_inline)]
+    #[unstable(feature = "unix_send_signal", issue = "141975")]
+    pub use super::process::ChildExt;
+    #[doc(no_inline)]
     #[stable(feature = "rust1", since = "1.0.0")]
     pub use super::process::{CommandExt, ExitStatusExt};
     #[doc(no_inline)]
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index 1bd3bab5e37..035768a6fab 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -1,3 +1,18 @@
+cfg_if::cfg_if! {
+    if #[cfg(any(
+        target_os = "linux", target_os = "android",
+        target_os = "hurd",
+        target_os = "dragonfly", target_os = "freebsd",
+        target_os = "openbsd", target_os = "netbsd",
+        target_os = "solaris", target_os = "illumos",
+        target_os = "haiku", target_os = "nto",
+        target_os = "cygwin"))] {
+        use libc::MSG_NOSIGNAL;
+    } else {
+        const MSG_NOSIGNAL: core::ffi::c_int = 0x0;
+    }
+}
+
 use super::{SocketAddr, sockaddr_un};
 #[cfg(any(doc, target_os = "android", target_os = "linux"))]
 use super::{SocketAncillary, recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to};
@@ -41,6 +56,12 @@ use crate::time::Duration;
 ///     Ok(())
 /// }
 /// ```
+///
+/// # `SIGPIPE`
+///
+/// Writes to the underlying socket in `SOCK_STREAM` mode are made with `MSG_NOSIGNAL` flag.
+/// This suppresses the emission of the  `SIGPIPE` signal when writing to disconnected socket.
+/// In some cases getting a `SIGPIPE` would trigger process termination.
 #[stable(feature = "unix_socket", since = "1.10.0")]
 pub struct UnixStream(pub(super) Socket);
 
@@ -633,7 +654,7 @@ impl io::Write for UnixStream {
 #[stable(feature = "unix_socket", since = "1.10.0")]
 impl<'a> io::Write for &'a UnixStream {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.0.write(buf)
+        self.0.send_with_flags(buf, MSG_NOSIGNAL)
     }
 
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index 57ce3c5a4bf..3f4fe2e56ec 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -378,6 +378,41 @@ impl ExitStatusExt for process::ExitStatusError {
     }
 }
 
+#[unstable(feature = "unix_send_signal", issue = "141975")]
+pub trait ChildExt: Sealed {
+    /// Sends a signal to a child process.
+    ///
+    /// # Errors
+    ///
+    /// This function will return an error if the signal is invalid. The integer values associated
+    /// with signals are implemenation-specific, so it's encouraged to use a crate that provides
+    /// posix bindings.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(unix_send_signal)]
+    ///
+    /// use std::{io, os::unix::process::ChildExt, process::{Command, Stdio}};
+    ///
+    /// use libc::SIGTERM;
+    ///
+    /// fn main() -> io::Result<()> {
+    ///     let child = Command::new("cat").stdin(Stdio::piped()).spawn()?;
+    ///     child.send_signal(SIGTERM)?;
+    ///     Ok(())
+    /// }
+    /// ```
+    fn send_signal(&self, signal: i32) -> io::Result<()>;
+}
+
+#[unstable(feature = "unix_send_signal", issue = "141975")]
+impl ChildExt for process::Child {
+    fn send_signal(&self, signal: i32) -> io::Result<()> {
+        self.handle.send_signal(signal)
+    }
+}
+
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl FromRawFd for process::Stdio {
     #[inline]
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 0469db0814c..07f212b1135 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -1316,8 +1316,17 @@ impl PathBuf {
             need_sep = false
         }
 
+        let need_clear = if cfg!(target_os = "cygwin") {
+            // If path is absolute and its prefix is none, it is like `/foo`,
+            // and will be handled below.
+            path.prefix().is_some()
+        } else {
+            // On Unix: prefix is always None.
+            path.is_absolute() || path.prefix().is_some()
+        };
+
         // absolute `path` replaces `self`
-        if path.is_absolute() || path.prefix().is_some() {
+        if need_clear {
             self.inner.truncate(0);
 
         // verbatim paths need . and .. removed
@@ -3643,6 +3652,11 @@ impl Error for NormalizeError {}
 /// paths, this is currently equivalent to calling
 /// [`GetFullPathNameW`][windows-path].
 ///
+/// On Cygwin, this is currently equivalent to calling [`cygwin_conv_path`][cygwin-path]
+/// with mode `CCP_WIN_A_TO_POSIX`, and then being processed like other POSIX platforms.
+/// If a Windows path is given, it will be converted to an absolute POSIX path without
+/// keeping `..`.
+///
 /// Note that these [may change in the future][changes].
 ///
 /// # Errors
@@ -3700,6 +3714,7 @@ impl Error for NormalizeError {}
 /// [changes]: io#platform-specific-behavior
 /// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
 /// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
+/// [cygwin-path]: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html
 #[stable(feature = "absolute_path", since = "1.79.0")]
 pub fn absolute<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
     let path = path.as_ref();
diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs
index c15d8c40085..69f03353153 100644
--- a/library/std/src/prelude/v1.rs
+++ b/library/std/src/prelude/v1.rs
@@ -45,10 +45,9 @@ pub use crate::result::Result::{self, Err, Ok};
 
 // Re-exported built-in macros
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
-#[allow(deprecated)]
 #[doc(no_inline)]
 pub use core::prelude::v1::{
-    assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args,
+    assert, cfg, column, compile_error, concat, env, file, format_args,
     format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env,
     stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd,
 };
diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs
index a95709b4891..9b674a25165 100644
--- a/library/std/src/sys/fs/windows.rs
+++ b/library/std/src/sys/fs/windows.rs
@@ -132,6 +132,7 @@ impl Iterator for ReadDir {
             let mut wfd = mem::zeroed();
             loop {
                 if c::FindNextFileW(handle.0, &mut wfd) == 0 {
+                    self.handle = None;
                     match api::get_last_error() {
                         WinError::NO_MORE_FILES => return None,
                         WinError { code } => {
diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs
index b35d5d2aa84..b2a4961c3c5 100644
--- a/library/std/src/sys/net/connection/socket/unix.rs
+++ b/library/std/src/sys/net/connection/socket/unix.rs
@@ -281,6 +281,14 @@ impl Socket {
         self.0.duplicate().map(Socket)
     }
 
+    pub fn send_with_flags(&self, buf: &[u8], flags: c_int) -> io::Result<usize> {
+        let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t;
+        let ret = cvt(unsafe {
+            libc::send(self.as_raw_fd(), buf.as_ptr() as *const c_void, len, flags)
+        })?;
+        Ok(ret as usize)
+    }
+
     fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> {
         let ret = cvt(unsafe {
             libc::recv(
diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs
index 2d949ec9e91..47e9a616bfd 100644
--- a/library/std/src/sys/pal/unix/linux/pidfd.rs
+++ b/library/std/src/sys/pal/unix/linux/pidfd.rs
@@ -13,11 +13,15 @@ pub(crate) struct PidFd(FileDesc);
 
 impl PidFd {
     pub fn kill(&self) -> io::Result<()> {
+        self.send_signal(libc::SIGKILL)
+    }
+
+    pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
         cvt(unsafe {
             libc::syscall(
                 libc::SYS_pidfd_send_signal,
                 self.0.as_raw_fd(),
-                libc::SIGKILL,
+                signal,
                 crate::ptr::null::<()>(),
                 0,
             )
diff --git a/library/std/src/sys/path/cygwin.rs b/library/std/src/sys/path/cygwin.rs
new file mode 100644
index 00000000000..e90372805bb
--- /dev/null
+++ b/library/std/src/sys/path/cygwin.rs
@@ -0,0 +1,92 @@
+use crate::ffi::OsString;
+use crate::os::unix::ffi::OsStringExt;
+use crate::path::{Path, PathBuf};
+use crate::sys::common::small_c_string::run_path_with_cstr;
+use crate::sys::cvt;
+use crate::{io, ptr};
+
+#[inline]
+pub fn is_sep_byte(b: u8) -> bool {
+    b == b'/' || b == b'\\'
+}
+
+/// Cygwin allways prefers `/` over `\`, and it always converts all `/` to `\`
+/// internally when calling Win32 APIs. Therefore, the server component of path
+/// `\\?\UNC\localhost/share` is `localhost/share` on Win32, but `localhost`
+/// on Cygwin.
+#[inline]
+pub fn is_verbatim_sep(b: u8) -> bool {
+    b == b'/' || b == b'\\'
+}
+
+pub use super::windows_prefix::parse_prefix;
+
+pub const MAIN_SEP_STR: &str = "/";
+pub const MAIN_SEP: char = '/';
+
+unsafe extern "C" {
+    // Doc: https://cygwin.com/cygwin-api/func-cygwin-conv-path.html
+    // Src: https://github.com/cygwin/cygwin/blob/718a15ba50e0d01c79800bd658c2477f9a603540/winsup/cygwin/path.cc#L3902
+    // Safety:
+    // * `what` should be `CCP_WIN_A_TO_POSIX` here
+    // * `from` is null-terminated UTF-8 path
+    // * `to` is buffer, the buffer size is `size`.
+    //
+    // Converts a path to an absolute POSIX path, no matter the input is Win32 path or POSIX path.
+    fn cygwin_conv_path(
+        what: libc::c_uint,
+        from: *const libc::c_char,
+        to: *mut u8,
+        size: libc::size_t,
+    ) -> libc::ssize_t;
+}
+
+const CCP_WIN_A_TO_POSIX: libc::c_uint = 2;
+
+/// Make a POSIX path absolute.
+pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
+    run_path_with_cstr(path, &|path| {
+        let conv = CCP_WIN_A_TO_POSIX;
+        let size = cvt(unsafe { cygwin_conv_path(conv, path.as_ptr(), ptr::null_mut(), 0) })?;
+        // If success, size should not be 0.
+        debug_assert!(size >= 1);
+        let size = size as usize;
+        let mut buffer = Vec::with_capacity(size);
+        cvt(unsafe { cygwin_conv_path(conv, path.as_ptr(), buffer.as_mut_ptr(), size) })?;
+        unsafe {
+            buffer.set_len(size - 1);
+        }
+        Ok(PathBuf::from(OsString::from_vec(buffer)))
+    })
+    .map(|path| {
+        if path.prefix().is_some() {
+            return path;
+        }
+
+        // From unix.rs
+        let mut components = path.components();
+        let path_os = path.as_os_str().as_encoded_bytes();
+
+        let mut normalized = if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
+            components.next();
+            PathBuf::from("//")
+        } else {
+            PathBuf::new()
+        };
+        normalized.extend(components);
+
+        if path_os.ends_with(b"/") {
+            normalized.push("");
+        }
+
+        normalized
+    })
+}
+
+pub(crate) fn is_absolute(path: &Path) -> bool {
+    if path.as_os_str().as_encoded_bytes().starts_with(b"\\") {
+        path.has_root() && path.prefix().is_some()
+    } else {
+        path.has_root()
+    }
+}
diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs
index 1fa4e80d678..a4ff4338cf5 100644
--- a/library/std/src/sys/path/mod.rs
+++ b/library/std/src/sys/path/mod.rs
@@ -1,6 +1,7 @@
 cfg_if::cfg_if! {
     if #[cfg(target_os = "windows")] {
         mod windows;
+        mod windows_prefix;
         pub use windows::*;
     } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
         mod sgx;
@@ -11,6 +12,10 @@ cfg_if::cfg_if! {
     } else if #[cfg(target_os = "uefi")] {
         mod uefi;
         pub use uefi::*;
+    } else if #[cfg(target_os = "cygwin")] {
+        mod cygwin;
+        mod windows_prefix;
+        pub use cygwin::*;
     } else {
         mod unix;
         pub use unix::*;
diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs
index e0e003f6a81..f124e1e5a71 100644
--- a/library/std/src/sys/path/windows.rs
+++ b/library/std/src/sys/path/windows.rs
@@ -1,5 +1,5 @@
 use crate::ffi::{OsStr, OsString};
-use crate::path::{Path, PathBuf, Prefix};
+use crate::path::{Path, PathBuf};
 use crate::sys::api::utf16;
 use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s};
 use crate::{io, ptr};
@@ -7,6 +7,8 @@ use crate::{io, ptr};
 #[cfg(test)]
 mod tests;
 
+pub use super::windows_prefix::parse_prefix;
+
 pub const MAIN_SEP_STR: &str = "\\";
 pub const MAIN_SEP: char = '\\';
 
@@ -77,177 +79,6 @@ pub(crate) fn append_suffix(path: PathBuf, suffix: &OsStr) -> PathBuf {
     path.into()
 }
 
-struct PrefixParser<'a, const LEN: usize> {
-    path: &'a OsStr,
-    prefix: [u8; LEN],
-}
-
-impl<'a, const LEN: usize> PrefixParser<'a, LEN> {
-    #[inline]
-    fn get_prefix(path: &OsStr) -> [u8; LEN] {
-        let mut prefix = [0; LEN];
-        // SAFETY: Only ASCII characters are modified.
-        for (i, &ch) in path.as_encoded_bytes().iter().take(LEN).enumerate() {
-            prefix[i] = if ch == b'/' { b'\\' } else { ch };
-        }
-        prefix
-    }
-
-    fn new(path: &'a OsStr) -> Self {
-        Self { path, prefix: Self::get_prefix(path) }
-    }
-
-    fn as_slice(&self) -> PrefixParserSlice<'a, '_> {
-        PrefixParserSlice {
-            path: self.path,
-            prefix: &self.prefix[..LEN.min(self.path.len())],
-            index: 0,
-        }
-    }
-}
-
-struct PrefixParserSlice<'a, 'b> {
-    path: &'a OsStr,
-    prefix: &'b [u8],
-    index: usize,
-}
-
-impl<'a> PrefixParserSlice<'a, '_> {
-    fn strip_prefix(&self, prefix: &str) -> Option<Self> {
-        self.prefix[self.index..]
-            .starts_with(prefix.as_bytes())
-            .then_some(Self { index: self.index + prefix.len(), ..*self })
-    }
-
-    fn prefix_bytes(&self) -> &'a [u8] {
-        &self.path.as_encoded_bytes()[..self.index]
-    }
-
-    fn finish(self) -> &'a OsStr {
-        // SAFETY: The unsafety here stems from converting between &OsStr and
-        // &[u8] and back. This is safe to do because (1) we only look at ASCII
-        // contents of the encoding and (2) new &OsStr values are produced only
-        // from ASCII-bounded slices of existing &OsStr values.
-        unsafe { OsStr::from_encoded_bytes_unchecked(&self.path.as_encoded_bytes()[self.index..]) }
-    }
-}
-
-pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
-    use Prefix::{DeviceNS, Disk, UNC, Verbatim, VerbatimDisk, VerbatimUNC};
-
-    let parser = PrefixParser::<8>::new(path);
-    let parser = parser.as_slice();
-    if let Some(parser) = parser.strip_prefix(r"\\") {
-        // \\
-
-        // The meaning of verbatim paths can change when they use a different
-        // separator.
-        if let Some(parser) = parser.strip_prefix(r"?\")
-            && !parser.prefix_bytes().iter().any(|&x| x == b'/')
-        {
-            // \\?\
-            if let Some(parser) = parser.strip_prefix(r"UNC\") {
-                // \\?\UNC\server\share
-
-                let path = parser.finish();
-                let (server, path) = parse_next_component(path, true);
-                let (share, _) = parse_next_component(path, true);
-
-                Some(VerbatimUNC(server, share))
-            } else {
-                let path = parser.finish();
-
-                // in verbatim paths only recognize an exact drive prefix
-                if let Some(drive) = parse_drive_exact(path) {
-                    // \\?\C:
-                    Some(VerbatimDisk(drive))
-                } else {
-                    // \\?\prefix
-                    let (prefix, _) = parse_next_component(path, true);
-                    Some(Verbatim(prefix))
-                }
-            }
-        } else if let Some(parser) = parser.strip_prefix(r".\") {
-            // \\.\COM42
-            let path = parser.finish();
-            let (prefix, _) = parse_next_component(path, false);
-            Some(DeviceNS(prefix))
-        } else {
-            let path = parser.finish();
-            let (server, path) = parse_next_component(path, false);
-            let (share, _) = parse_next_component(path, false);
-
-            if !server.is_empty() && !share.is_empty() {
-                // \\server\share
-                Some(UNC(server, share))
-            } else {
-                // no valid prefix beginning with "\\" recognized
-                None
-            }
-        }
-    } else {
-        // If it has a drive like `C:` then it's a disk.
-        // Otherwise there is no prefix.
-        parse_drive(path).map(Disk)
-    }
-}
-
-// Parses a drive prefix, e.g. "C:" and "C:\whatever"
-fn parse_drive(path: &OsStr) -> Option<u8> {
-    // In most DOS systems, it is not possible to have more than 26 drive letters.
-    // See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
-    fn is_valid_drive_letter(drive: &u8) -> bool {
-        drive.is_ascii_alphabetic()
-    }
-
-    match path.as_encoded_bytes() {
-        [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()),
-        _ => None,
-    }
-}
-
-// Parses a drive prefix exactly, e.g. "C:"
-fn parse_drive_exact(path: &OsStr) -> Option<u8> {
-    // only parse two bytes: the drive letter and the drive separator
-    if path.as_encoded_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) {
-        parse_drive(path)
-    } else {
-        None
-    }
-}
-
-// Parse the next path component.
-//
-// Returns the next component and the rest of the path excluding the component and separator.
-// Does not recognize `/` as a separator character if `verbatim` is true.
-fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
-    let separator = if verbatim { is_verbatim_sep } else { is_sep_byte };
-
-    match path.as_encoded_bytes().iter().position(|&x| separator(x)) {
-        Some(separator_start) => {
-            let separator_end = separator_start + 1;
-
-            let component = &path.as_encoded_bytes()[..separator_start];
-
-            // Panic safe
-            // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index.
-            let path = &path.as_encoded_bytes()[separator_end..];
-
-            // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\')
-            // is encoded in a single byte, therefore `bytes[separator_start]` and
-            // `bytes[separator_end]` must be code point boundaries and thus
-            // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices.
-            unsafe {
-                (
-                    OsStr::from_encoded_bytes_unchecked(component),
-                    OsStr::from_encoded_bytes_unchecked(path),
-                )
-            }
-        }
-        None => (path, OsStr::new("")),
-    }
-}
-
 /// Returns a UTF-16 encoded path capable of bypassing the legacy `MAX_PATH` limits.
 ///
 /// This path may or may not have a verbatim prefix.
diff --git a/library/std/src/sys/path/windows/tests.rs b/library/std/src/sys/path/windows/tests.rs
index 9eb79203dca..830f48d7bfc 100644
--- a/library/std/src/sys/path/windows/tests.rs
+++ b/library/std/src/sys/path/windows/tests.rs
@@ -1,4 +1,6 @@
+use super::super::windows_prefix::*;
 use super::*;
+use crate::path::Prefix;
 
 #[test]
 fn test_parse_next_component() {
diff --git a/library/std/src/sys/path/windows_prefix.rs b/library/std/src/sys/path/windows_prefix.rs
new file mode 100644
index 00000000000..b9dfe754485
--- /dev/null
+++ b/library/std/src/sys/path/windows_prefix.rs
@@ -0,0 +1,182 @@
+//! Parse Windows prefixes, for both Windows and Cygwin.
+
+use super::{is_sep_byte, is_verbatim_sep};
+use crate::ffi::OsStr;
+use crate::path::Prefix;
+
+struct PrefixParser<'a, const LEN: usize> {
+    path: &'a OsStr,
+    prefix: [u8; LEN],
+}
+
+impl<'a, const LEN: usize> PrefixParser<'a, LEN> {
+    #[inline]
+    fn get_prefix(path: &OsStr) -> [u8; LEN] {
+        let mut prefix = [0; LEN];
+        // SAFETY: Only ASCII characters are modified.
+        for (i, &ch) in path.as_encoded_bytes().iter().take(LEN).enumerate() {
+            prefix[i] = if ch == b'/' { b'\\' } else { ch };
+        }
+        prefix
+    }
+
+    fn new(path: &'a OsStr) -> Self {
+        Self { path, prefix: Self::get_prefix(path) }
+    }
+
+    fn as_slice(&self) -> PrefixParserSlice<'a, '_> {
+        PrefixParserSlice {
+            path: self.path,
+            prefix: &self.prefix[..LEN.min(self.path.len())],
+            index: 0,
+        }
+    }
+}
+
+struct PrefixParserSlice<'a, 'b> {
+    path: &'a OsStr,
+    prefix: &'b [u8],
+    index: usize,
+}
+
+impl<'a> PrefixParserSlice<'a, '_> {
+    fn strip_prefix(&self, prefix: &str) -> Option<Self> {
+        self.prefix[self.index..]
+            .starts_with(prefix.as_bytes())
+            .then_some(Self { index: self.index + prefix.len(), ..*self })
+    }
+
+    fn prefix_bytes(&self) -> &'a [u8] {
+        &self.path.as_encoded_bytes()[..self.index]
+    }
+
+    fn finish(self) -> &'a OsStr {
+        // SAFETY: The unsafety here stems from converting between &OsStr and
+        // &[u8] and back. This is safe to do because (1) we only look at ASCII
+        // contents of the encoding and (2) new &OsStr values are produced only
+        // from ASCII-bounded slices of existing &OsStr values.
+        unsafe { OsStr::from_encoded_bytes_unchecked(&self.path.as_encoded_bytes()[self.index..]) }
+    }
+}
+
+pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
+    use Prefix::{DeviceNS, Disk, UNC, Verbatim, VerbatimDisk, VerbatimUNC};
+
+    let parser = PrefixParser::<8>::new(path);
+    let parser = parser.as_slice();
+    if let Some(parser) = parser.strip_prefix(r"\\") {
+        // \\
+
+        // It's a POSIX path.
+        if cfg!(target_os = "cygwin") && !path.as_encoded_bytes().iter().any(|&x| x == b'\\') {
+            return None;
+        }
+
+        // The meaning of verbatim paths can change when they use a different
+        // separator.
+        if let Some(parser) = parser.strip_prefix(r"?\")
+            // Cygwin allows `/` in verbatim paths.
+            && (cfg!(target_os = "cygwin") || !parser.prefix_bytes().iter().any(|&x| x == b'/'))
+        {
+            // \\?\
+            if let Some(parser) = parser.strip_prefix(r"UNC\") {
+                // \\?\UNC\server\share
+
+                let path = parser.finish();
+                let (server, path) = parse_next_component(path, true);
+                let (share, _) = parse_next_component(path, true);
+
+                Some(VerbatimUNC(server, share))
+            } else {
+                let path = parser.finish();
+
+                // in verbatim paths only recognize an exact drive prefix
+                if let Some(drive) = parse_drive_exact(path) {
+                    // \\?\C:
+                    Some(VerbatimDisk(drive))
+                } else {
+                    // \\?\prefix
+                    let (prefix, _) = parse_next_component(path, true);
+                    Some(Verbatim(prefix))
+                }
+            }
+        } else if let Some(parser) = parser.strip_prefix(r".\") {
+            // \\.\COM42
+            let path = parser.finish();
+            let (prefix, _) = parse_next_component(path, false);
+            Some(DeviceNS(prefix))
+        } else {
+            let path = parser.finish();
+            let (server, path) = parse_next_component(path, false);
+            let (share, _) = parse_next_component(path, false);
+
+            if !server.is_empty() && !share.is_empty() {
+                // \\server\share
+                Some(UNC(server, share))
+            } else {
+                // no valid prefix beginning with "\\" recognized
+                None
+            }
+        }
+    } else {
+        // If it has a drive like `C:` then it's a disk.
+        // Otherwise there is no prefix.
+        Some(Disk(parse_drive(path)?))
+    }
+}
+
+// Parses a drive prefix, e.g. "C:" and "C:\whatever"
+fn parse_drive(path: &OsStr) -> Option<u8> {
+    // In most DOS systems, it is not possible to have more than 26 drive letters.
+    // See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
+    fn is_valid_drive_letter(drive: &u8) -> bool {
+        drive.is_ascii_alphabetic()
+    }
+
+    match path.as_encoded_bytes() {
+        [drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()),
+        _ => None,
+    }
+}
+
+// Parses a drive prefix exactly, e.g. "C:"
+fn parse_drive_exact(path: &OsStr) -> Option<u8> {
+    // only parse two bytes: the drive letter and the drive separator
+    if path.as_encoded_bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) {
+        parse_drive(path)
+    } else {
+        None
+    }
+}
+
+// Parse the next path component.
+//
+// Returns the next component and the rest of the path excluding the component and separator.
+// Does not recognize `/` as a separator character on Windows if `verbatim` is true.
+pub(crate) fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
+    let separator = if verbatim { is_verbatim_sep } else { is_sep_byte };
+
+    match path.as_encoded_bytes().iter().position(|&x| separator(x)) {
+        Some(separator_start) => {
+            let separator_end = separator_start + 1;
+
+            let component = &path.as_encoded_bytes()[..separator_start];
+
+            // Panic safe
+            // The max `separator_end` is `bytes.len()` and `bytes[bytes.len()..]` is a valid index.
+            let path = &path.as_encoded_bytes()[separator_end..];
+
+            // SAFETY: `path` is a valid wtf8 encoded slice and each of the separators ('/', '\')
+            // is encoded in a single byte, therefore `bytes[separator_start]` and
+            // `bytes[separator_end]` must be code point boundaries and thus
+            // `bytes[..separator_start]` and `bytes[separator_end..]` are valid wtf8 slices.
+            unsafe {
+                (
+                    OsStr::from_encoded_bytes_unchecked(component),
+                    OsStr::from_encoded_bytes_unchecked(path),
+                )
+            }
+        }
+        None => (path, OsStr::new("")),
+    }
+}
diff --git a/library/std/src/sys/process/unix/fuchsia.rs b/library/std/src/sys/process/unix/fuchsia.rs
index 017ab91797c..d71be510b6a 100644
--- a/library/std/src/sys/process/unix/fuchsia.rs
+++ b/library/std/src/sys/process/unix/fuchsia.rs
@@ -152,6 +152,11 @@ impl Process {
         Ok(())
     }
 
+    pub fn send_signal(&self, _signal: i32) -> io::Result<()> {
+        // Fuchsia doesn't have a direct equivalent for signals
+        unimplemented!()
+    }
+
     pub fn wait(&mut self) -> io::Result<ExitStatus> {
         let mut proc_info: zx_info_process_t = Default::default();
         let mut actual: size_t = 0;
diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs
index 4f595ac9a1c..1fe80c13b81 100644
--- a/library/std/src/sys/process/unix/unix.rs
+++ b/library/std/src/sys/process/unix/unix.rs
@@ -963,9 +963,13 @@ impl Process {
         self.pid as u32
     }
 
-    pub fn kill(&mut self) -> io::Result<()> {
+    pub fn kill(&self) -> io::Result<()> {
+        self.send_signal(libc::SIGKILL)
+    }
+
+    pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
         // If we've already waited on this process then the pid can be recycled
-        // and used for another process, and we probably shouldn't be killing
+        // and used for another process, and we probably shouldn't be signaling
         // random processes, so return Ok because the process has exited already.
         if self.status.is_some() {
             return Ok(());
@@ -973,9 +977,9 @@ impl Process {
         #[cfg(target_os = "linux")]
         if let Some(pid_fd) = self.pidfd.as_ref() {
             // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too
-            return pid_fd.kill();
+            return pid_fd.send_signal(signal);
         }
-        cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+        cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
     }
 
     pub fn wait(&mut self) -> io::Result<ExitStatus> {
diff --git a/library/std/src/sys/process/unix/unsupported.rs b/library/std/src/sys/process/unix/unsupported.rs
index e86561a5c5c..87403cd50f8 100644
--- a/library/std/src/sys/process/unix/unsupported.rs
+++ b/library/std/src/sys/process/unix/unsupported.rs
@@ -40,7 +40,11 @@ impl Process {
         0
     }
 
-    pub fn kill(&mut self) -> io::Result<()> {
+    pub fn kill(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn send_signal(&self, _signal: i32) -> io::Result<()> {
         unsupported()
     }
 
diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs
index f33b4a375da..51ae8c56bdb 100644
--- a/library/std/src/sys/process/unix/vxworks.rs
+++ b/library/std/src/sys/process/unix/vxworks.rs
@@ -146,14 +146,18 @@ impl Process {
         self.pid as u32
     }
 
-    pub fn kill(&mut self) -> io::Result<()> {
+    pub fn kill(&self) -> io::Result<()> {
+        self.send_signal(libc::SIGKILL)
+    }
+
+    pub fn send_signal(&self, signal: i32) -> io::Result<()> {
         // If we've already waited on this process then the pid can be recycled
         // and used for another process, and we probably shouldn't be killing
         // random processes, so return Ok because the process has exited already.
         if self.status.is_some() {
             Ok(())
         } else {
-            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
+            cvt(unsafe { libc::kill(self.pid, signal) }).map(drop)
         }
     }
 
diff --git a/library/std/src/sys/random/uefi.rs b/library/std/src/sys/random/uefi.rs
index a4d29e66f38..5f001f0f532 100644
--- a/library/std/src/sys/random/uefi.rs
+++ b/library/std/src/sys/random/uefi.rs
@@ -1,27 +1,158 @@
-use r_efi::protocols::rng;
+pub fn fill_bytes(bytes: &mut [u8]) {
+    // Handle zero-byte request
+    if bytes.is_empty() {
+        return;
+    }
+
+    // Try EFI_RNG_PROTOCOL
+    if rng_protocol::fill_bytes(bytes) {
+        return;
+    }
 
-use crate::sys::pal::helpers;
+    // Fallback to rdrand if rng protocol missing.
+    //
+    // For real-world example, see [issue-13825](https://github.com/rust-lang/rust/issues/138252#issuecomment-2891270323)
+    #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
+    if rdrand::fill_bytes(bytes) {
+        return;
+    }
 
-pub fn fill_bytes(bytes: &mut [u8]) {
-    let handles =
-        helpers::locate_handles(rng::PROTOCOL_GUID).expect("failed to generate random data");
-    for handle in handles {
-        if let Ok(protocol) = helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) {
-            let r = unsafe {
-                ((*protocol.as_ptr()).get_rng)(
-                    protocol.as_ptr(),
-                    crate::ptr::null_mut(),
-                    bytes.len(),
-                    bytes.as_mut_ptr(),
-                )
+    panic!("failed to generate random data");
+}
+
+mod rng_protocol {
+    use r_efi::protocols::rng;
+
+    use crate::sys::pal::helpers;
+
+    pub(crate) fn fill_bytes(bytes: &mut [u8]) -> bool {
+        if let Ok(handles) = helpers::locate_handles(rng::PROTOCOL_GUID) {
+            for handle in handles {
+                if let Ok(protocol) =
+                    helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID)
+                {
+                    let r = unsafe {
+                        ((*protocol.as_ptr()).get_rng)(
+                            protocol.as_ptr(),
+                            crate::ptr::null_mut(),
+                            bytes.len(),
+                            bytes.as_mut_ptr(),
+                        )
+                    };
+                    if r.is_error() {
+                        continue;
+                    } else {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        false
+    }
+}
+
+/// Port from [getrandom](https://github.com/rust-random/getrandom/blob/master/src/backends/rdrand.rs)
+#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
+mod rdrand {
+    cfg_if::cfg_if! {
+        if #[cfg(target_arch = "x86_64")] {
+            use crate::arch::x86_64 as arch;
+            use arch::_rdrand64_step as rdrand_step;
+            type Word = u64;
+        } else if #[cfg(target_arch = "x86")] {
+            use crate::arch::x86 as arch;
+            use arch::_rdrand32_step as rdrand_step;
+            type Word = u32;
+        }
+    }
+
+    static RDRAND_GOOD: crate::sync::LazyLock<bool> = crate::sync::LazyLock::new(is_rdrand_good);
+
+    // Recommendation from "Intel® Digital Random Number Generator (DRNG) Software
+    // Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures
+    // Software Developer’s Manual" - Volume 1 - Section 7.3.17.1.
+    const RETRY_LIMIT: usize = 10;
+
+    unsafe fn rdrand() -> Option<Word> {
+        for _ in 0..RETRY_LIMIT {
+            let mut val = 0;
+            if unsafe { rdrand_step(&mut val) } == 1 {
+                return Some(val);
+            }
+        }
+        None
+    }
+
+    // Run a small self-test to make sure we aren't repeating values
+    // Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c
+    // Fails with probability < 2^(-90) on 32-bit systems
+    unsafe fn self_test() -> bool {
+        // On AMD, RDRAND returns 0xFF...FF on failure, count it as a collision.
+        let mut prev = Word::MAX;
+        let mut fails = 0;
+        for _ in 0..8 {
+            match unsafe { rdrand() } {
+                Some(val) if val == prev => fails += 1,
+                Some(val) => prev = val,
+                None => return false,
             };
-            if r.is_error() {
-                continue;
-            } else {
-                return;
+        }
+        fails <= 2
+    }
+
+    fn is_rdrand_good() -> bool {
+        #[cfg(not(target_feature = "rdrand"))]
+        {
+            // SAFETY: All Rust x86 targets are new enough to have CPUID, and we
+            // check that leaf 1 is supported before using it.
+            let cpuid0 = unsafe { arch::__cpuid(0) };
+            if cpuid0.eax < 1 {
+                return false;
+            }
+            let cpuid1 = unsafe { arch::__cpuid(1) };
+
+            let vendor_id =
+                [cpuid0.ebx.to_le_bytes(), cpuid0.edx.to_le_bytes(), cpuid0.ecx.to_le_bytes()];
+            if vendor_id == [*b"Auth", *b"enti", *b"cAMD"] {
+                let mut family = (cpuid1.eax >> 8) & 0xF;
+                if family == 0xF {
+                    family += (cpuid1.eax >> 20) & 0xFF;
+                }
+                // AMD CPUs families before 17h (Zen) sometimes fail to set CF when
+                // RDRAND fails after suspend. Don't use RDRAND on those families.
+                // See https://bugzilla.redhat.com/show_bug.cgi?id=1150286
+                if family < 0x17 {
+                    return false;
+                }
+            }
+
+            const RDRAND_FLAG: u32 = 1 << 30;
+            if cpuid1.ecx & RDRAND_FLAG == 0 {
+                return false;
             }
         }
+
+        // SAFETY: We have already checked that rdrand is available.
+        unsafe { self_test() }
     }
 
-    panic!("failed to generate random data");
+    unsafe fn rdrand_exact(dest: &mut [u8]) -> Option<()> {
+        let mut chunks = dest.array_chunks_mut();
+        for chunk in &mut chunks {
+            *chunk = unsafe { rdrand() }?.to_ne_bytes();
+        }
+
+        let tail = chunks.into_remainder();
+        let n = tail.len();
+        if n > 0 {
+            let src = unsafe { rdrand() }?.to_ne_bytes();
+            tail.copy_from_slice(&src[..n]);
+        }
+        Some(())
+    }
+
+    pub(crate) fn fill_bytes(bytes: &mut [u8]) -> bool {
+        if *RDRAND_GOOD { unsafe { rdrand_exact(bytes).is_some() } } else { false }
+    }
 }
diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs
index be0dda1d426..901d2770f20 100644
--- a/library/std/tests/path.rs
+++ b/library/std/tests/path.rs
@@ -1112,6 +1112,473 @@ pub fn test_decompositions_windows() {
     );
 }
 
+// Unix paths are tested in `test_decompositions_unix` above.
+#[test]
+#[cfg(target_os = "cygwin")]
+pub fn test_decompositions_cygwin() {
+    t!("\\",
+    iter: ["/"],
+    has_root: true,
+    is_absolute: false,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("c:",
+    iter: ["c:"],
+    has_root: false,
+    is_absolute: false,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("c:\\",
+    iter: ["c:", "/"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("c:/",
+    iter: ["c:", "/"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("a\\b\\c",
+    iter: ["a", "b", "c"],
+    has_root: false,
+    is_absolute: false,
+    parent: Some("a\\b"),
+    file_name: Some("c"),
+    file_stem: Some("c"),
+    extension: None,
+    file_prefix: Some("c")
+    );
+
+    t!("\\a",
+    iter: ["/", "a"],
+    has_root: true,
+    is_absolute: false,
+    parent: Some("\\"),
+    file_name: Some("a"),
+    file_stem: Some("a"),
+    extension: None,
+    file_prefix: Some("a")
+    );
+
+    t!("c:\\foo.txt",
+    iter: ["c:", "/", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("c:\\"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("\\\\server\\share\\foo.txt",
+    iter: ["\\\\server\\share", "/", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\server\\share\\"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("//server/share\\foo.txt",
+    iter: ["//server/share", "/", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//server/share\\"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("//server/share/foo.txt",
+    iter: ["/", "server", "share", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//server/share"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("\\\\server\\share",
+    iter: ["\\\\server\\share", "/"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("\\\\server",
+    iter: ["/", "server"],
+    has_root: true,
+    is_absolute: false,
+    parent: Some("\\"),
+    file_name: Some("server"),
+    file_stem: Some("server"),
+    extension: None,
+    file_prefix: Some("server")
+    );
+
+    t!("\\\\?\\bar\\foo.txt",
+    iter: ["\\\\?\\bar", "/", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\bar\\"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("\\\\?\\bar",
+    iter: ["\\\\?\\bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("\\\\?\\",
+    iter: ["\\\\?\\"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("\\\\?\\UNC\\server\\share\\foo.txt",
+    iter: ["\\\\?\\UNC\\server\\share", "/", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\UNC\\server\\share\\"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("\\\\?\\UNC\\server/share\\foo.txt",
+    iter: ["\\\\?\\UNC\\server/share", "/", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\UNC\\server/share\\"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("//?/UNC/server\\share/foo.txt",
+    iter: ["//?/UNC/server\\share", "/", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//?/UNC/server\\share/"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("//?/UNC/server/share/foo.txt",
+    iter: ["/", "?", "UNC", "server", "share", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//?/UNC/server/share"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("\\\\?\\UNC\\server",
+    iter: ["\\\\?\\UNC\\server"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("\\\\?\\UNC\\",
+    iter: ["\\\\?\\UNC\\"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("\\\\?\\C:\\foo.txt",
+    iter: ["\\\\?\\C:", "/", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\C:\\"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("//?/C:\\foo.txt",
+    iter: ["//?/C:", "/", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//?/C:\\"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("//?/C:/foo.txt",
+    iter: ["/", "?", "C:", "foo.txt"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//?/C:"),
+    file_name: Some("foo.txt"),
+    file_stem: Some("foo"),
+    extension: Some("txt"),
+    file_prefix: Some("foo")
+    );
+
+    t!("\\\\?\\C:\\",
+    iter: ["\\\\?\\C:", "/"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("\\\\?\\C:",
+    iter: ["\\\\?\\C:"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("\\\\?\\foo/bar",
+    iter: ["\\\\?\\foo", "/", "bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\foo/"),
+    file_name: Some("bar"),
+    file_stem: Some("bar"),
+    extension: None,
+    file_prefix: Some("bar")
+    );
+
+    t!("\\\\?\\C:/foo/bar",
+    iter: ["\\\\?\\C:", "/", "foo", "bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\C:/foo"),
+    file_name: Some("bar"),
+    file_stem: Some("bar"),
+    extension: None,
+    file_prefix: Some("bar")
+    );
+
+    t!("\\\\.\\foo\\bar",
+    iter: ["\\\\.\\foo", "/", "bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\.\\foo\\"),
+    file_name: Some("bar"),
+    file_stem: Some("bar"),
+    extension: None,
+    file_prefix: Some("bar")
+    );
+
+    t!("\\\\.\\foo",
+    iter: ["\\\\.\\foo", "/"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("\\\\.\\foo/bar",
+    iter: ["\\\\.\\foo", "/", "bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\.\\foo/"),
+    file_name: Some("bar"),
+    file_stem: Some("bar"),
+    extension: None,
+    file_prefix: Some("bar")
+    );
+
+    t!("\\\\.\\foo\\bar/baz",
+    iter: ["\\\\.\\foo", "/", "bar", "baz"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\.\\foo\\bar"),
+    file_name: Some("baz"),
+    file_stem: Some("baz"),
+    extension: None,
+    file_prefix: Some("baz")
+    );
+
+    t!("\\\\.\\",
+    iter: ["\\\\.\\", "/"],
+    has_root: true,
+    is_absolute: true,
+    parent: None,
+    file_name: None,
+    file_stem: None,
+    extension: None,
+    file_prefix: None
+    );
+
+    t!("//.\\foo/bar",
+    iter: ["//.\\foo", "/", "bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//.\\foo/"),
+    file_name: Some("bar"),
+    file_stem: Some("bar"),
+    extension: None,
+    file_prefix: Some("bar")
+    );
+
+    t!("\\\\./foo/bar",
+    iter: ["\\\\./foo", "/", "bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\./foo/"),
+    file_name: Some("bar"),
+    file_stem: Some("bar"),
+    extension: None,
+    file_prefix: Some("bar")
+    );
+
+    t!("//./foo\\bar",
+    iter: ["//./foo", "/", "bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//./foo\\"),
+    file_name: Some("bar"),
+    file_stem: Some("bar"),
+    extension: None,
+    file_prefix: Some("bar")
+    );
+
+    t!("//./?/C:/foo/bar",
+    iter: ["/", "?", "C:", "foo", "bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//./?/C:/foo"),
+    file_name: Some("bar"),
+    file_stem: Some("bar"),
+    extension: None,
+    file_prefix: Some("bar")
+    );
+
+    t!("//././../././../?/C:/foo/bar",
+    iter: ["/", "..", "..", "?", "C:", "foo", "bar"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("//././../././../?/C:/foo"),
+    file_name: Some("bar"),
+    file_stem: Some("bar"),
+    extension: None,
+    file_prefix: Some("bar")
+    );
+
+    t!("\\\\?\\a\\b\\",
+    iter: ["\\\\?\\a", "/", "b"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\a\\"),
+    file_name: Some("b"),
+    file_stem: Some("b"),
+    extension: None,
+    file_prefix: Some("b")
+    );
+
+    t!("\\\\?\\C:\\foo.txt.zip",
+    iter: ["\\\\?\\C:", "/", "foo.txt.zip"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\C:\\"),
+    file_name: Some("foo.txt.zip"),
+    file_stem: Some("foo.txt"),
+    extension: Some("zip"),
+    file_prefix: Some("foo")
+    );
+
+    t!("\\\\?\\C:\\.foo.txt.zip",
+    iter: ["\\\\?\\C:", "/", ".foo.txt.zip"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\C:\\"),
+    file_name: Some(".foo.txt.zip"),
+    file_stem: Some(".foo.txt"),
+    extension: Some("zip"),
+    file_prefix: Some(".foo")
+    );
+
+    t!("\\\\?\\C:\\.foo",
+    iter: ["\\\\?\\C:", "/", ".foo"],
+    has_root: true,
+    is_absolute: true,
+    parent: Some("\\\\?\\C:\\"),
+    file_name: Some(".foo"),
+    file_stem: Some(".foo"),
+    extension: None,
+    file_prefix: Some(".foo")
+    );
+}
+
 #[test]
 pub fn test_stem_ext() {
     t!("foo",
@@ -1227,6 +1694,11 @@ pub fn test_push() {
         tp!("/foo/bar", "/", "/");
         tp!("/foo/bar", "/baz", "/baz");
         tp!("/foo/bar", "./baz", "/foo/bar/./baz");
+
+        if cfg!(target_os = "cygwin") {
+            tp!("c:\\", "windows", "c:\\windows");
+            tp!("c:", "windows", "c:windows");
+        }
     } else {
         tp!("", "foo", "foo");
         tp!("foo", "bar", r"foo\bar");
diff --git a/library/std/tests/sync/mpmc.rs b/library/std/tests/sync/mpmc.rs
index 594fc2180d8..bf80ab96a88 100644
--- a/library/std/tests/sync/mpmc.rs
+++ b/library/std/tests/sync/mpmc.rs
@@ -463,12 +463,12 @@ fn oneshot_single_thread_recv_timeout() {
 fn stress_recv_timeout_two_threads() {
     let (tx, rx) = channel();
     let stress = stress_factor() + 50;
-    let timeout = Duration::from_millis(5);
+    let timeout = Duration::from_millis(10);
 
     thread::spawn(move || {
         for i in 0..stress {
             if i % 2 == 0 {
-                thread::sleep(timeout * 2);
+                thread::sleep(timeout * 4);
             }
             tx.send(1usize).unwrap();
         }
diff --git a/src/bootstrap/build.rs b/src/bootstrap/build.rs
index e0e32d31353..d9810e899a0 100644
--- a/src/bootstrap/build.rs
+++ b/src/bootstrap/build.rs
@@ -1,6 +1,7 @@
 use std::env;
 
 fn main() {
+    // this is needed because `HOST` is only available to build scripts.
     let host = env::var("HOST").unwrap();
     println!("cargo:rerun-if-changed=build.rs");
     println!("cargo:rustc-env=BUILD_TRIPLE={host}");
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 0d4d6e0ff54..c077555b906 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function
 import shlex
 import sys
 import os
+import re
 
 rust_dir = os.path.dirname(os.path.abspath(__file__))
 rust_dir = os.path.dirname(rust_dir)
@@ -585,16 +586,31 @@ def parse_example_config(known_args, config):
     section_order = [None]
     targets = {}
     top_level_keys = []
+    comment_lines = []
 
     with open(rust_dir + "/bootstrap.example.toml") as example_config:
         example_lines = example_config.read().split("\n")
     for line in example_lines:
-        if cur_section is None:
-            if line.count("=") == 1:
-                top_level_key = line.split("=")[0]
-                top_level_key = top_level_key.strip(" #")
-                top_level_keys.append(top_level_key)
-        if line.startswith("["):
+        if line.count("=") >= 1 and not line.startswith("# "):
+            key = line.split("=")[0]
+            key = key.strip(" #")
+            parts = key.split(".")
+            if len(parts) > 1:
+                cur_section = parts[0]
+                if cur_section not in sections:
+                    sections[cur_section] = ["[" + cur_section + "]"]
+                    section_order.append(cur_section)
+            elif cur_section is None:
+                top_level_keys.append(key)
+            # put the comment lines within the start of
+            # a new section, not outside it.
+            sections[cur_section] += comment_lines
+            comment_lines = []
+            # remove just the `section.` part from the line, if present.
+            sections[cur_section].append(
+                re.sub("(#?)([a-zA-Z_-]+\\.)?(.*)", "\\1\\3", line)
+            )
+        elif line.startswith("["):
             cur_section = line[1:-1]
             if cur_section.startswith("target"):
                 cur_section = "target"
@@ -605,8 +621,9 @@ def parse_example_config(known_args, config):
             sections[cur_section] = [line]
             section_order.append(cur_section)
         else:
-            sections[cur_section].append(line)
+            comment_lines.append(line)
 
+    sections[cur_section] += comment_lines
     # Fill out the `targets` array by giving all configured targets a copy of the
     # `target` section we just loaded from the example config
     configured_targets = [build(known_args)]
diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index 0671a8467e8..0364c664ba5 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -151,18 +151,6 @@ fn main() {
             cmd.arg("--sysroot").arg(&sysroot);
         }
 
-        // If we're compiling specifically the `panic_abort` crate then we pass
-        // the `-C panic=abort` option. Note that we do not do this for any
-        // other crate intentionally as this is the only crate for now that we
-        // ship with panic=abort.
-        //
-        // This... is a bit of a hack how we detect this. Ideally this
-        // information should be encoded in the crate I guess? Would likely
-        // require an RFC amendment to RFC 1513, however.
-        if crate_name == Some("panic_abort") {
-            cmd.arg("-C").arg("panic=abort");
-        }
-
         let crate_type = parse_value_from_args(&orig_args, "--crate-type");
         // `-Ztls-model=initial-exec` must not be applied to proc-macros, see
         // issue https://github.com/rust-lang/rust/issues/100530
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index f47873590a1..fcd4f4078ad 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -21,13 +21,6 @@ pub struct Std {
     ///
     /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
     crates: Vec<String>,
-    /// Override `Builder::kind` on cargo invocations.
-    ///
-    /// By default, `Builder::kind` is propagated as the subcommand to the cargo invocations.
-    /// However, there are cases when this is not desirable. For example, when running `x clippy $tool_name`,
-    /// passing `Builder::kind` to cargo invocations would run clippy on the entire compiler and library,
-    /// which is not useful if we only want to lint a few crates with specific rules.
-    override_build_kind: Option<Kind>,
     /// Never use this from outside calls. It is intended for internal use only within `check::Std::make_run`
     /// and `check::Std::run`.
     custom_stage: Option<u32>,
@@ -37,12 +30,7 @@ impl Std {
     const CRATE_OR_DEPS: &[&str] = &["sysroot", "coretests", "alloctests"];
 
     pub fn new(target: TargetSelection) -> Self {
-        Self { target, crates: vec![], override_build_kind: None, custom_stage: None }
-    }
-
-    pub fn build_kind(mut self, kind: Option<Kind>) -> Self {
-        self.override_build_kind = kind;
-        self
+        Self { target, crates: vec![], custom_stage: None }
     }
 }
 
@@ -68,12 +56,7 @@ impl Step for Std {
             1
         };
 
-        run.builder.ensure(Std {
-            target: run.target,
-            crates,
-            override_build_kind: None,
-            custom_stage: Some(stage),
-        });
+        run.builder.ensure(Std { target: run.target, crates, custom_stage: Some(stage) });
     }
 
     fn run(self, builder: &Builder<'_>) {
@@ -116,7 +99,7 @@ impl Step for Std {
             Mode::Std,
             SourceType::InTree,
             target,
-            self.override_build_kind.unwrap_or(builder.kind),
+            Kind::Check,
         );
 
         std_cargo(builder, target, compiler.stage, &mut cargo);
@@ -147,9 +130,8 @@ impl Step for Std {
         }
         drop(_guard);
 
-        // don't run on std twice with x.py clippy
         // don't check test dependencies if we haven't built libtest
-        if builder.kind == Kind::Clippy || !self.crates.iter().any(|krate| krate == "test") {
+        if !self.crates.iter().any(|krate| krate == "test") {
             return;
         }
 
@@ -165,7 +147,7 @@ impl Step for Std {
             Mode::Std,
             SourceType::InTree,
             target,
-            self.override_build_kind.unwrap_or(builder.kind),
+            Kind::Check,
         );
 
         // If we're not in stage 0, tests and examples will fail to compile
@@ -199,13 +181,6 @@ pub struct Rustc {
     ///
     /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
     crates: Vec<String>,
-    /// Override `Builder::kind` on cargo invocations.
-    ///
-    /// By default, `Builder::kind` is propagated as the subcommand to the cargo invocations.
-    /// However, there are cases when this is not desirable. For example, when running `x clippy $tool_name`,
-    /// passing `Builder::kind` to cargo invocations would run clippy on the entire compiler and library,
-    /// which is not useful if we only want to lint a few crates with specific rules.
-    override_build_kind: Option<Kind>,
 }
 
 impl Rustc {
@@ -215,12 +190,7 @@ impl Rustc {
             .into_iter()
             .map(|krate| krate.name.to_string())
             .collect();
-        Self { target, crates, override_build_kind: None }
-    }
-
-    pub fn build_kind(mut self, build_kind: Option<Kind>) -> Self {
-        self.override_build_kind = build_kind;
-        self
+        Self { target, crates }
     }
 }
 
@@ -235,7 +205,7 @@ impl Step for Rustc {
 
     fn make_run(run: RunConfig<'_>) {
         let crates = run.make_run_crates(Alias::Compiler);
-        run.builder.ensure(Rustc { target: run.target, crates, override_build_kind: None });
+        run.builder.ensure(Rustc { target: run.target, crates });
     }
 
     /// Builds the compiler.
@@ -256,7 +226,7 @@ impl Step for Rustc {
             builder.ensure(crate::core::build_steps::compile::Std::new(compiler, compiler.host));
             builder.ensure(crate::core::build_steps::compile::Std::new(compiler, target));
         } else {
-            builder.ensure(Std::new(target).build_kind(self.override_build_kind));
+            builder.ensure(Std::new(target));
         }
 
         let mut cargo = builder::Cargo::new(
@@ -265,17 +235,11 @@ impl Step for Rustc {
             Mode::Rustc,
             SourceType::InTree,
             target,
-            self.override_build_kind.unwrap_or(builder.kind),
+            Kind::Check,
         );
 
         rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
 
-        // For ./x.py clippy, don't run with --all-targets because
-        // linting tests and benchmarks can produce very noisy results
-        if builder.kind != Kind::Clippy {
-            cargo.arg("--all-targets");
-        }
-
         // Explicitly pass -p for all compiler crates -- this will force cargo
         // to also check the tests/benches/examples for these crates, rather
         // than just the leaf crate.
@@ -400,14 +364,9 @@ impl Step for RustAnalyzer {
 
         cargo.allow_features(crate::core::build_steps::tool::RustAnalyzer::ALLOW_FEATURES);
 
-        // For ./x.py clippy, don't check those targets because
-        // linting tests and benchmarks can produce very noisy results
-        if builder.kind != Kind::Clippy {
-            // can't use `--all-targets` because `--examples` doesn't work well
-            cargo.arg("--bins");
-            cargo.arg("--tests");
-            cargo.arg("--benches");
-        }
+        cargo.arg("--bins");
+        cargo.arg("--tests");
+        cargo.arg("--benches");
 
         // Cargo's output path in a given stage, compiled by a particular
         // compiler for the specified target.
@@ -468,11 +427,7 @@ impl Step for Compiletest {
 
         cargo.allow_features(COMPILETEST_ALLOW_FEATURES);
 
-        // For ./x.py clippy, don't run with --all-targets because
-        // linting tests and benchmarks can produce very noisy results
-        if builder.kind != Kind::Clippy {
-            cargo.arg("--all-targets");
-        }
+        cargo.arg("--all-targets");
 
         let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, self.target))
             .with_prefix("compiletest-check");
@@ -546,11 +501,7 @@ fn run_tool_check_step(
         &[],
     );
 
-    // For ./x.py clippy, don't run with --all-targets because
-    // linting tests and benchmarks can produce very noisy results
-    if builder.kind != Kind::Clippy {
-        cargo.arg("--all-targets");
-    }
+    cargo.arg("--all-targets");
 
     let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target))
         .with_prefix(&format!("{}-check", step_type_name.to_lowercase()));
diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs
index 788a3b9601d..ebf0caccfbc 100644
--- a/src/bootstrap/src/core/build_steps/clippy.rs
+++ b/src/bootstrap/src/core/build_steps/clippy.rs
@@ -217,7 +217,7 @@ impl Step for Rustc {
                 builder.ensure(compile::Std::new(compiler, compiler.host));
                 builder.ensure(compile::Std::new(compiler, target));
             } else {
-                builder.ensure(check::Std::new(target).build_kind(Some(Kind::Check)));
+                builder.ensure(check::Std::new(target));
             }
         }
 
@@ -289,7 +289,7 @@ macro_rules! lint_any {
                 let target = self.target;
 
                 if !builder.download_rustc() {
-                    builder.ensure(check::Rustc::new(target, builder).build_kind(Some(Kind::Check)));
+                    builder.ensure(check::Rustc::new(target, builder));
                 };
 
                 let cargo = prepare_tool_cargo(
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 86b7456d7b4..9ce81ff9a22 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -531,7 +531,7 @@ enum EditorKind {
 
 impl EditorKind {
     // Used in `./tests.rs`.
-    #[allow(dead_code)]
+    #[cfg(test)]
     pub const ALL: &[EditorKind] = &[
         EditorKind::Emacs,
         EditorKind::Helix,
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 0088e851d39..248ee4196b8 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -390,7 +390,6 @@ macro_rules! bootstrap_tool {
         ;
     )+) => {
         #[derive(PartialEq, Eq, Clone)]
-        #[allow(dead_code)]
         pub enum Tool {
             $(
                 $name,
@@ -716,7 +715,7 @@ impl Step for Rustdoc {
             && target_compiler.stage > 0
             && builder.rust_info().is_managed_git_subrepository()
         {
-            let files_to_track = &["src/librustdoc", "src/tools/rustdoc"];
+            let files_to_track = &["src/librustdoc", "src/tools/rustdoc", "src/rustdoc-json-types"];
 
             // Check if unchanged
             if !builder.config.has_changes_from_upstream(files_to_track) {
@@ -1129,6 +1128,7 @@ macro_rules! tool_extended {
             tool_name: $tool_name:expr,
             stable: $stable:expr
             $( , add_bins_to_sysroot: $add_bins_to_sysroot:expr )?
+            $( , add_features: $add_features:expr )?
             $( , )?
         }
     ) => {
@@ -1168,6 +1168,7 @@ macro_rules! tool_extended {
                     $tool_name,
                     $path,
                     None $( .or(Some(&$add_bins_to_sysroot)) )?,
+                    None $( .or(Some($add_features)) )?,
                 )
             }
         }
@@ -1205,7 +1206,13 @@ fn run_tool_build_step(
     tool_name: &'static str,
     path: &'static str,
     add_bins_to_sysroot: Option<&[&str]>,
+    add_features: Option<fn(&Builder<'_>, TargetSelection, &mut Vec<String>)>,
 ) -> ToolBuildResult {
+    let mut extra_features = Vec::new();
+    if let Some(func) = add_features {
+        func(builder, target, &mut extra_features);
+    }
+
     let ToolBuildResult { tool_path, build_compiler, target_compiler } =
         builder.ensure(ToolBuild {
             compiler,
@@ -1213,7 +1220,7 @@ fn run_tool_build_step(
             tool: tool_name,
             mode: Mode::ToolRustc,
             path,
-            extra_features: vec![],
+            extra_features,
             source_type: SourceType::InTree,
             allow_features: "",
             cargo_args: vec![],
@@ -1256,7 +1263,12 @@ tool_extended!(Clippy {
     path: "src/tools/clippy",
     tool_name: "clippy-driver",
     stable: true,
-    add_bins_to_sysroot: ["clippy-driver"]
+    add_bins_to_sysroot: ["clippy-driver"],
+    add_features: |builder, target, features| {
+        if builder.config.jemalloc(target) {
+            features.push("jemalloc".to_string());
+        }
+    }
 });
 tool_extended!(Miri {
     path: "src/tools/miri",
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index 0e3c3aaee0f..99044e2a253 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -683,6 +683,7 @@ impl Builder<'_> {
                         .arg("--print=file-names")
                         .arg("--crate-type=proc-macro")
                         .arg("-")
+                        .stdin(std::process::Stdio::null())
                         .run_capture(self)
                         .stderr();
 
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 6268a2b59d6..f1af2b285a2 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -9,6 +9,7 @@ use crate::Flags;
 use crate::core::build_steps::doc::DocumentationFormat;
 use crate::core::config::Config;
 use crate::utils::cache::ExecutedStep;
+use crate::utils::helpers::get_host_target;
 use crate::utils::tests::git::{GitCtx, git_test};
 
 static TEST_TRIPLE_1: &str = "i686-unknown-haiku";
@@ -1236,29 +1237,48 @@ fn any_debug() {
 /// The staging tests use insta for snapshot testing.
 /// See bootstrap's README on how to bless the snapshots.
 mod staging {
+    use crate::Build;
+    use crate::core::builder::Builder;
     use crate::core::builder::tests::{
         TEST_TRIPLE_1, configure, configure_with_args, render_steps, run_build,
     };
+    use crate::utils::tests::{ConfigBuilder, TestCtx};
 
     #[test]
     fn build_compiler_stage_1() {
-        let mut cache = run_build(
-            &["compiler".into()],
-            configure_with_args(&["build", "--stage", "1"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]),
-        );
-        let steps = cache.into_executed_steps();
-        insta::assert_snapshot!(render_steps(&steps), @r"
-        [build] rustc 0 <target1> -> std 0 <target1>
-        [build] llvm <target1>
-        [build] rustc 0 <target1> -> rustc 1 <target1>
-        [build] rustc 0 <target1> -> rustc 1 <target1>
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("build")
+                .path("compiler")
+                .stage(1)
+                .get_steps(), @r"
+        [build] rustc 0 <host> -> std 0 <host>
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
         ");
     }
+
+    impl ConfigBuilder {
+        fn get_steps(self) -> String {
+            let config = self.create_config();
+
+            let kind = config.cmd.kind();
+            let build = Build::new(config);
+            let builder = Builder::new(&build);
+            builder.run_step_descriptions(&Builder::get_step_descriptions(kind), &builder.paths);
+            render_steps(&builder.cache.into_executed_steps())
+        }
+    }
 }
 
 /// Renders the executed bootstrap steps for usage in snapshot tests with insta.
 /// Only renders certain important steps.
 /// Each value in `steps` should be a tuple of (Step, step output).
+///
+/// The arrow in the rendered output (`X -> Y`) means `X builds Y`.
+/// This is similar to the output printed by bootstrap to stdout, but here it is
+/// generated purely for the purpose of tests.
 fn render_steps(steps: &[ExecutedStep]) -> String {
     steps
         .iter()
@@ -1275,18 +1295,17 @@ fn render_steps(steps: &[ExecutedStep]) -> String {
             }
             let stage =
                 if let Some(stage) = metadata.stage { format!("{stage} ") } else { "".to_string() };
-            write!(record, "{} {stage}<{}>", metadata.name, metadata.target);
+            write!(record, "{} {stage}<{}>", metadata.name, normalize_target(metadata.target));
             Some(record)
         })
-        .map(|line| {
-            line.replace(TEST_TRIPLE_1, "target1")
-                .replace(TEST_TRIPLE_2, "target2")
-                .replace(TEST_TRIPLE_3, "target3")
-        })
         .collect::<Vec<_>>()
         .join("\n")
 }
 
+fn normalize_target(target: TargetSelection) -> String {
+    target.to_string().replace(&get_host_target().to_string(), "host")
+}
+
 fn render_compiler(compiler: Compiler) -> String {
-    format!("rustc {} <{}>", compiler.stage, compiler.host)
+    format!("rustc {} <{}>", compiler.stage, normalize_target(compiler.host))
 }
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index ff0fda2d2e6..d3393afcae0 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -49,7 +49,7 @@ use crate::core::download::is_download_ci_available;
 use crate::utils::channel;
 use crate::utils::exec::command;
 use crate::utils::execution_context::ExecutionContext;
-use crate::utils::helpers::exe;
+use crate::utils::helpers::{exe, get_host_target};
 use crate::{GitInfo, OnceLock, TargetSelection, check_ci_llvm, helpers, t};
 
 /// Each path in this list is considered "allowed" in the `download-rustc="if-unchanged"` logic.
@@ -349,7 +349,7 @@ impl Config {
             stderr_is_tty: std::io::stderr().is_terminal(),
 
             // set by build.rs
-            host_target: TargetSelection::from_user(env!("BUILD_TRIPLE")),
+            host_target: get_host_target(),
 
             src: {
                 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs
index eb9802bf2e1..78b28ac1828 100644
--- a/src/bootstrap/src/utils/exec.rs
+++ b/src/bootstrap/src/utils/exec.rs
@@ -119,6 +119,11 @@ impl<'a> BootstrapCommand {
         self
     }
 
+    pub fn stdin(&mut self, stdin: std::process::Stdio) -> &mut Self {
+        self.command.stdin(stdin);
+        self
+    }
+
     #[must_use]
     pub fn delay_failure(self) -> Self {
         Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self }
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 2f18fb60318..3c5f612daa7 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -178,6 +178,11 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<
     }
 }
 
+/// Return the host target on which we are currently running.
+pub fn get_host_target() -> TargetSelection {
+    TargetSelection::from_user(env!("BUILD_TRIPLE"))
+}
+
 /// Rename a file if from and to are in the same filesystem or
 /// copy and remove the file otherwise
 pub fn move_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs
index 73c500f6e36..91877fd0da4 100644
--- a/src/bootstrap/src/utils/tests/mod.rs
+++ b/src/bootstrap/src/utils/tests/mod.rs
@@ -1,3 +1,74 @@
 //! This module contains shared utilities for bootstrap tests.
 
+use std::path::{Path, PathBuf};
+use std::thread;
+
+use tempfile::TempDir;
+
+use crate::core::builder::Builder;
+use crate::core::config::DryRun;
+use crate::{Build, Config, Flags, t};
+
 pub mod git;
+
+/// Holds temporary state of a bootstrap test.
+/// Right now it is only used to redirect the build directory of the bootstrap
+/// invocation, in the future it would be great if we could actually execute
+/// the whole test with this directory set as the workdir.
+pub struct TestCtx {
+    directory: TempDir,
+}
+
+impl TestCtx {
+    pub fn new() -> Self {
+        let directory = TempDir::new().expect("cannot create temporary directory");
+        eprintln!("Running test in {}", directory.path().display());
+        Self { directory }
+    }
+
+    /// Starts a new invocation of bootstrap that executes `kind` as its top level command
+    /// (i.e. `x <kind>`). Returns a builder that configures the created config through CLI flags.
+    pub fn config(&self, kind: &str) -> ConfigBuilder {
+        ConfigBuilder::from_args(&[kind], self.directory.path().to_owned())
+    }
+}
+
+/// Used to configure an invocation of bootstrap.
+/// Currently runs in the rustc checkout, long-term it should be switched
+/// to run in a (cache-primed) temporary directory instead.
+pub struct ConfigBuilder {
+    args: Vec<String>,
+    directory: PathBuf,
+}
+
+impl ConfigBuilder {
+    fn from_args(args: &[&str], directory: PathBuf) -> Self {
+        Self { args: args.iter().copied().map(String::from).collect(), directory }
+    }
+
+    pub fn path(mut self, path: &str) -> Self {
+        self.args.push(path.to_string());
+        self
+    }
+
+    pub fn stage(mut self, stage: u32) -> Self {
+        self.args.push("--stage".to_string());
+        self.args.push(stage.to_string());
+        self
+    }
+
+    pub fn create_config(mut self) -> Config {
+        // Run in dry-check, otherwise the test would be too slow
+        self.args.push("--dry-run".to_string());
+
+        // Ignore submodules
+        self.args.push("--set".to_string());
+        self.args.push("build.submodules=false".to_string());
+
+        // Do not mess with the local rustc checkout build directory
+        self.args.push("--build-dir".to_string());
+        self.args.push(self.directory.join("build").display().to_string());
+
+        Config::parse(Flags::parse(&self.args))
+    }
+}
diff --git a/src/build_helper/src/ci.rs b/src/build_helper/src/ci.rs
index 60f319129a0..9d114c70a67 100644
--- a/src/build_helper/src/ci.rs
+++ b/src/build_helper/src/ci.rs
@@ -17,7 +17,11 @@ impl CiEnv {
     }
 
     pub fn is_ci() -> bool {
-        Self::current() != CiEnv::None
+        Self::current().is_running_in_ci()
+    }
+
+    pub fn is_running_in_ci(self) -> bool {
+        self != CiEnv::None
     }
 
     /// Checks if running in rust-lang/rust managed CI job.
diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs
index 438cd14389c..9d1195aadf8 100644
--- a/src/build_helper/src/git.rs
+++ b/src/build_helper/src/git.rs
@@ -198,7 +198,7 @@ fn get_latest_upstream_commit_that_modified_files(
 /// author.
 ///
 /// If we are in CI, we simply return our first parent.
-fn get_closest_upstream_commit(
+pub fn get_closest_upstream_commit(
     git_dir: Option<&Path>,
     config: &GitConfig<'_>,
     env: CiEnv,
diff --git a/src/ci/docker/host-x86_64/mingw-check-1/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-1/Dockerfile
index a877de1f7b2..9bdcf00dccc 100644
--- a/src/ci/docker/host-x86_64/mingw-check-1/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check-1/Dockerfile
@@ -39,7 +39,6 @@ RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-require
 
 COPY host-x86_64/mingw-check-1/check-default-config-profiles.sh /scripts/
 COPY host-x86_64/mingw-check-1/validate-toolstate.sh /scripts/
-COPY host-x86_64/mingw-check-1/validate-error-codes.sh /scripts/
 
 # Check library crates on all tier 1 targets.
 # We disable optimized compiler built-ins because that requires a C toolchain for the target.
@@ -52,7 +51,6 @@ ENV SCRIPT \
            python3 ../x.py check --stage 1 --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
            python3 ../x.py check --stage 1 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \
            /scripts/validate-toolstate.sh && \
-           /scripts/validate-error-codes.sh && \
            reuse --include-submodules lint && \
            python3 ../x.py test collect-license-metadata && \
            # Runs checks to ensure that there are no issues in our JS code.
diff --git a/src/ci/docker/host-x86_64/mingw-check-1/validate-error-codes.sh b/src/ci/docker/host-x86_64/mingw-check-1/validate-error-codes.sh
deleted file mode 100755
index e9aa948eb87..00000000000
--- a/src/ci/docker/host-x86_64/mingw-check-1/validate-error-codes.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-# Checks that no error code explanation is removed.
-
-set -eo pipefail
-
-if [[ -z "$BASE_COMMIT" ]]; then
-    echo "not checking error code explanations removal"
-    exit 0
-fi
-
-echo "Check if an error code explanation was removed..."
-
-if (git diff "$BASE_COMMIT" --name-status | grep '^D' \
-        | grep --quiet "compiler/rustc_error_codes/src/error_codes/"); then
-    echo "Error code explanations should never be removed!"
-    echo "Take a look at E0001 to see how to handle it."
-    exit 1
-fi
-
-echo "No error code explanation was removed!"
diff --git a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile
index 8d2c5e004e4..62cd8a31212 100644
--- a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile
@@ -39,7 +39,6 @@ RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-require
     && pip3 install virtualenv
 
 COPY host-x86_64/mingw-check-1/validate-toolstate.sh /scripts/
-COPY host-x86_64/mingw-check-1/validate-error-codes.sh /scripts/
 
 RUN bash -c 'npm install -g eslint@$(cat /tmp/eslint.version)'
 
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile
new file mode 100644
index 00000000000..b937bc3e678
--- /dev/null
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile
@@ -0,0 +1,57 @@
+FROM ghcr.io/rust-lang/ubuntu:22.04
+
+ARG DEBIAN_FRONTEND=noninteractive
+RUN apt-get update && apt-get install -y --no-install-recommends \
+  g++ \
+  make \
+  ninja-build \
+  file \
+  curl \
+  ca-certificates \
+  python3 \
+  git \
+  cmake \
+  libssl-dev \
+  sudo \
+  xz-utils \
+  tidy \
+  \
+  libc6 \
+  wget \
+  # libgccjit dependencies
+  flex \
+  libmpfr-dev \
+  libgmp-dev \
+  libmpc3 \
+  libmpc-dev \
+  && rm -rf /var/lib/apt/lists/*
+
+COPY scripts/sccache.sh /scripts/
+RUN sh /scripts/sccache.sh
+
+# Fix rustc_codegen_gcc lto issues.
+ENV GCC_EXEC_PREFIX="/usr/lib/gcc/"
+
+COPY host-x86_64/x86_64-gnu-miri/check-miri.sh /tmp/
+
+ENV RUST_CONFIGURE_ARGS \
+  --build=x86_64-unknown-linux-gnu \
+  --enable-new-symbol-mangling
+
+ENV HOST_TARGET x86_64-unknown-linux-gnu
+
+# FIXME(#133381): currently rustc alt builds do *not* have rustc debug
+# assertions enabled! Therefore, we cannot force download CI rustc.
+#ENV FORCE_CI_RUSTC 1
+
+COPY scripts/shared.sh /scripts/
+
+# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries
+# to create a new folder. For reference:
+# https://github.com/puppeteer/puppeteer/issues/375
+#
+# We also specify the version in case we need to update it to go around cache limitations.
+#
+# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case
+# the local version of the package is different than the one used by the CI.
+ENV SCRIPT /tmp/check-miri.sh ../x.py
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh
new file mode 100755
index 00000000000..c2a5b308b32
--- /dev/null
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+# ignore-tidy-linelength
+
+set -eu
+set -x # so one can see where we are in the script
+
+X_PY="$1"
+
+# Testing Miri is a bit complicated.
+# We set the GC interval to the shortest possible value (0 would be off) to increase the chance
+# that bugs which only surface when the GC runs at a specific time are more likely to cause CI to fail.
+# This significantly increases the runtime of our test suite, or we'd do this in PR CI too.
+if [ -z "${PR_CI_JOB:-}" ]; then
+    MIRIFLAGS=-Zmiri-provenance-gc=1 python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri
+else
+    python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri
+fi
+# We re-run the test suite for a chance to find bugs in the intrinsic fallback bodies and in MIR
+# optimizations. This can miss UB, so we only run the "pass" tests. We need to enable debug
+# assertions as `-O` disables them but some tests rely on them. We also set a cfg flag so tests can
+# adjust their expectations if needed. This can change the output of the tests so we ignore that,
+# we only ensure that all assertions still pass.
+MIRIFLAGS="-Zmiri-force-intrinsic-fallback --cfg force_intrinsic_fallback -O -Zmir-opt-level=4 -Cdebug-assertions=yes" \
+  MIRI_SKIP_UI_CHECKS=1 \
+  python3 "$X_PY" test --stage 2 src/tools/miri -- tests/pass tests/panic
+# We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc.
+# Also cover some other targets via cross-testing, in particular all tier 1 targets.
+case $HOST_TARGET in
+  x86_64-unknown-linux-gnu)
+    # Only this branch runs in PR CI.
+    # Fully test all main OSes, and all main architectures.
+    python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target aarch64-apple-darwin
+    python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target i686-pc-windows-msvc
+    # Only run "pass" tests for the remaining targets, which is quite a bit faster.
+    python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-pc-windows-gnu --test-args pass
+    python3 "$X_PY" test --stage 2 src/tools/miri --target i686-unknown-linux-gnu --test-args pass
+    python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-unknown-linux-gnu --test-args pass
+    python3 "$X_PY" test --stage 2 src/tools/miri --target s390x-unknown-linux-gnu --test-args pass
+    ;;
+  x86_64-pc-windows-msvc)
+    # Strangely, Linux targets do not work here. cargo always says
+    # "error: cannot produce cdylib for ... as the target ... does not support these crate types".
+    # Only run "pass" tests, which is quite a bit faster.
+    #FIXME: Re-enable this once CI issues are fixed
+    # See <https://github.com/rust-lang/rust/issues/127883>
+    # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`.
+    #python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-apple-darwin --test-args pass
+    ;;
+  *)
+    echo "FATAL: unexpected host $HOST_TARGET"
+    exit 1
+    ;;
+esac
+# Also smoke-test `x.py miri`. This doesn't run any actual tests (that would take too long),
+# but it ensures that the crates build properly when tested with Miri.
+
+#FIXME: Re-enable this for msvc once CI issues are fixed
+if [ "$HOST_TARGET" != "x86_64-pc-windows-msvc" ]; then
+  python3 "$X_PY" miri --stage 2 library/core --test-args notest
+  python3 "$X_PY" miri --stage 2 library/alloc --test-args notest
+  python3 "$X_PY" miri --stage 2 library/std --test-args notest
+fi
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
index 62e0451814b..ff9fedad656 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh
@@ -30,58 +30,3 @@ cat /tmp/toolstate/toolstates.json
 python3 "$X_PY" test --stage 2 check-tools
 python3 "$X_PY" test --stage 2 src/tools/clippy
 python3 "$X_PY" test --stage 2 src/tools/rustfmt
-
-# Testing Miri is a bit more complicated.
-# We set the GC interval to the shortest possible value (0 would be off) to increase the chance
-# that bugs which only surface when the GC runs at a specific time are more likely to cause CI to fail.
-# This significantly increases the runtime of our test suite, or we'd do this in PR CI too.
-if [ -z "${PR_CI_JOB:-}" ]; then
-    MIRIFLAGS=-Zmiri-provenance-gc=1 python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri
-else
-    python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri
-fi
-# We re-run the test suite for a chance to find bugs in the intrinsic fallback bodies and in MIR
-# optimizations. This can miss UB, so we only run the "pass" tests. We need to enable debug
-# assertions as `-O` disables them but some tests rely on them. We also set a cfg flag so tests can
-# adjust their expectations if needed. This can change the output of the tests so we ignore that,
-# we only ensure that all assertions still pass.
-MIRIFLAGS="-Zmiri-force-intrinsic-fallback --cfg force_intrinsic_fallback -O -Zmir-opt-level=4 -Cdebug-assertions=yes" \
-  MIRI_SKIP_UI_CHECKS=1 \
-  python3 "$X_PY" test --stage 2 src/tools/miri -- tests/pass tests/panic
-# We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc.
-# Also cover some other targets via cross-testing, in particular all tier 1 targets.
-case $HOST_TARGET in
-  x86_64-unknown-linux-gnu)
-    # Only this branch runs in PR CI.
-    # Fully test all main OSes, and all main architectures.
-    python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target aarch64-apple-darwin
-    python3 "$X_PY" test --stage 2 src/tools/miri src/tools/miri/cargo-miri --target i686-pc-windows-msvc
-    # Only run "pass" tests for the remaining targets, which is quite a bit faster.
-    python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-pc-windows-gnu --test-args pass
-    python3 "$X_PY" test --stage 2 src/tools/miri --target i686-unknown-linux-gnu --test-args pass
-    python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-unknown-linux-gnu --test-args pass
-    python3 "$X_PY" test --stage 2 src/tools/miri --target s390x-unknown-linux-gnu --test-args pass
-    ;;
-  x86_64-pc-windows-msvc)
-    # Strangely, Linux targets do not work here. cargo always says
-    # "error: cannot produce cdylib for ... as the target ... does not support these crate types".
-    # Only run "pass" tests, which is quite a bit faster.
-    #FIXME: Re-enable this once CI issues are fixed
-    # See <https://github.com/rust-lang/rust/issues/127883>
-    # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`.
-    #python3 "$X_PY" test --stage 2 src/tools/miri --target x86_64-apple-darwin --test-args pass
-    ;;
-  *)
-    echo "FATAL: unexpected host $HOST_TARGET"
-    exit 1
-    ;;
-esac
-# Also smoke-test `x.py miri`. This doesn't run any actual tests (that would take too long),
-# but it ensures that the crates build properly when tested with Miri.
-
-#FIXME: Re-enable this for msvc once CI issues are fixed
-if [ "$HOST_TARGET" != "x86_64-pc-windows-msvc" ]; then
-  python3 "$X_PY" miri --stage 2 library/core --test-args notest
-  python3 "$X_PY" miri --stage 2 library/alloc --test-args notest
-  python3 "$X_PY" miri --stage 2 library/std --test-args notest
-fi
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index e996a8f7139..3aa435003d3 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -150,7 +150,9 @@ pr:
       DOCKER_SCRIPT: stage_2_test_set2.sh
     <<: *job-aarch64-linux
   - name: x86_64-gnu-tools
-    <<: *job-linux-36c-codebuild
+    <<: *job-linux-4c
+  - name: x86_64-gnu-miri
+    <<: *job-linux-4c
 
 # Jobs that run when you perform a try build (@bors try)
 # These jobs automatically inherit envs.try, to avoid repeating
@@ -425,6 +427,9 @@ auto:
       DEPLOY_TOOLSTATES_JSON: toolstates-linux.json
     <<: *job-linux-4c
 
+  - name: x86_64-gnu-miri
+    <<: *job-linux-4c
+
   ####################
   #  macOS Builders  #
   ####################
diff --git a/src/doc/book b/src/doc/book
-Subproject 4433c9f0cad8460bee05ede040587f8a1fa3f1d
+Subproject 8a6d44e45b7b564eeb6bae30507e1fbac439d72
diff --git a/src/doc/reference b/src/doc/reference
-Subproject d4c66b346f4b72d29e70390a3fa3ea7d4e064db
+Subproject 50fc1628f36563958399123829c73755fa7a842
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject 9baa9e863116cb9524a177d5a5c475baac18928
+Subproject 05c7d8bae65f23a1837430c5a19be129d414f5e
diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version
index 30ba3070e1f..c986141b654 100644
--- a/src/doc/rustc-dev-guide/rust-version
+++ b/src/doc/rustc-dev-guide/rust-version
@@ -1 +1 @@
-d1d8e386c5e84c4ba857f56c3291f73c27e2d62a
+bc4376fa73b636eb6f2c7d48b1f731d70f022c4b
diff --git a/src/doc/rustc-dev-guide/src/autodiff/flags.md b/src/doc/rustc-dev-guide/src/autodiff/flags.md
index 65287d9ba4c..efbb9ea3497 100644
--- a/src/doc/rustc-dev-guide/src/autodiff/flags.md
+++ b/src/doc/rustc-dev-guide/src/autodiff/flags.md
@@ -6,6 +6,7 @@ To support you while debugging or profiling, we have added support for an experi
 
 ```text
 PrintTA // Print TypeAnalysis information
+PrintTAFn // Print TypeAnalysis information for a specific function
 PrintAA // Print ActivityAnalysis information
 Print // Print differentiated functions while they are being generated and optimized
 PrintPerf // Print AD related Performance warnings
diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md
index 9e4a11044e8..09dc476d68e 100644
--- a/src/doc/rustc-dev-guide/src/tests/ui.md
+++ b/src/doc/rustc-dev-guide/src/tests/ui.md
@@ -13,6 +13,11 @@ used for many other purposes. For example, tests can also be configured to [run
 the resulting program](#controlling-passfail-expectations) to verify its
 behavior.
 
+For a survey of each subdirectory's purpose under `tests/ui`, consult the
+[SUMMARY.md](https://github.com/rust-lang/rust/tree/master/tests/ui/SUMMARY.md).
+This is useful if you write a new test, and are looking for a category to
+place it in.
+
 If you need to work with `#![no_std]` cross-compiling tests, consult the
 [`minicore` test auxiliary](./minicore.md) chapter.
 
@@ -54,6 +59,11 @@ The output is normalized to ignore unwanted differences, see the
 [Normalization](#normalization) section. If the file is missing, then
 compiletest expects the corresponding output to be empty.
 
+A common reason to use normalization, revisions, and most of the other following tools,
+is to account for platform differences. Consider alternatives to these tools, like
+e.g. using the `extern "rust-invalid"` ABI that is invalid on every platform
+instead of fixing the test to use cross-compilation and testing every possibly-invalid ABI.
+
 There can be multiple stdout/stderr files. The general form is:
 
 ```text
diff --git a/src/doc/unstable-book/src/compiler-flags/autodiff.md b/src/doc/unstable-book/src/compiler-flags/autodiff.md
index 95c188d1f3b..28d2ece1468 100644
--- a/src/doc/unstable-book/src/compiler-flags/autodiff.md
+++ b/src/doc/unstable-book/src/compiler-flags/autodiff.md
@@ -10,6 +10,7 @@ Multiple options can be separated with a comma. Valid options are:
 
 `Enable` - Required flag to enable autodiff
 `PrintTA` - print Type Analysis Information
+`PrintTAFn` - print Type Analysis Information for a specific function
 `PrintAA` - print Activity Analysis Information
 `PrintPerf` - print Performance Warnings from Enzyme
 `PrintSteps` - prints all intermediate transformations
diff --git a/src/doc/unstable-book/src/compiler-flags/macro-stats.md b/src/doc/unstable-book/src/compiler-flags/macro-stats.md
index b2622cff057..f3fa69058a7 100644
--- a/src/doc/unstable-book/src/compiler-flags/macro-stats.md
+++ b/src/doc/unstable-book/src/compiler-flags/macro-stats.md
@@ -10,12 +10,12 @@ generated code is normally invisible to the programmer.
 
 This flag helps identify such cases. When enabled, the compiler measures the
 effect on code size of all used macros and prints a table summarizing that
-effect. For each distinct macro, it counts how many times it is used, and the
-net effect on code size (in terms of lines of code, and bytes of code). The
+effect. For each distinct macro, it counts how many times it is used, and how
+much code it produces when expanded (in lines of code, and bytes of code). The
 code size evaluation uses the compiler's internal pretty-printing, and so will
 be independent of the formatting in the original code.
 
-Note that the net effect of a macro may be negative. E.g. the `cfg!` and
+Note that the output size of a macro may be zero. E.g. the `cfg!` and
 `#[test]` macros often strip out code.
 
 If a macro is identified as causing a large increase in code size, it is worth
diff --git a/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md
index b7a3aa71fc4..03e576e3e30 100644
--- a/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md
+++ b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md
@@ -15,7 +15,7 @@ This flag is equivalent to:
 - `-fmin-function-alignment` for [GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fmin-function-alignment_003dn)
 - `-falign-functions` for [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang1-falign-functions)
 
-The specified alignment is a minimum. A higher alignment can be specified for specific functions by using the [`repr(align(...))`](https://github.com/rust-lang/rust/issues/82232) feature and annotating the function with a `#[repr(align(<align>))]` attribute. The attribute's value is ignored when it is lower than the value passed to `min-function-alignment`.
+The specified alignment is a minimum. A higher alignment can be specified for specific functions by using the [`align(...)`](https://github.com/rust-lang/rust/issues/82232) feature and annotating the function with a `#[align(<align>)]` attribute. The attribute's value is ignored when it is lower than the value passed to `min-function-alignment`.
 
 There are two additional edge cases for this flag:
 
diff --git a/src/doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md b/src/doc/unstable-book/src/language-features/abi-cmse-nonsecure-call.md
index 79a177cb28b..6b653a194fe 100644
--- a/src/doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md
+++ b/src/doc/unstable-book/src/language-features/abi-cmse-nonsecure-call.md
@@ -1,4 +1,4 @@
-# `abi_c_cmse_nonsecure_call`
+# `abi_cmse_nonsecure_call`
 
 The tracking issue for this feature is: [#81391]
 
@@ -14,10 +14,9 @@ LLVM, the Rust compiler and the linker are providing
 [support](https://developer.arm.com/documentation/ecm0359818/latest/) for the
 TrustZone-M feature.
 
-One of the things provided, with this unstable feature, is the
-`C-cmse-nonsecure-call` function ABI. This ABI is used on function pointers to
-non-secure code to mark a non-secure function call (see [section
-5.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
+One of the things provided with this unstable feature is the "cmse-nonsecure-call" function ABI.
+This ABI is used on function pointers to non-secure code to mark a non-secure function call
+(see [section 5.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
 
 With this ABI, the compiler will do the following to perform the call:
 * save registers needed after the call to Secure memory
@@ -28,19 +27,16 @@ With this ABI, the compiler will do the following to perform the call:
 To avoid using the non-secure stack, the compiler will constrain the number and
 type of parameters/return value.
 
-The `extern "C-cmse-nonsecure-call"` ABI is otherwise equivalent to the
-`extern "C"` ABI.
-
 <!-- NOTE(ignore) this example is specific to thumbv8m targets -->
 
 ``` rust,ignore
 #![no_std]
-#![feature(abi_c_cmse_nonsecure_call)]
+#![feature(abi_cmse_nonsecure_call)]
 
 #[no_mangle]
 pub fn call_nonsecure_function(addr: usize) -> u32 {
     let non_secure_function =
-        unsafe { core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn() -> u32>(addr) };
+        unsafe { core::mem::transmute::<usize, extern "cmse-nonsecure-call" fn() -> u32>(addr) };
     non_secure_function()
 }
 ```
diff --git a/src/doc/unstable-book/src/language-features/cmse-nonsecure-entry.md b/src/doc/unstable-book/src/language-features/cmse-nonsecure-entry.md
index ca95ccf33ac..1d76d651659 100644
--- a/src/doc/unstable-book/src/language-features/cmse-nonsecure-entry.md
+++ b/src/doc/unstable-book/src/language-features/cmse-nonsecure-entry.md
@@ -14,10 +14,9 @@ LLVM, the Rust compiler and the linker are providing
 [support](https://developer.arm.com/documentation/ecm0359818/latest/) for the
 TrustZone-M feature.
 
-One of the things provided, with this unstable feature, is the
-`C-cmse-nonsecure-entry` ABI. This ABI marks a Secure function as an
-entry function (see [section
-5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
+One of the things provided with this unstable feature is the "cmse-nonsecure-entry" ABI.
+This ABI marks a Secure function as an entry function (see
+[section 5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
 With this ABI, the compiler will do the following:
 * add a special symbol on the function which is the `__acle_se_` prefix and the
   standard function name
@@ -28,9 +27,7 @@ With this ABI, the compiler will do the following:
 
 Because the stack can not be used to pass parameters, there will be compilation
 errors if:
-* the total size of all parameters is too big (for example more than four 32
-  bits integers)
-* the entry function is not using a C ABI
+* the total size of all parameters is too big (for example, more than four 32-bit integers)
 
 The special symbol `__acle_se_` will be used by the linker to generate a secure
 gateway veneer.
@@ -42,7 +39,7 @@ gateway veneer.
 #![feature(cmse_nonsecure_entry)]
 
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
     input + 6
 }
 ```
diff --git a/src/doc/unstable-book/src/language-features/loop-match.md b/src/doc/unstable-book/src/language-features/loop-match.md
new file mode 100644
index 00000000000..4cc763d3434
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/loop-match.md
@@ -0,0 +1,52 @@
+# `loop_match`
+
+The tracking issue for this feature is: [#132306]
+
+[#132306]: https://github.com/rust-lang/rust/issues/132306
+
+------
+
+The `#[loop_match]` and `#[const_continue]` attributes can be used to improve the code
+generation of logic that fits this shape:
+
+```ignore (pseudo-rust)
+loop {
+    state = 'blk: {
+        match state {
+            State::A => {
+                break 'blk State::B
+            }
+            State::B => { /* ... */ }
+            /* ... */
+        }
+    }
+}
+```
+
+Here the loop itself can be annotated with `#[loop_match]`, and any `break 'blk` with
+`#[const_continue]` if the value is know at compile time:
+
+```ignore (pseudo-rust)
+#[loop_match]
+loop {
+    state = 'blk: {
+        match state {
+            State::A => {
+                #[const_continue]
+                break 'blk State::B
+            }
+            State::B => { /* ... */ }
+            /* ... */
+        }
+    }
+}
+```
+
+The observable behavior of this loop is exactly the same as without the extra attributes.
+The difference is in the generated output: normally, when the state is `A`, control flow
+moves from the `A` branch, back to the top of the loop, then to the `B` branch. With the
+attributes, The `A` branch will immediately jump to the `B` branch.
+
+Removing the indirection can be beneficial for stack usage and branch prediction, and
+enables other optimizations by clearly splitting out the control flow paths that your
+program will actually use.
diff --git a/src/doc/unstable-book/src/language-features/macro-metavar-expr-concat.md b/src/doc/unstable-book/src/language-features/macro-metavar-expr-concat.md
index b6dbdb14407..7eb5dca532f 100644
--- a/src/doc/unstable-book/src/language-features/macro-metavar-expr-concat.md
+++ b/src/doc/unstable-book/src/language-features/macro-metavar-expr-concat.md
@@ -8,7 +8,8 @@ In stable Rust, there is no way to create new identifiers by joining identifiers
  `#![feature(macro_metavar_expr_concat)]` introduces a way to do this, using the concat metavariable expression.
 
 > This feature uses the syntax from [`macro_metavar_expr`] but is otherwise
-> independent. It replaces the old unstable feature [`concat_idents`].
+> independent. It replaces the since-removed unstable feature
+> [`concat_idents`].
 
 > This is an experimental feature; it and its syntax will require a RFC before stabilization.
 
@@ -126,8 +127,7 @@ test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
 
 [`paste`]: https://crates.io/crates/paste
 [RFC 3086]: https://rust-lang.github.io/rfcs/3086-macro-metavar-expr.html
-[`concat_idents!`]: https://doc.rust-lang.org/nightly/std/macro.concat_idents.html
 [`macro_metavar_expr`]: ../language-features/macro-metavar-expr.md
-[`concat_idents`]: ../library-features/concat-idents.md
+[`concat_idents`]: https://github.com/rust-lang/rust/issues/29599
 [#124225]: https://github.com/rust-lang/rust/issues/124225
 [declarative macros]: https://doc.rust-lang.org/stable/reference/macros-by-example.html
diff --git a/src/doc/unstable-book/src/library-features/concat-idents.md b/src/doc/unstable-book/src/library-features/concat-idents.md
deleted file mode 100644
index 8a38d155e3d..00000000000
--- a/src/doc/unstable-book/src/library-features/concat-idents.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# `concat_idents`
-
-The tracking issue for this feature is: [#29599]
-
-This feature is deprecated, to be replaced by [`macro_metavar_expr_concat`].
-
-[#29599]: https://github.com/rust-lang/rust/issues/29599
-[`macro_metavar_expr_concat`]: https://github.com/rust-lang/rust/issues/124225
-
-------------------------
-
-> This feature is expected to be superseded by [`macro_metavar_expr_concat`](../language-features/macro-metavar-expr-concat.md).
-
-The `concat_idents` feature adds a macro for concatenating multiple identifiers
-into one identifier.
-
-## Examples
-
-```rust
-#![feature(concat_idents)]
-
-fn main() {
-    fn foobar() -> u32 { 23 }
-    let f = concat_idents!(foo, bar);
-    assert_eq!(f(), 23);
-}
-```
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index c889f52b789..11d5b472d73 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -23,7 +23,7 @@ pub(crate) fn synthesize_blanket_impls(
     let ty = tcx.type_of(item_def_id);
 
     let mut blanket_impls = Vec::new();
-    for trait_def_id in tcx.all_traits() {
+    for trait_def_id in tcx.visible_traits() {
         if !cx.cache.effective_visibilities.is_reachable(tcx, trait_def_id)
             || cx.generated_synthetics.contains(&(ty.skip_binder(), trait_def_id))
         {
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 2d9670a3d10..69899539b45 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -4,6 +4,7 @@ use std::sync::{Arc, OnceLock as OnceCell};
 use std::{fmt, iter};
 
 use arrayvec::ArrayVec;
+use itertools::Either;
 use rustc_abi::{ExternAbi, VariantIdx};
 use rustc_attr_data_structures::{
     AttributeKind, ConstStability, Deprecation, Stability, StableSince,
@@ -199,49 +200,49 @@ impl ExternalCrate {
             .unwrap_or(Unknown) // Well, at least we tried.
     }
 
-    pub(crate) fn keywords(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, Symbol)> {
+    fn mapped_root_modules<T>(
+        &self,
+        tcx: TyCtxt<'_>,
+        f: impl Fn(DefId, TyCtxt<'_>) -> Option<(DefId, T)>,
+    ) -> impl Iterator<Item = (DefId, T)> {
         let root = self.def_id();
 
-        let as_keyword = |res: Res<!>| {
-            if let Res::Def(DefKind::Mod, def_id) = res {
-                let mut keyword = None;
-                let meta_items = tcx
-                    .get_attrs(def_id, sym::doc)
-                    .flat_map(|attr| attr.meta_item_list().unwrap_or_default());
-                for meta in meta_items {
-                    if meta.has_name(sym::keyword)
-                        && let Some(v) = meta.value_str()
-                    {
-                        keyword = Some(v);
-                        break;
-                    }
-                }
-                return keyword.map(|p| (def_id, p));
-            }
-            None
-        };
         if root.is_local() {
-            tcx.hir_root_module()
-                .item_ids
-                .iter()
-                .filter_map(|&id| {
-                    let item = tcx.hir_item(id);
-                    match item.kind {
-                        hir::ItemKind::Mod(..) => {
-                            as_keyword(Res::Def(DefKind::Mod, id.owner_id.to_def_id()))
-                        }
-                        _ => None,
-                    }
-                })
-                .collect()
+            Either::Left(
+                tcx.hir_root_module()
+                    .item_ids
+                    .iter()
+                    .filter(move |&&id| matches!(tcx.hir_item(id).kind, hir::ItemKind::Mod(..)))
+                    .filter_map(move |&id| f(id.owner_id.into(), tcx)),
+            )
         } else {
-            tcx.module_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect()
+            Either::Right(
+                tcx.module_children(root)
+                    .iter()
+                    .filter_map(|item| {
+                        if let Res::Def(DefKind::Mod, did) = item.res { Some(did) } else { None }
+                    })
+                    .filter_map(move |did| f(did, tcx)),
+            )
         }
     }
 
-    pub(crate) fn primitives(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, PrimitiveType)> {
-        let root = self.def_id();
+    pub(crate) fn keywords(&self, tcx: TyCtxt<'_>) -> impl Iterator<Item = (DefId, Symbol)> {
+        fn as_keyword(did: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, Symbol)> {
+            tcx.get_attrs(did, sym::doc)
+                .flat_map(|attr| attr.meta_item_list().unwrap_or_default())
+                .filter(|meta| meta.has_name(sym::keyword))
+                .find_map(|meta| meta.value_str())
+                .map(|value| (did, value))
+        }
+
+        self.mapped_root_modules(tcx, as_keyword)
+    }
 
+    pub(crate) fn primitives(
+        &self,
+        tcx: TyCtxt<'_>,
+    ) -> impl Iterator<Item = (DefId, PrimitiveType)> {
         // Collect all inner modules which are tagged as implementations of
         // primitives.
         //
@@ -259,40 +260,21 @@ impl ExternalCrate {
         // Also note that this does not attempt to deal with modules tagged
         // duplicately for the same primitive. This is handled later on when
         // rendering by delegating everything to a hash map.
-        let as_primitive = |res: Res<!>| {
-            let Res::Def(DefKind::Mod, def_id) = res else { return None };
-            tcx.get_attrs(def_id, sym::rustc_doc_primitive)
-                .map(|attr| {
-                    let attr_value = attr.value_str().expect("syntax should already be validated");
-                    let Some(prim) = PrimitiveType::from_symbol(attr_value) else {
-                        span_bug!(
-                            attr.span(),
-                            "primitive `{attr_value}` is not a member of `PrimitiveType`"
-                        );
-                    };
-
-                    (def_id, prim)
-                })
-                .next()
-        };
+        fn as_primitive(def_id: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, PrimitiveType)> {
+            tcx.get_attrs(def_id, sym::rustc_doc_primitive).next().map(|attr| {
+                let attr_value = attr.value_str().expect("syntax should already be validated");
+                let Some(prim) = PrimitiveType::from_symbol(attr_value) else {
+                    span_bug!(
+                        attr.span(),
+                        "primitive `{attr_value}` is not a member of `PrimitiveType`"
+                    );
+                };
 
-        if root.is_local() {
-            tcx.hir_root_module()
-                .item_ids
-                .iter()
-                .filter_map(|&id| {
-                    let item = tcx.hir_item(id);
-                    match item.kind {
-                        hir::ItemKind::Mod(..) => {
-                            as_primitive(Res::Def(DefKind::Mod, id.owner_id.to_def_id()))
-                        }
-                        _ => None,
-                    }
-                })
-                .collect()
-        } else {
-            tcx.module_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect()
+                (def_id, prim)
+            })
         }
+
+        self.mapped_root_modules(tcx, as_primitive)
     }
 }
 
@@ -764,15 +746,18 @@ impl Item {
         Some(tcx.visibility(def_id))
     }
 
-    pub(crate) fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec<String> {
+    fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec<String> {
         const ALLOWED_ATTRIBUTES: &[Symbol] =
             &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive];
-
         self.attrs
             .other_attrs
             .iter()
             .filter_map(|attr| {
-                if is_json {
+                // NoMangle is special cased, as it appears in HTML output, and we want to show it in source form, not HIR printing.
+                // It is also used by cargo-semver-checks.
+                if matches!(attr, hir::Attribute::Parsed(AttributeKind::NoMangle(..))) {
+                    Some("#[no_mangle]".to_string())
+                } else if is_json {
                     match attr {
                         // rustdoc-json stores this in `Item::deprecation`, so we
                         // don't want it it `Item::attrs`.
@@ -785,26 +770,22 @@ impl Item {
                             s
                         }),
                     }
-                } else if attr.has_any_name(ALLOWED_ATTRIBUTES) {
+                } else {
+                    if !attr.has_any_name(ALLOWED_ATTRIBUTES) {
+                        return None;
+                    }
                     Some(
                         rustc_hir_pretty::attribute_to_string(&tcx, attr)
                             .replace("\\\n", "")
                             .replace('\n', "")
                             .replace("  ", " "),
                     )
-                } else {
-                    None
                 }
             })
             .collect()
     }
 
-    pub(crate) fn attributes_and_repr(
-        &self,
-        tcx: TyCtxt<'_>,
-        cache: &Cache,
-        is_json: bool,
-    ) -> Vec<String> {
+    pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec<String> {
         let mut attrs = self.attributes_without_repr(tcx, is_json);
 
         if let Some(repr_attr) = self.repr(tcx, cache, is_json) {
@@ -1966,7 +1947,7 @@ impl PrimitiveType {
                 let e = ExternalCrate { crate_num };
                 let crate_name = e.name(tcx);
                 debug!(?crate_num, ?crate_name);
-                for &(def_id, prim) in &e.primitives(tcx) {
+                for (def_id, prim) in e.primitives(tcx) {
                     // HACK: try to link to std instead where possible
                     if crate_name == sym::core && primitive_locations.contains_key(&prim) {
                         continue;
@@ -2450,20 +2431,6 @@ pub(crate) enum ConstantKind {
     Infer,
 }
 
-impl Constant {
-    pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String {
-        self.kind.expr(tcx)
-    }
-
-    pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> Option<String> {
-        self.kind.value(tcx)
-    }
-
-    pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool {
-        self.kind.is_literal(tcx)
-    }
-}
-
 impl ConstantKind {
     pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String {
         match *self {
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index c58b07a5b67..2c9878636ab 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -61,7 +61,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
     let keywords = local_crate.keywords(cx.tcx);
     {
         let ItemKind::ModuleItem(m) = &mut module.inner.kind else { unreachable!() };
-        m.items.extend(primitives.iter().map(|&(def_id, prim)| {
+        m.items.extend(primitives.map(|(def_id, prim)| {
             Item::from_def_id_and_parts(
                 def_id,
                 Some(prim.as_sym()),
@@ -69,7 +69,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
                 cx,
             )
         }));
-        m.items.extend(keywords.into_iter().map(|(def_id, kw)| {
+        m.items.extend(keywords.map(|(def_id, kw)| {
             Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::KeywordItem, cx)
         }));
     }
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index f93aa8ffd0d..986390dbaa0 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::DiagCtxtHandle;
 use rustc_session::config::{
     self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns,
-    OptionsTargetModifiers, UnstableOptions, get_cmd_lint_options, nightly_options,
+    OptionsTargetModifiers, Sysroot, UnstableOptions, get_cmd_lint_options, nightly_options,
     parse_crate_types_from_list, parse_externs, parse_target_triple,
 };
 use rustc_session::lint::Level;
@@ -103,9 +103,7 @@ pub(crate) struct Options {
     /// compiling doctests from the crate.
     pub(crate) edition: Edition,
     /// The path to the sysroot. Used during the compilation process.
-    pub(crate) sysroot: PathBuf,
-    /// Has the same value as `sysroot` except is `None` when the user didn't pass `---sysroot`.
-    pub(crate) maybe_sysroot: Option<PathBuf>,
+    pub(crate) sysroot: Sysroot,
     /// Lint information passed over the command-line.
     pub(crate) lint_opts: Vec<(String, Level)>,
     /// Whether to ask rustc to describe the lints it knows.
@@ -201,7 +199,6 @@ impl fmt::Debug for Options {
             .field("target", &self.target)
             .field("edition", &self.edition)
             .field("sysroot", &self.sysroot)
-            .field("maybe_sysroot", &self.maybe_sysroot)
             .field("lint_opts", &self.lint_opts)
             .field("describe_lints", &self.describe_lints)
             .field("lint_cap", &self.lint_cap)
@@ -725,16 +722,14 @@ impl Options {
         }
 
         let target = parse_target_triple(early_dcx, matches);
-        let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
-
-        let sysroot = rustc_session::filesearch::materialize_sysroot(maybe_sysroot.clone());
+        let sysroot = Sysroot::new(matches.opt_str("sysroot").map(PathBuf::from));
 
         let libs = matches
             .opt_strs("L")
             .iter()
             .map(|s| {
                 SearchPath::from_cli_opt(
-                    &sysroot,
+                    sysroot.path(),
                     &target,
                     early_dcx,
                     s,
@@ -827,7 +822,6 @@ impl Options {
             target,
             edition,
             sysroot,
-            maybe_sysroot,
             lint_opts,
             describe_lints,
             lint_cap,
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 204f8decffc..bd57bb21e63 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -149,15 +149,12 @@ pub(crate) fn new_dcx(
     diagnostic_width: Option<usize>,
     unstable_opts: &UnstableOptions,
 ) -> rustc_errors::DiagCtxt {
-    let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-        rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-        false,
-    );
+    let translator = rustc_driver::default_translator();
     let emitter: Box<DynEmitter> = match error_format {
         ErrorOutputType::HumanReadable { kind, color_config } => {
             let short = kind.short();
             Box::new(
-                HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
+                HumanEmitter::new(stderr_destination(color_config), translator)
                     .sm(source_map.map(|sm| sm as _))
                     .short_message(short)
                     .diagnostic_width(diagnostic_width)
@@ -178,7 +175,7 @@ pub(crate) fn new_dcx(
                 JsonEmitter::new(
                     Box::new(io::BufWriter::new(io::stderr())),
                     Some(source_map),
-                    fallback_bundle,
+                    translator,
                     pretty,
                     json_rendered,
                     color_config,
@@ -356,7 +353,7 @@ pub(crate) fn run_global_ctxt(
     rustc_passes::stability::check_unused_or_stable_features(tcx);
 
     let auto_traits =
-        tcx.all_traits().filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)).collect();
+        tcx.visible_traits().filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)).collect();
 
     let mut ctxt = DocContext {
         tcx,
@@ -387,8 +384,6 @@ pub(crate) fn run_global_ctxt(
         ctxt.external_traits.insert(sized_trait_did, sized_trait);
     }
 
-    debug!("crate: {:?}", tcx.hir_crate(()));
-
     let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt));
 
     if krate.module.doc_value().is_empty() {
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 130fdff1afe..1b5c9fd4664 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -514,8 +514,9 @@ fn run_test(
 
     compiler_args.push(format!("@{}", doctest.global_opts.args_file.display()));
 
-    if let Some(sysroot) = &rustdoc_options.maybe_sysroot {
-        compiler_args.push(format!("--sysroot={}", sysroot.display()));
+    let sysroot = &rustdoc_options.sysroot;
+    if let Some(explicit_sysroot) = &sysroot.explicit {
+        compiler_args.push(format!("--sysroot={}", explicit_sysroot.display()));
     }
 
     compiler_args.extend_from_slice(&["--edition".to_owned(), doctest.edition.to_string()]);
@@ -574,7 +575,7 @@ fn run_test(
     let rustc_binary = rustdoc_options
         .test_builder
         .as_deref()
-        .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc"));
+        .unwrap_or_else(|| rustc_interface::util::rustc_path(sysroot).expect("found rustc"));
     let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary);
 
     compiler.args(&compiler_args);
diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs
index 3ff6828e52f..f229f77c978 100644
--- a/src/librustdoc/doctest/make.rs
+++ b/src/librustdoc/doctest/make.rs
@@ -456,16 +456,13 @@ fn parse_source(
     let filename = FileName::anon_source_code(&wrapped_source);
 
     let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
-    let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-        rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-        false,
-    );
+    let translator = rustc_driver::default_translator();
     info.supports_color =
-        HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
+        HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator.clone())
             .supports_color();
     // Any errors in parsing should also appear when the doctest is compiled for real, so just
     // send all the errors that the parser emits directly into a `Sink` instead of stderr.
-    let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
+    let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
 
     // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
     let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs
index 48626171404..79ff1fa38c3 100644
--- a/src/librustdoc/formats/renderer.rs
+++ b/src/librustdoc/formats/renderer.rs
@@ -68,8 +68,6 @@ pub(crate) trait FormatRenderer<'tcx>: Sized {
 
     /// Post processing hook for cleanup and dumping output to files.
     fn after_krate(self) -> Result<(), Error>;
-
-    fn cache(&self) -> &Cache;
 }
 
 fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>(
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 38214451657..3b4dae841ee 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -875,8 +875,4 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
 
         Ok(())
     }
-
-    fn cache(&self) -> &Cache {
-        &self.shared.cache
-    }
 }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 66d5aafa3c1..ed58bae70bd 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1194,7 +1194,7 @@ fn render_assoc_item(
 // a whitespace prefix and newline.
 fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
     fmt::from_fn(move |f| {
-        for a in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
+        for a in it.attributes(cx.tcx(), cx.cache(), false) {
             writeln!(f, "{prefix}{a}")?;
         }
         Ok(())
@@ -1210,7 +1210,7 @@ fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) {
 // When an attribute is rendered inside a <code> tag, it is formatted using
 // a div to produce a newline after it.
 fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
-    for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
+    for attr in it.attributes(cx.tcx(), cx.cache(), false) {
         render_code_attribute(CodeAttribute(attr), w);
     }
 }
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index a75088d27cc..e16acc9622f 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -469,7 +469,8 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
 
                     let unsafety_flag = match myitem.kind {
                         clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
-                            if myitem.fn_header(tcx).unwrap().is_unsafe() =>
+                            if myitem.fn_header(tcx).unwrap().safety
+                                == hir::HeaderSafety::Normal(hir::Safety::Unsafe) =>
                         {
                             "<sup title=\"unsafe function\">⚠</sup>"
                         }
@@ -1491,7 +1492,7 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
                     writeln!(f, "{repr}")?;
                 };
             } else {
-                for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) {
+                for a in self.it.attributes(self.cx.tcx(), self.cx.cache(), false) {
                     writeln!(f, "{a}")?;
                 }
             }
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index fb2b45802a6..606a9113908 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -439,24 +439,20 @@ impl CratesIndexPart {
         let content =
             format!("<h1>List of all crates</h1><ul class=\"all-items\">{DELIMITER}</ul>");
         let template = layout::render(layout, &page, "", content, style_files);
-        match SortedTemplate::from_template(&template, DELIMITER) {
-            Ok(template) => template,
-            Err(e) => panic!(
-                "Object Replacement Character (U+FFFC) should not appear in the --index-page: {e}"
-            ),
-        }
+        SortedTemplate::from_template(&template, DELIMITER)
+            .expect("Object Replacement Character (U+FFFC) should not appear in the --index-page")
     }
 
     /// Might return parts that are duplicate with ones in prexisting index.html
     fn get(crate_name: &str, external_crates: &[String]) -> Result<PartsAndLocations<Self>, Error> {
         let mut ret = PartsAndLocations::default();
-        let path = PathBuf::from("index.html");
+        let path = Path::new("index.html");
         for crate_name in external_crates.iter().map(|s| s.as_str()).chain(once(crate_name)) {
             let part = format!(
                 "<li><a href=\"{trailing_slash}index.html\">{crate_name}</a></li>",
                 trailing_slash = ensure_trailing_slash(crate_name),
             );
-            ret.push(path.clone(), part);
+            ret.push(path.to_path_buf(), part);
         }
         Ok(ret)
     }
@@ -737,7 +733,7 @@ impl TraitAliasPart {
                 },
             };
 
-            let implementors = imps
+            let mut implementors = imps
                 .iter()
                 .filter_map(|imp| {
                     // If the trait and implementation are in the same crate, then
@@ -759,12 +755,12 @@ impl TraitAliasPart {
                         })
                     }
                 })
-                .collect::<Vec<_>>();
+                .peekable();
 
             // Only create a js file if we have impls to add to it. If the trait is
             // documented locally though we always create the file to avoid dead
             // links.
-            if implementors.is_empty() && !cache.paths.contains_key(&did) {
+            if implementors.peek().is_none() && !cache.paths.contains_key(&did) {
                 continue;
             }
 
@@ -775,11 +771,7 @@ impl TraitAliasPart {
             path.push(format!("{remote_item_type}.{}.js", remote_path[remote_path.len() - 1]));
 
             let part = OrderedJson::array_sorted(
-                implementors
-                    .iter()
-                    .map(OrderedJson::serialize)
-                    .collect::<Result<Vec<_>, _>>()
-                    .unwrap(),
+                implementors.map(|implementor| OrderedJson::serialize(implementor).unwrap()),
             );
             path_parts.push(path, OrderedJson::array_unsorted([crate_name_json, &part]));
         }
@@ -874,9 +866,8 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> {
             let impl_ = cache
                 .impls
                 .get(&target_did)
-                .map(|v| &v[..])
-                .unwrap_or_default()
-                .iter()
+                .into_iter()
+                .flatten()
                 .map(|impl_| {
                     (impl_.impl_item.item_id, AliasedTypeImpl { impl_, type_aliases: Vec::new() })
                 })
@@ -891,14 +882,8 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> {
         // Exclude impls that are directly on this type. They're already in the HTML.
         // Some inlining scenarios can cause there to be two versions of the same
         // impl: one on the type alias and one on the underlying target type.
-        let mut seen_impls: FxHashSet<ItemId> = cache
-            .impls
-            .get(&self_did)
-            .map(|s| &s[..])
-            .unwrap_or_default()
-            .iter()
-            .map(|i| i.impl_item.item_id)
-            .collect();
+        let mut seen_impls: FxHashSet<ItemId> =
+            cache.impls.get(&self_did).into_iter().flatten().map(|i| i.impl_item.item_id).collect();
         for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ {
             // Only include this impl if it actually unifies with this alias.
             // Synthetic impls are not included; those are also included in the HTML.
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 1fa6b5a60f3..c34b3154269 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -353,7 +353,7 @@ pub(crate) fn print_src(
         );
         Ok(())
     });
-    let max_nb_digits = if lines > 0 { lines.ilog(10) + 1 } else { 1 };
+    let max_nb_digits = if lines > 0 { lines.ilog10() + 1 } else { 1 };
     match source_context {
         SourceContext::Standalone { file_path } => Source {
             code_html: code,
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 7b1a61a3ffa..2de8f836da3 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1,6 +1,6 @@
 // Local js definitions:
 /* global addClass, getSettingValue, hasClass, updateLocalStorage */
-/* global onEachLazy, removeClass, getVar */
+/* global onEachLazy, removeClass, getVar, nonnull */
 
 "use strict";
 
@@ -2138,3 +2138,31 @@ function preLoadCss(cssUrl) {
         elem.addEventListener("click", showHideCodeExampleButtons);
     });
 }());
+
+// This section is a bugfix for firefox: when copying text with `user-select: none`, it adds
+// extra backline characters.
+//
+// Rustdoc issue: Workaround for https://github.com/rust-lang/rust/issues/141464
+// Firefox issue: https://bugzilla.mozilla.org/show_bug.cgi?id=1273836
+(function() {
+    document.body.addEventListener("copy", event => {
+        let target = nonnull(event.target);
+        let isInsideCode = false;
+        while (target && target !== document.body) {
+            // @ts-expect-error
+            if (target.tagName === "CODE") {
+                isInsideCode = true;
+                break;
+            }
+            // @ts-expect-error
+            target = target.parentElement;
+        }
+        if (!isInsideCode) {
+            return;
+        }
+        const selection = document.getSelection();
+         // @ts-expect-error
+        nonnull(event.clipboardData).setData("text/plain", selection.toString());
+        event.preventDefault();
+    });
+}());
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index b611a3e501d..a2c48708512 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -5394,43 +5394,6 @@ function updateCrate(ev) {
     search(true);
 }
 
-// @ts-expect-error
-function initSearch(searchIndx) {
-    rawSearchIndex = searchIndx;
-    if (typeof window !== "undefined") {
-        // @ts-expect-error
-        docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
-        registerSearchEvents();
-        // If there's a search term in the URL, execute the search now.
-        if (window.searchState.getQueryStringParams().search) {
-            search();
-        }
-    } else if (typeof exports !== "undefined") {
-        // @ts-expect-error
-        docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
-        exports.docSearch = docSearch;
-        exports.parseQuery = DocSearch.parseQuery;
-    }
-}
-
-if (typeof exports !== "undefined") {
-    exports.initSearch = initSearch;
-}
-
-if (typeof window !== "undefined") {
-    // @ts-expect-error
-    window.initSearch = initSearch;
-    // @ts-expect-error
-    if (window.searchIndex !== undefined) {
-        // @ts-expect-error
-        initSearch(window.searchIndex);
-    }
-} else {
-    // Running in Node, not a browser. Run initSearch just to produce the
-    // exports.
-    initSearch(new Map());
-}
-
 // Parts of this code are based on Lucene, which is licensed under the
 // Apache/2.0 license.
 // More information found here:
@@ -5909,3 +5872,44 @@ Lev1TParametricDescription.prototype.toStates3 = /*3 bits per value */ new Int32
 Lev1TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new Int32Array([
     0xa0fc0000,0x5555ba08,0x55555555,
 ]);
+
+// ====================
+// WARNING: Nothing should be added below this comment: we need the `initSearch` function to
+// be called ONLY when the whole file has been parsed and loaded.
+
+// @ts-expect-error
+function initSearch(searchIndx) {
+    rawSearchIndex = searchIndx;
+    if (typeof window !== "undefined") {
+        // @ts-expect-error
+        docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
+        registerSearchEvents();
+        // If there's a search term in the URL, execute the search now.
+        if (window.searchState.getQueryStringParams().search) {
+            search();
+        }
+    } else if (typeof exports !== "undefined") {
+        // @ts-expect-error
+        docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
+        exports.docSearch = docSearch;
+        exports.parseQuery = DocSearch.parseQuery;
+    }
+}
+
+if (typeof exports !== "undefined") {
+    exports.initSearch = initSearch;
+}
+
+if (typeof window !== "undefined") {
+    // @ts-expect-error
+    window.initSearch = initSearch;
+    // @ts-expect-error
+    if (window.searchIndex !== undefined) {
+        // @ts-expect-error
+        initSearch(window.searchIndex);
+    }
+} else {
+    // Running in Node, not a browser. Run initSearch just to produce the
+    // exports.
+    initSearch(new Map());
+}
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 6bdf3b5fe38..f51b35097f6 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -11,12 +11,11 @@ use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::DefId;
 use rustc_metadata::rendered_const;
 use rustc_middle::{bug, ty};
-use rustc_span::{Pos, Symbol, kw};
+use rustc_span::{Pos, kw, sym};
 use rustdoc_json_types::*;
 use thin_vec::ThinVec;
 
 use crate::clean::{self, ItemId};
-use crate::formats::FormatRenderer;
 use crate::formats::item_type::ItemType;
 use crate::json::JsonRenderer;
 use crate::passes::collect_intra_doc_links::UrlFragment;
@@ -41,7 +40,7 @@ impl JsonRenderer<'_> {
             })
             .collect();
         let docs = item.opt_doc_value();
-        let attrs = item.attributes_and_repr(self.tcx, self.cache(), true);
+        let attrs = item.attributes(self.tcx, &self.cache, true);
         let span = item.span(self.tcx);
         let visibility = item.visibility(self.tcx);
         let clean::ItemInner { name, item_id, .. } = *item.inner;
@@ -67,47 +66,16 @@ impl JsonRenderer<'_> {
             id,
             crate_id: item_id.krate().as_u32(),
             name: name.map(|sym| sym.to_string()),
-            span: span.and_then(|span| self.convert_span(span)),
-            visibility: self.convert_visibility(visibility),
+            span: span.and_then(|span| span.into_json(self)),
+            visibility: visibility.into_json(self),
             docs,
             attrs,
-            deprecation: deprecation.map(from_deprecation),
+            deprecation: deprecation.into_json(self),
             inner,
             links,
         })
     }
 
-    fn convert_span(&self, span: clean::Span) -> Option<Span> {
-        match span.filename(self.sess()) {
-            rustc_span::FileName::Real(name) => {
-                if let Some(local_path) = name.into_local_path() {
-                    let hi = span.hi(self.sess());
-                    let lo = span.lo(self.sess());
-                    Some(Span {
-                        filename: local_path,
-                        begin: (lo.line, lo.col.to_usize() + 1),
-                        end: (hi.line, hi.col.to_usize() + 1),
-                    })
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        }
-    }
-
-    fn convert_visibility(&self, v: Option<ty::Visibility<DefId>>) -> Visibility {
-        match v {
-            None => Visibility::Default,
-            Some(ty::Visibility::Public) => Visibility::Public,
-            Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate,
-            Some(ty::Visibility::Restricted(did)) => Visibility::Restricted {
-                parent: self.id_from_item_default(did.into()),
-                path: self.tcx.def_path(did).to_string_no_crate_verbose(),
-            },
-        }
-    }
-
     fn ids(&self, items: &[clean::Item]) -> Vec<Id> {
         items
             .iter()
@@ -141,11 +109,29 @@ where
     }
 }
 
+impl<T, U> FromClean<Box<T>> for U
+where
+    U: FromClean<T>,
+{
+    fn from_clean(opt: &Box<T>, renderer: &JsonRenderer<'_>) -> Self {
+        opt.as_ref().into_json(renderer)
+    }
+}
+
+impl<T, U> FromClean<Option<T>> for Option<U>
+where
+    U: FromClean<T>,
+{
+    fn from_clean(opt: &Option<T>, renderer: &JsonRenderer<'_>) -> Self {
+        opt.as_ref().map(|x| x.into_json(renderer))
+    }
+}
+
 impl<T, U> FromClean<Vec<T>> for Vec<U>
 where
     U: FromClean<T>,
 {
-    fn from_clean(items: &Vec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> {
+    fn from_clean(items: &Vec<T>, renderer: &JsonRenderer<'_>) -> Self {
         items.iter().map(|i| i.into_json(renderer)).collect()
     }
 }
@@ -154,35 +140,78 @@ impl<T, U> FromClean<ThinVec<T>> for Vec<U>
 where
     U: FromClean<T>,
 {
-    fn from_clean(items: &ThinVec<T>, renderer: &JsonRenderer<'_>) -> Vec<U> {
+    fn from_clean(items: &ThinVec<T>, renderer: &JsonRenderer<'_>) -> Self {
         items.iter().map(|i| i.into_json(renderer)).collect()
     }
 }
 
-pub(crate) fn from_deprecation(deprecation: attrs::Deprecation) -> Deprecation {
-    let attrs::Deprecation { since, note, suggestion: _ } = deprecation;
-    let since = match since {
-        DeprecatedSince::RustcVersion(version) => Some(version.to_string()),
-        DeprecatedSince::Future => Some("TBD".to_owned()),
-        DeprecatedSince::NonStandard(since) => Some(since.to_string()),
-        DeprecatedSince::Unspecified | DeprecatedSince::Err => None,
-    };
-    Deprecation { since, note: note.map(|s| s.to_string()) }
+impl FromClean<clean::Span> for Option<Span> {
+    fn from_clean(span: &clean::Span, renderer: &JsonRenderer<'_>) -> Self {
+        match span.filename(renderer.sess()) {
+            rustc_span::FileName::Real(name) => {
+                if let Some(local_path) = name.into_local_path() {
+                    let hi = span.hi(renderer.sess());
+                    let lo = span.lo(renderer.sess());
+                    Some(Span {
+                        filename: local_path,
+                        begin: (lo.line, lo.col.to_usize() + 1),
+                        end: (hi.line, hi.col.to_usize() + 1),
+                    })
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        }
+    }
 }
 
-impl FromClean<clean::GenericArgs> for GenericArgs {
-    fn from_clean(args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self {
-        use clean::GenericArgs::*;
-        match args {
-            AngleBracketed { args, constraints } => GenericArgs::AngleBracketed {
-                args: args.into_json(renderer),
-                constraints: constraints.into_json(renderer),
+impl FromClean<Option<ty::Visibility<DefId>>> for Visibility {
+    fn from_clean(v: &Option<ty::Visibility<DefId>>, renderer: &JsonRenderer<'_>) -> Self {
+        match v {
+            None => Visibility::Default,
+            Some(ty::Visibility::Public) => Visibility::Public,
+            Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate,
+            Some(ty::Visibility::Restricted(did)) => Visibility::Restricted {
+                parent: renderer.id_from_item_default((*did).into()),
+                path: renderer.tcx.def_path(*did).to_string_no_crate_verbose(),
             },
-            Parenthesized { inputs, output } => GenericArgs::Parenthesized {
+        }
+    }
+}
+
+impl FromClean<attrs::Deprecation> for Deprecation {
+    fn from_clean(deprecation: &attrs::Deprecation, _renderer: &JsonRenderer<'_>) -> Self {
+        let attrs::Deprecation { since, note, suggestion: _ } = deprecation;
+        let since = match since {
+            DeprecatedSince::RustcVersion(version) => Some(version.to_string()),
+            DeprecatedSince::Future => Some("TBD".to_string()),
+            DeprecatedSince::NonStandard(since) => Some(since.to_string()),
+            DeprecatedSince::Unspecified | DeprecatedSince::Err => None,
+        };
+        Deprecation { since, note: note.map(|sym| sym.to_string()) }
+    }
+}
+
+impl FromClean<clean::GenericArgs> for Option<Box<GenericArgs>> {
+    fn from_clean(generic_args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self {
+        use clean::GenericArgs::*;
+        match generic_args {
+            AngleBracketed { args, constraints } => {
+                if generic_args.is_empty() {
+                    None
+                } else {
+                    Some(Box::new(GenericArgs::AngleBracketed {
+                        args: args.into_json(renderer),
+                        constraints: constraints.into_json(renderer),
+                    }))
+                }
+            }
+            Parenthesized { inputs, output } => Some(Box::new(GenericArgs::Parenthesized {
                 inputs: inputs.into_json(renderer),
-                output: output.as_ref().map(|a| a.as_ref().into_json(renderer)),
-            },
-            ReturnTypeNotation => GenericArgs::ReturnTypeNotation,
+                output: output.into_json(renderer),
+            })),
+            ReturnTypeNotation => Some(Box::new(GenericArgs::ReturnTypeNotation)),
         }
     }
 }
@@ -191,7 +220,7 @@ impl FromClean<clean::GenericArg> for GenericArg {
     fn from_clean(arg: &clean::GenericArg, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericArg::*;
         match arg {
-            Lifetime(l) => GenericArg::Lifetime(convert_lifetime(l)),
+            Lifetime(l) => GenericArg::Lifetime(l.into_json(renderer)),
             Type(t) => GenericArg::Type(t.into_json(renderer)),
             Const(box c) => GenericArg::Const(c.into_json(renderer)),
             Infer => GenericArg::Infer,
@@ -199,17 +228,6 @@ impl FromClean<clean::GenericArg> for GenericArg {
     }
 }
 
-impl FromClean<clean::Constant> for Constant {
-    // FIXME(generic_const_items): Add support for generic const items.
-    fn from_clean(constant: &clean::Constant, renderer: &JsonRenderer<'_>) -> Self {
-        let tcx = renderer.tcx;
-        let expr = constant.expr(tcx);
-        let value = constant.value(tcx);
-        let is_literal = constant.is_literal(tcx);
-        Constant { expr, value, is_literal }
-    }
-}
-
 impl FromClean<clean::ConstantKind> for Constant {
     // FIXME(generic_const_items): Add support for generic const items.
     fn from_clean(constant: &clean::ConstantKind, renderer: &JsonRenderer<'_>) -> Self {
@@ -257,21 +275,25 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum
         StructFieldItem(f) => ItemEnum::StructField(f.into_json(renderer)),
         EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)),
         VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)),
-        FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), renderer)),
+        FunctionItem(f) => {
+            ItemEnum::Function(from_clean_function(f, true, header.unwrap(), renderer))
+        }
         ForeignFunctionItem(f, _) => {
-            ItemEnum::Function(from_function(f, false, header.unwrap(), renderer))
+            ItemEnum::Function(from_clean_function(f, false, header.unwrap(), renderer))
         }
-        TraitItem(t) => ItemEnum::Trait(t.as_ref().into_json(renderer)),
+        TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)),
         TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)),
-        MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)),
+        MethodItem(m, _) => {
+            ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer))
+        }
         RequiredMethodItem(m) => {
-            ItemEnum::Function(from_function(m, false, header.unwrap(), renderer))
+            ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer))
         }
-        ImplItem(i) => ItemEnum::Impl(i.as_ref().into_json(renderer)),
-        StaticItem(s) => ItemEnum::Static(convert_static(s, &rustc_hir::Safety::Safe, renderer)),
-        ForeignStaticItem(s, safety) => ItemEnum::Static(convert_static(s, safety, renderer)),
+        ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)),
+        StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)),
+        ForeignStaticItem(s, safety) => ItemEnum::Static(from_clean_static(s, *safety, renderer)),
         ForeignTypeItem => ItemEnum::ExternType,
-        TypeAliasItem(t) => ItemEnum::TypeAlias(t.as_ref().into_json(renderer)),
+        TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)),
         // FIXME(generic_const_items): Add support for generic free consts
         ConstantItem(ci) => ItemEnum::Constant {
             type_: ci.type_.into_json(renderer),
@@ -287,7 +309,7 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
         RequiredAssocConstItem(_generics, ty) => {
-            ItemEnum::AssocConst { type_: ty.as_ref().into_json(renderer), value: None }
+            ItemEnum::AssocConst { type_: ty.into_json(renderer), value: None }
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
         ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst {
@@ -359,32 +381,38 @@ impl FromClean<clean::Union> for Union {
     }
 }
 
-pub(crate) fn from_fn_header(header: &rustc_hir::FnHeader) -> FunctionHeader {
-    FunctionHeader {
-        is_async: header.is_async(),
-        is_const: header.is_const(),
-        is_unsafe: header.is_unsafe(),
-        abi: convert_abi(header.abi),
+impl FromClean<rustc_hir::FnHeader> for FunctionHeader {
+    fn from_clean(header: &rustc_hir::FnHeader, renderer: &JsonRenderer<'_>) -> Self {
+        FunctionHeader {
+            is_async: header.is_async(),
+            is_const: header.is_const(),
+            is_unsafe: header.is_unsafe(),
+            abi: header.abi.into_json(renderer),
+        }
     }
 }
 
-fn convert_abi(a: ExternAbi) -> Abi {
-    match a {
-        ExternAbi::Rust => Abi::Rust,
-        ExternAbi::C { unwind } => Abi::C { unwind },
-        ExternAbi::Cdecl { unwind } => Abi::Cdecl { unwind },
-        ExternAbi::Stdcall { unwind } => Abi::Stdcall { unwind },
-        ExternAbi::Fastcall { unwind } => Abi::Fastcall { unwind },
-        ExternAbi::Aapcs { unwind } => Abi::Aapcs { unwind },
-        ExternAbi::Win64 { unwind } => Abi::Win64 { unwind },
-        ExternAbi::SysV64 { unwind } => Abi::SysV64 { unwind },
-        ExternAbi::System { unwind } => Abi::System { unwind },
-        _ => Abi::Other(a.to_string()),
+impl FromClean<ExternAbi> for Abi {
+    fn from_clean(a: &ExternAbi, _renderer: &JsonRenderer<'_>) -> Self {
+        match *a {
+            ExternAbi::Rust => Abi::Rust,
+            ExternAbi::C { unwind } => Abi::C { unwind },
+            ExternAbi::Cdecl { unwind } => Abi::Cdecl { unwind },
+            ExternAbi::Stdcall { unwind } => Abi::Stdcall { unwind },
+            ExternAbi::Fastcall { unwind } => Abi::Fastcall { unwind },
+            ExternAbi::Aapcs { unwind } => Abi::Aapcs { unwind },
+            ExternAbi::Win64 { unwind } => Abi::Win64 { unwind },
+            ExternAbi::SysV64 { unwind } => Abi::SysV64 { unwind },
+            ExternAbi::System { unwind } => Abi::System { unwind },
+            _ => Abi::Other(a.to_string()),
+        }
     }
 }
 
-fn convert_lifetime(l: &clean::Lifetime) -> String {
-    l.0.to_string()
+impl FromClean<clean::Lifetime> for String {
+    fn from_clean(l: &clean::Lifetime, _renderer: &JsonRenderer<'_>) -> String {
+        l.0.to_string()
+    }
 }
 
 impl FromClean<clean::Generics> for Generics {
@@ -409,16 +437,16 @@ impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind {
     fn from_clean(kind: &clean::GenericParamDefKind, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericParamDefKind::*;
         match kind {
-            Lifetime { outlives } => GenericParamDefKind::Lifetime {
-                outlives: outlives.into_iter().map(convert_lifetime).collect(),
-            },
+            Lifetime { outlives } => {
+                GenericParamDefKind::Lifetime { outlives: outlives.into_json(renderer) }
+            }
             Type { bounds, default, synthetic } => GenericParamDefKind::Type {
                 bounds: bounds.into_json(renderer),
-                default: default.as_ref().map(|x| x.as_ref().into_json(renderer)),
+                default: default.into_json(renderer),
                 is_synthetic: *synthetic,
             },
             Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
-                type_: ty.as_ref().into_json(renderer),
+                type_: ty.into_json(renderer),
                 default: default.as_ref().map(|x| x.as_ref().clone()),
             },
         }
@@ -432,45 +460,14 @@ impl FromClean<clean::WherePredicate> for WherePredicate {
             BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate {
                 type_: ty.into_json(renderer),
                 bounds: bounds.into_json(renderer),
-                generic_params: bound_params
-                    .iter()
-                    .map(|x| {
-                        let name = x.name.to_string();
-                        let kind = match &x.kind {
-                            clean::GenericParamDefKind::Lifetime { outlives } => {
-                                GenericParamDefKind::Lifetime {
-                                    outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(),
-                                }
-                            }
-                            clean::GenericParamDefKind::Type { bounds, default, synthetic } => {
-                                GenericParamDefKind::Type {
-                                    bounds: bounds
-                                        .into_iter()
-                                        .map(|bound| bound.into_json(renderer))
-                                        .collect(),
-                                    default: default
-                                        .as_ref()
-                                        .map(|ty| ty.as_ref().into_json(renderer)),
-                                    is_synthetic: *synthetic,
-                                }
-                            }
-                            clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => {
-                                GenericParamDefKind::Const {
-                                    type_: ty.as_ref().into_json(renderer),
-                                    default: default.as_ref().map(|d| d.as_ref().clone()),
-                                }
-                            }
-                        };
-                        GenericParamDef { name, kind }
-                    })
-                    .collect(),
+                generic_params: bound_params.into_json(renderer),
             },
             RegionPredicate { lifetime, bounds } => WherePredicate::LifetimePredicate {
-                lifetime: convert_lifetime(lifetime),
+                lifetime: lifetime.into_json(renderer),
                 outlives: bounds
                     .iter()
                     .map(|bound| match bound {
-                        clean::GenericBound::Outlives(lt) => convert_lifetime(lt),
+                        clean::GenericBound::Outlives(lt) => lt.into_json(renderer),
                         _ => bug!("found non-outlives-bound on lifetime predicate"),
                     })
                     .collect(),
@@ -494,15 +491,15 @@ impl FromClean<clean::GenericBound> for GenericBound {
                 GenericBound::TraitBound {
                     trait_: trait_.into_json(renderer),
                     generic_params: generic_params.into_json(renderer),
-                    modifier: from_trait_bound_modifier(modifier),
+                    modifier: modifier.into_json(renderer),
                 }
             }
-            Outlives(lifetime) => GenericBound::Outlives(convert_lifetime(lifetime)),
+            Outlives(lifetime) => GenericBound::Outlives(lifetime.into_json(renderer)),
             Use(args) => GenericBound::Use(
                 args.iter()
                     .map(|arg| match arg {
                         clean::PreciseCapturingArg::Lifetime(lt) => {
-                            PreciseCapturingArg::Lifetime(convert_lifetime(lt))
+                            PreciseCapturingArg::Lifetime(lt.into_json(renderer))
                         }
                         clean::PreciseCapturingArg::Param(param) => {
                             PreciseCapturingArg::Param(param.to_string())
@@ -514,19 +511,22 @@ impl FromClean<clean::GenericBound> for GenericBound {
     }
 }
 
-pub(crate) fn from_trait_bound_modifier(
-    modifiers: &rustc_hir::TraitBoundModifiers,
-) -> TraitBoundModifier {
-    use rustc_hir as hir;
-    let hir::TraitBoundModifiers { constness, polarity } = modifiers;
-    match (constness, polarity) {
-        (hir::BoundConstness::Never, hir::BoundPolarity::Positive) => TraitBoundModifier::None,
-        (hir::BoundConstness::Never, hir::BoundPolarity::Maybe(_)) => TraitBoundModifier::Maybe,
-        (hir::BoundConstness::Maybe(_), hir::BoundPolarity::Positive) => {
-            TraitBoundModifier::MaybeConst
+impl FromClean<rustc_hir::TraitBoundModifiers> for TraitBoundModifier {
+    fn from_clean(
+        modifiers: &rustc_hir::TraitBoundModifiers,
+        _renderer: &JsonRenderer<'_>,
+    ) -> Self {
+        use rustc_hir as hir;
+        let hir::TraitBoundModifiers { constness, polarity } = modifiers;
+        match (constness, polarity) {
+            (hir::BoundConstness::Never, hir::BoundPolarity::Positive) => TraitBoundModifier::None,
+            (hir::BoundConstness::Never, hir::BoundPolarity::Maybe(_)) => TraitBoundModifier::Maybe,
+            (hir::BoundConstness::Maybe(_), hir::BoundPolarity::Positive) => {
+                TraitBoundModifier::MaybeConst
+            }
+            // FIXME: Fill out the rest of this matrix.
+            _ => TraitBoundModifier::None,
         }
-        // FIXME: Fill out the rest of this matrix.
-        _ => TraitBoundModifier::None,
     }
 }
 
@@ -540,35 +540,35 @@ impl FromClean<clean::Type> for Type {
         match ty {
             clean::Type::Path { path } => Type::ResolvedPath(path.into_json(renderer)),
             clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait {
-                lifetime: lt.as_ref().map(convert_lifetime),
+                lifetime: lt.into_json(renderer),
                 traits: bounds.into_json(renderer),
             }),
             Generic(s) => Type::Generic(s.to_string()),
             // FIXME: add dedicated variant to json Type?
             SelfTy => Type::Generic("Self".to_owned()),
             Primitive(p) => Type::Primitive(p.as_sym().to_string()),
-            BareFunction(f) => Type::FunctionPointer(Box::new(f.as_ref().into_json(renderer))),
+            BareFunction(f) => Type::FunctionPointer(Box::new(f.into_json(renderer))),
             Tuple(t) => Type::Tuple(t.into_json(renderer)),
-            Slice(t) => Type::Slice(Box::new(t.as_ref().into_json(renderer))),
+            Slice(t) => Type::Slice(Box::new(t.into_json(renderer))),
             Array(t, s) => {
-                Type::Array { type_: Box::new(t.as_ref().into_json(renderer)), len: s.to_string() }
+                Type::Array { type_: Box::new(t.into_json(renderer)), len: s.to_string() }
             }
             clean::Type::Pat(t, p) => Type::Pat {
-                type_: Box::new(t.as_ref().into_json(renderer)),
+                type_: Box::new(t.into_json(renderer)),
                 __pat_unstable_do_not_use: p.to_string(),
             },
             ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)),
             Infer => Type::Infer,
             RawPointer(mutability, type_) => Type::RawPointer {
                 is_mutable: *mutability == ast::Mutability::Mut,
-                type_: Box::new(type_.as_ref().into_json(renderer)),
+                type_: Box::new(type_.into_json(renderer)),
             },
             BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
-                lifetime: lifetime.as_ref().map(convert_lifetime),
+                lifetime: lifetime.into_json(renderer),
                 is_mutable: *mutability == ast::Mutability::Mut,
-                type_: Box::new(type_.as_ref().into_json(renderer)),
+                type_: Box::new(type_.into_json(renderer)),
             },
-            QPath(qpath) => qpath.as_ref().into_json(renderer),
+            QPath(qpath) => qpath.into_json(renderer),
             // FIXME(unsafe_binder): Implement rustdoc-json.
             UnsafeBinder(_) => todo!(),
         }
@@ -576,11 +576,24 @@ impl FromClean<clean::Type> for Type {
 }
 
 impl FromClean<clean::Path> for Path {
-    fn from_clean(path: &clean::Path, renderer: &JsonRenderer<'_>) -> Path {
+    fn from_clean(path: &clean::Path, renderer: &JsonRenderer<'_>) -> Self {
         Path {
             path: path.whole_name(),
             id: renderer.id_from_item_default(path.def_id().into()),
-            args: path.segments.last().map(|args| Box::new(args.args.into_json(renderer))),
+            args: {
+                if let Some((final_seg, rest_segs)) = path.segments.split_last() {
+                    // In general, `clean::Path` can hold things like
+                    // `std::vec::Vec::<u32>::new`, where generic args appear
+                    // in a middle segment. But for the places where `Path` is
+                    // used by rustdoc-json-types, generic args can only be
+                    // used in the final segment, e.g. `std::vec::Vec<u32>`. So
+                    // check that the non-final segments have no generic args.
+                    assert!(rest_segs.iter().all(|seg| seg.args.is_empty()));
+                    final_seg.args.into_json(renderer)
+                } else {
+                    None // no generics on any segments because there are no segments
+                }
+            },
         }
     }
 }
@@ -591,15 +604,15 @@ impl FromClean<clean::QPathData> for Type {
 
         Self::QualifiedPath {
             name: assoc.name.to_string(),
-            args: Box::new(assoc.args.into_json(renderer)),
+            args: assoc.args.into_json(renderer),
             self_type: Box::new(self_type.into_json(renderer)),
-            trait_: trait_.as_ref().map(|trait_| trait_.into_json(renderer)),
+            trait_: trait_.into_json(renderer),
         }
     }
 }
 
 impl FromClean<clean::Term> for Term {
-    fn from_clean(term: &clean::Term, renderer: &JsonRenderer<'_>) -> Term {
+    fn from_clean(term: &clean::Term, renderer: &JsonRenderer<'_>) -> Self {
         match term {
             clean::Term::Type(ty) => Term::Type(ty.into_json(renderer)),
             clean::Term::Constant(c) => Term::Constant(c.into_json(renderer)),
@@ -615,7 +628,7 @@ impl FromClean<clean::BareFunctionDecl> for FunctionPointer {
                 is_unsafe: safety.is_unsafe(),
                 is_const: false,
                 is_async: false,
-                abi: convert_abi(*abi),
+                abi: abi.into_json(renderer),
             },
             generic_params: generic_params.into_json(renderer),
             sig: decl.into_json(renderer),
@@ -694,17 +707,17 @@ impl FromClean<clean::Impl> for Impl {
                 .into_iter()
                 .map(|x| x.to_string())
                 .collect(),
-            trait_: trait_.as_ref().map(|path| path.into_json(renderer)),
+            trait_: trait_.into_json(renderer),
             for_: for_.into_json(renderer),
             items: renderer.ids(&items),
             is_negative,
             is_synthetic,
-            blanket_impl: blanket_impl.map(|x| x.as_ref().into_json(renderer)),
+            blanket_impl: blanket_impl.map(|x| x.into_json(renderer)),
         }
     }
 }
 
-pub(crate) fn from_function(
+pub(crate) fn from_clean_function(
     clean::Function { decl, generics }: &clean::Function,
     has_body: bool,
     header: rustc_hir::FnHeader,
@@ -713,7 +726,7 @@ pub(crate) fn from_function(
     Function {
         sig: decl.into_json(renderer),
         generics: generics.into_json(renderer),
-        header: from_fn_header(&header),
+        header: header.into_json(renderer),
         has_body,
     }
 }
@@ -735,7 +748,7 @@ impl FromClean<clean::Variant> for Variant {
     fn from_clean(variant: &clean::Variant, renderer: &JsonRenderer<'_>) -> Self {
         use clean::VariantKind::*;
 
-        let discriminant = variant.discriminant.as_ref().map(|d| d.into_json(renderer));
+        let discriminant = variant.discriminant.into_json(renderer);
 
         let kind = match &variant.kind {
             CLike => VariantKind::Plain,
@@ -768,10 +781,7 @@ impl FromClean<clean::Import> for Use {
         use clean::ImportKind::*;
         let (name, is_glob) = match import.kind {
             Simple(s) => (s.to_string(), false),
-            Glob => (
-                import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(),
-                true,
-            ),
+            Glob => (import.source.path.last_opt().unwrap_or(sym::asterisk).to_string(), true),
         };
         Use {
             source: import.source.path.whole_name(),
@@ -783,20 +793,22 @@ impl FromClean<clean::Import> for Use {
 }
 
 impl FromClean<clean::ProcMacro> for ProcMacro {
-    fn from_clean(mac: &clean::ProcMacro, _renderer: &JsonRenderer<'_>) -> Self {
+    fn from_clean(mac: &clean::ProcMacro, renderer: &JsonRenderer<'_>) -> Self {
         ProcMacro {
-            kind: from_macro_kind(mac.kind),
+            kind: mac.kind.into_json(renderer),
             helpers: mac.helpers.iter().map(|x| x.to_string()).collect(),
         }
     }
 }
 
-pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind {
-    use rustc_span::hygiene::MacroKind::*;
-    match kind {
-        Bang => MacroKind::Bang,
-        Attr => MacroKind::Attr,
-        Derive => MacroKind::Derive,
+impl FromClean<rustc_span::hygiene::MacroKind> for MacroKind {
+    fn from_clean(kind: &rustc_span::hygiene::MacroKind, _renderer: &JsonRenderer<'_>) -> Self {
+        use rustc_span::hygiene::MacroKind::*;
+        match kind {
+            Bang => MacroKind::Bang,
+            Attr => MacroKind::Attr,
+            Derive => MacroKind::Derive,
+        }
     }
 }
 
@@ -807,9 +819,9 @@ impl FromClean<clean::TypeAlias> for TypeAlias {
     }
 }
 
-fn convert_static(
+fn from_clean_static(
     stat: &clean::Static,
-    safety: &rustc_hir::Safety,
+    safety: rustc_hir::Safety,
     renderer: &JsonRenderer<'_>,
 ) -> Static {
     let tcx = renderer.tcx;
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 2feadce26d0..600a4b429f3 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -18,7 +18,6 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
-use rustc_session::features::StabilityExt;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustdoc_json_types as types;
 // It's important to use the FxHashMap from rustdoc_json_types here, instead of
@@ -148,7 +147,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
             .copied()
             .filter(|(_, stability, _)| {
                 // Describe only target features which the user can toggle
-                stability.is_toggle_permitted(sess).is_ok()
+                stability.toggle_allowed().is_ok()
             })
             .map(|(name, stability, implied_features)| {
                 types::TargetFeature {
@@ -164,7 +163,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
                             // Imply only target features which the user can toggle
                             feature_stability
                                 .get(name)
-                                .map(|stability| stability.is_toggle_permitted(sess).is_ok())
+                                .map(|stability| stability.toggle_allowed().is_ok())
                                 .unwrap_or(false)
                         })
                         .map(String::from)
@@ -377,8 +376,34 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
             self.serialize_and_write(output_crate, BufWriter::new(stdout().lock()), "<stdout>")
         }
     }
+}
 
-    fn cache(&self) -> &Cache {
-        &self.cache
-    }
+// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
+//
+// These assertions are here, not in `src/rustdoc-json-types/lib.rs` where the types are defined,
+// because we have access to `static_assert_size` here.
+#[cfg(target_pointer_width = "64")]
+mod size_asserts {
+    use rustc_data_structures::static_assert_size;
+
+    use super::types::*;
+    // tidy-alphabetical-start
+    static_assert_size!(AssocItemConstraint, 112);
+    static_assert_size!(Crate, 184);
+    static_assert_size!(ExternalCrate, 48);
+    static_assert_size!(FunctionPointer, 168);
+    static_assert_size!(GenericArg, 80);
+    static_assert_size!(GenericArgs, 104);
+    static_assert_size!(GenericBound, 72);
+    static_assert_size!(GenericParamDef, 136);
+    static_assert_size!(Impl, 304);
+    // `Item` contains a `PathBuf`, which is different sizes on different OSes.
+    static_assert_size!(Item, 528 + size_of::<std::path::PathBuf>());
+    static_assert_size!(ItemSummary, 32);
+    static_assert_size!(PolyTrait, 64);
+    static_assert_size!(PreciseCapturingArg, 32);
+    static_assert_size!(TargetFeature, 80);
+    static_assert_size!(Type, 80);
+    static_assert_size!(WherePredicate, 160);
+    // tidy-alphabetical-end
 }
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index f4e4cd924f7..2339a6b69cd 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -35,7 +35,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
     });
 
     let local_crate = ExternalCrate { crate_num: LOCAL_CRATE };
-    let prims: FxHashSet<PrimitiveType> = local_crate.primitives(tcx).iter().map(|p| p.1).collect();
+    let prims: FxHashSet<PrimitiveType> = local_crate.primitives(tcx).map(|(_, p)| p).collect();
 
     let crate_items = {
         let mut coll = ItemAndAliasCollector::new(&cx.cache);
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 9662dd85d67..91cddbe5a5b 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -6,8 +6,8 @@ use std::sync::Arc;
 use rustc_data_structures::sync::Lock;
 use rustc_errors::emitter::Emitter;
 use rustc_errors::registry::Registry;
-use rustc_errors::translation::{Translate, to_fluent_args};
-use rustc_errors::{Applicability, DiagCtxt, DiagInner, LazyFallbackBundle};
+use rustc_errors::translation::{Translator, to_fluent_args};
+use rustc_errors::{Applicability, DiagCtxt, DiagInner};
 use rustc_parse::{source_str_to_stream, unwrap_or_emit_fatal};
 use rustc_resolve::rustdoc::source_span_for_markdown_range;
 use rustc_session::parse::ParseSess;
@@ -36,11 +36,8 @@ fn check_rust_syntax(
     code_block: RustCodeBlock,
 ) {
     let buffer = Arc::new(Lock::new(Buffer::default()));
-    let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-        rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-        false,
-    );
-    let emitter = BufferEmitter { buffer: Arc::clone(&buffer), fallback_bundle };
+    let translator = rustc_driver::default_translator();
+    let emitter = BufferEmitter { buffer: Arc::clone(&buffer), translator };
 
     let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
     let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
@@ -149,17 +146,7 @@ struct Buffer {
 
 struct BufferEmitter {
     buffer: Arc<Lock<Buffer>>,
-    fallback_bundle: LazyFallbackBundle,
-}
-
-impl Translate for BufferEmitter {
-    fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
-        None
-    }
-
-    fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
-        &self.fallback_bundle
-    }
+    translator: Translator,
 }
 
 impl Emitter for BufferEmitter {
@@ -168,6 +155,7 @@ impl Emitter for BufferEmitter {
 
         let fluent_args = to_fluent_args(diag.args.iter());
         let translated_main_message = self
+            .translator
             .translate_message(&diag.messages[0].0, &fluent_args)
             .unwrap_or_else(|e| panic!("{e}"));
 
@@ -180,4 +168,8 @@ impl Emitter for BufferEmitter {
     fn source_map(&self) -> Option<&SourceMap> {
         None
     }
+
+    fn translator(&self) -> &Translator {
+        &self.translator
+    }
 }
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 1f93895ae07..0e72ddd9db1 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -37,8 +37,8 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
 // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line
 // are deliberately not in a doc comment, because they need not be in public docs.)
 //
-// Latest feature: Pretty printing of inline attributes changed
-pub const FORMAT_VERSION: u32 = 48;
+// Latest feature: Pretty printing of no_mangle attributes changed
+pub const FORMAT_VERSION: u32 = 53;
 
 /// The root of the emitted JSON blob.
 ///
@@ -277,8 +277,8 @@ pub struct PolyTrait {
 /// A set of generic arguments provided to a path segment, e.g.
 ///
 /// ```text
-/// std::option::Option::<u32>::None
-///                      ^^^^^
+/// std::option::Option<u32>
+///                    ^^^^^
 /// ```
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
@@ -331,7 +331,7 @@ pub enum GenericArg {
     Const(Constant),
     /// A generic argument that's explicitly set to be inferred.
     /// ```text
-    /// std::vec::Vec::<_>::new()
+    /// std::vec::Vec::<_>
     ///                 ^
     /// ```
     Infer,
@@ -362,7 +362,7 @@ pub struct AssocItemConstraint {
     /// The name of the associated type/constant.
     pub name: String,
     /// Arguments provided to the associated type/constant.
-    pub args: GenericArgs,
+    pub args: Option<Box<GenericArgs>>,
     /// The kind of bound applied to the associated type/constant.
     pub binding: AssocItemConstraintKind,
 }
@@ -1118,7 +1118,7 @@ pub enum Type {
         /// <core::slice::IterMut<'static, u32> as BetterIterator>::Item<'static>
         /// //                                                          ^^^^^^^^^
         /// ```
-        args: Box<GenericArgs>,
+        args: Option<Box<GenericArgs>>,
         /// The type with which this type is associated.
         ///
         /// ```ignore (incomplete expression)
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 2251525ae503fa196f6d7f9ce6d32eccb2d5f04
+Subproject 409fed7dc1553d49cb9a8c0637d12d65571346c
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 3a76c61489e..13cf82a062b 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -58,6 +58,7 @@ rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
 [features]
 integration = ["dep:tempfile"]
 internal = ["dep:clippy_lints_internal", "dep:tempfile"]
+jemalloc = []
 
 [package.metadata.rust-analyzer]
 # This package uses #[feature(rustc_private)]
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 08592f2521f..3b827cc5603 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -2,6 +2,7 @@ use crate::utils::{
     ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, expect_action, update_text_region_fn,
 };
 use itertools::Itertools;
+use rustc_lexer::{LiteralKind, TokenKind, tokenize};
 use std::collections::HashSet;
 use std::fmt::Write;
 use std::ops::Range;
@@ -342,7 +343,7 @@ fn parse_str_lit(s: &str) -> String {
         .and_then(|s| s.strip_suffix('"'))
         .unwrap_or_else(|| panic!("expected quoted string, found `{s}`"));
     let mut res = String::with_capacity(s.len());
-    rustc_literal_escaper::unescape_unicode(s, mode, &mut |_, ch| {
+    rustc_literal_escaper::unescape_str(s, |range, ch| {
         if let Ok(ch) = ch {
             res.push(ch);
         }
diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
index ae36bb76117..8f95e44bf85 100644
--- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -56,7 +56,7 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -
         .and_then(|trait_id| {
             cx.tcx.associated_items(trait_id).find_by_ident_and_kind(
                 cx.tcx,
-                Ident::from_str("Output"),
+                Ident::with_dummy_span(sym::Output),
                 ty::AssocTag::Type,
                 trait_id,
             )
diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs
index 61dfc0fc042..d9e88d6a401 100644
--- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::SpanRangeExt;
-use clippy_utils::{expr_or_init, path_def_id, paths, std_or_core};
+use clippy_utils::{expr_or_init, is_path_diagnostic_item, std_or_core, sym};
 use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
@@ -53,8 +53,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) ->
 
 fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
     if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
-        && let Some(fun_id) = path_def_id(cx, fun)
-        && paths::ALIGN_OF.matches(cx, fun_id)
+        && is_path_diagnostic_item(cx, fun, sym::mem_align_of)
         && let Some(args) = path.segments.last().and_then(|seg| seg.args)
         && let [GenericArg::Type(generic_ty)] = args.args
     {
diff --git a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
index 289b6b915d4..d3c39686976 100644
--- a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::attr::AttributeExt as _;
 use rustc_ast::token::CommentKind;
 use rustc_errors::Applicability;
 use rustc_hir::{AttrStyle, Attribute};
@@ -43,13 +44,19 @@ pub fn check(cx: &LateContext<'_>, doc: &str, range: Range<usize>, fragments: &F
                 "looks like a footnote ref, but has no matching footnote",
                 |diag| {
                     if this_fragment.kind == DocFragmentKind::SugaredDoc {
-                        let (doc_attr, (_, doc_attr_comment_kind)) = attrs
+                        let (doc_attr, (_, doc_attr_comment_kind), attr_style) = attrs
                             .iter()
                             .filter(|attr| attr.span().overlaps(this_fragment.span))
                             .rev()
-                            .find_map(|attr| Some((attr, attr.doc_str_and_comment_kind()?)))
+                            .find_map(|attr| {
+                                Some((
+                                    attr,
+                                    attr.doc_str_and_comment_kind()?,
+                                    attr.doc_resolution_scope()?,
+                                ))
+                            })
                             .unwrap();
-                        let (to_add, terminator) = match (doc_attr_comment_kind, doc_attr.style()) {
+                        let (to_add, terminator) = match (doc_attr_comment_kind, attr_style) {
                             (CommentKind::Line, AttrStyle::Outer) => ("\n///\n/// ", ""),
                             (CommentKind::Line, AttrStyle::Inner) => ("\n//!\n//! ", ""),
                             (CommentKind::Block, AttrStyle::Outer) => ("\n/** ", " */"),
diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
index ec4538039a9..7ba11c20f45 100644
--- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
@@ -42,9 +42,8 @@ pub fn check(
                 let mut test_attr_spans = vec![];
                 let filename = FileName::anon_source_code(&code);
 
-                let fallback_bundle =
-                    rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
-                let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
+                let translator = rustc_driver::default_translator();
+                let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
                 let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
                 #[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx
                 let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 6ed7c87915b..b0077a9b05f 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -7,6 +7,7 @@ use clippy_utils::{
     get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id,
 };
 use rustc_abi::ExternAbi;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_errors::Applicability;
 use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -155,7 +156,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
             let sig = match callee_ty_adjusted.kind() {
                 ty::FnDef(def, _) => {
                     // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location`
-                    if cx.tcx.has_attr(*def, sym::track_caller) {
+                    if find_attr!(cx.tcx.get_all_attrs(*def), AttributeKind::TrackCaller(..)) {
                         return;
                     }
 
@@ -236,7 +237,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
         },
         ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
             if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
-                && !cx.tcx.has_attr(method_def_id, sym::track_caller)
+                && !find_attr!(cx.tcx.get_all_attrs(method_def_id), AttributeKind::TrackCaller(..))
                 && check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
             {
                 let mut app = Applicability::MachineApplicable;
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index 70655838b6a..c0c23e217fd 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -15,6 +15,8 @@ use clippy_utils::ty::is_must_use_ty;
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{return_ty, trait_ref_of_method};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
+use rustc_span::Symbol;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 
 use core::ops::ControlFlow;
 
@@ -22,7 +24,7 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
 
 pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
     let attrs = cx.tcx.hir_attrs(item.hir_id());
-    let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
+    let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
     if let hir::ItemKind::Fn {
         ref sig,
         body: ref body_id,
@@ -31,9 +33,9 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
     {
         let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
-        if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
-        } else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
+        if let Some((attr_span, reason)) = attr {
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig);
+        } else if is_public && !is_proc_macro(attrs) && !find_attr!(attrs, AttributeKind::NoMangle(..)) {
             check_must_use_candidate(
                 cx,
                 sig.decl,
@@ -52,9 +54,9 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
         let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
         let attrs = cx.tcx.hir_attrs(item.hir_id());
-        let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
-        if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
+        let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
+        if let Some((attr_span, reason)) = attr {
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig);
         } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id).is_none() {
             check_must_use_candidate(
                 cx,
@@ -75,9 +77,9 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
         let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 
         let attrs = cx.tcx.hir_attrs(item.hir_id());
-        let attr = cx.tcx.get_attr(item.owner_id, sym::must_use);
-        if let Some(attr) = attr {
-            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig);
+        let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
+        if let Some((attr_span, reason)) = attr {
+            check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig);
         } else if let hir::TraitFn::Provided(eid) = *eid {
             let body = cx.tcx.hir_body(eid);
             if attr.is_none() && is_public && !is_proc_macro(attrs) {
@@ -103,7 +105,8 @@ fn check_needless_must_use(
     item_id: hir::OwnerId,
     item_span: Span,
     fn_header_span: Span,
-    attr: &Attribute,
+    attr_span: Span,
+    reason: Option<Symbol>,
     attrs: &[Attribute],
     sig: &FnSig<'_>,
 ) {
@@ -119,7 +122,7 @@ fn check_needless_must_use(
                 "this unit-returning function has a `#[must_use]` attribute",
                 |diag| {
                     diag.span_suggestion(
-                        attr.span(),
+                        attr_span,
                         "remove the attribute",
                         "",
                         Applicability::MachineApplicable,
@@ -137,11 +140,11 @@ fn check_needless_must_use(
                 MUST_USE_UNIT,
                 fn_header_span,
                 "this unit-returning function has a `#[must_use]` attribute",
-                Some(attr.span()),
+                Some(attr_span),
                 "remove `must_use`",
             );
         }
-    } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
+    } else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
         // Ignore async functions unless Future::Output type is a must_use type
         if sig.header.is_async() {
             let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs
index b55c11f2d5b..922db174e3d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::msrvs::Msrv;
-use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym};
+use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
@@ -220,5 +220,5 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 }
 
 fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    paths::SLICE_FROM_REF.matches_path(cx, expr)
+    clippy_utils::is_path_diagnostic_item(cx, expr, sym::slice_from_ref)
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs b/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs
index ec4b9c7ae2e..9276261606e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/io_other_error.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{expr_or_init, paths};
+use clippy_utils::{expr_or_init, is_path_diagnostic_item, sym};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
@@ -10,8 +10,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args
         && !expr.span.from_expansion()
         && !error_kind.span.from_expansion()
         && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind
-        && paths::IO_ERROR_NEW.matches_path(cx, path)
-        && paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind))
+        && is_path_diagnostic_item(cx, path, sym::io_error_new)
+        && let ExprKind::Path(QPath::Resolved(_, init_path)) = &expr_or_init(cx, error_kind).kind
+        && let [.., error_kind_ty, error_kind_variant] = init_path.segments
+        && cx.tcx.is_diagnostic_item(sym::io_errorkind, error_kind_ty.res.def_id())
+        && error_kind_variant.ident.name == sym::Other
         && msrv.meets(cx, msrvs::IO_ERROR_OTHER)
     {
         span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
index b71dde90691..0159c5d2ac1 100644
--- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
+++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
@@ -6,6 +6,8 @@ use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::{BytePos, Pos};
+use rustc_attr_data_structures::AttributeKind;
+use rustc_hir::Attribute;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -44,8 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
             let mut app = Applicability::MaybeIncorrect;
             let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(ident.span.lo()), "..", &mut app);
             for attr in attrs {
-                if let Some(ident) = attr.ident()
-                    && ident.name == rustc_span::sym::no_mangle
+                if let Attribute::Parsed(AttributeKind::NoMangle(attr_span)) = attr
                     && fn_sig.header.abi == ExternAbi::Rust
                     && let Some((fn_attrs, _)) = fn_snippet.rsplit_once("fn")
                     && !fn_attrs.contains("extern")
@@ -54,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
                         .span
                         .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
                         .shrink_to_lo();
-                    let attr_snippet = snippet(cx, attr.span(), "..");
+                    let attr_snippet = snippet(cx, *attr_span, "..");
 
                     span_lint_and_then(
                         cx,
diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
index 07ae92fa984..1b304dc5768 100644
--- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs
@@ -6,7 +6,9 @@ use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::declare_lint_pass;
-use rustc_span::{Span, sym};
+use rustc_span::{Span};
+use rustc_attr_data_structures::AttributeKind;
+use rustc_attr_data_structures::find_attr;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -74,7 +76,10 @@ fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, spa
         // We only show this warning for public exported methods.
         && cx.effective_visibilities.is_exported(fn_def)
         // We don't want to emit this lint if the `#[must_use]` attribute is already there.
-        && !cx.tcx.hir_attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use))
+        && !find_attr!(
+            cx.tcx.hir_attrs(owner_id.into()),
+            AttributeKind::MustUse { .. }
+        )
         && cx.tcx.visibility(fn_def.to_def_id()).is_public()
         && let ret_ty = return_ty(cx, owner_id)
         && let self_arg = nth_arg(cx, owner_id, 0)
diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
index 54d09ff9ee4..dda2f8cc1d0 100644
--- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
@@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs;
 use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{is_no_std_crate, paths};
+use clippy_utils::{is_no_std_crate, sym};
 use rustc_ast::{LitIntType, LitKind, UintTy};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
@@ -100,7 +100,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
             && let Some(start_snippet) = start.span.get_source_text(cx)
             && let Some(end_snippet) = end.span.get_source_text(cx)
         {
-            let should_emit_every_value = if let Some(step_def_id) = paths::ITER_STEP.only(cx)
+            let should_emit_every_value = if let Some(step_def_id) = cx.tcx.get_diagnostic_item(sym::range_step)
                 && implements_trait(cx, ty, step_def_id, &[])
             {
                 true
diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
index 7d7d74f27b3..3e847543e1c 100644
--- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
+++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs
@@ -2,7 +2,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_in_const_context, paths, sym};
+use clippy_utils::{is_in_const_context, is_path_diagnostic_item, sym};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
                     }
                 },
                 hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => {
-                    if paths::CHAR_TO_DIGIT.matches_path(cx, to_digits_call) {
+                    if is_path_diagnostic_item(cx, to_digits_call, sym::char_to_digit) {
                         Some((false, char_arg, radix_arg))
                     } else {
                         None
diff --git a/src/tools/clippy/clippy_lints/src/useless_concat.rs b/src/tools/clippy/clippy_lints/src/useless_concat.rs
index 1ed1fbb3b9c..96845adb04a 100644
--- a/src/tools/clippy/clippy_lints/src/useless_concat.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_concat.rs
@@ -1,8 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::macro_backtrace;
-use clippy_utils::paths::CONCAT;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::tokenize_with_text;
+use clippy_utils::{sym, tokenize_with_text};
 use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
@@ -43,7 +42,7 @@ impl LateLintPass<'_> for UselessConcat {
             // Get the direct parent of the expression.
             && let Some(macro_call) = macro_backtrace(expr.span).next()
             // Check if the `concat` macro from the `core` library.
-            && CONCAT.matches(cx, macro_call.def_id)
+            && cx.tcx.is_diagnostic_item(sym::macro_concat, macro_call.def_id)
             // We get the original code to parse it.
             && let Some(original_code) = snippet_opt(cx, macro_call.span)
             // This check allows us to ensure that the code snippet:
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index c7a2375c8df..913589319fc 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1886,7 +1886,10 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         _ => None,
     };
 
-    did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
+    did.is_some_and(|did| find_attr!(
+            cx.tcx.get_all_attrs(did),
+            AttributeKind::MustUse { ..}
+        ))
 }
 
 /// Checks if a function's body represents the identity function. Looks for bodies of the form:
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index f37a609497e..8bbcb220210 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -126,15 +126,6 @@ path_macros! {
     macro_path: PathNS::Macro,
 }
 
-// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items.
-pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of);
-pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit);
-pub static CONCAT: PathLookup = macro_path!(core::concat);
-pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new);
-pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other);
-pub static ITER_STEP: PathLookup = type_path!(core::iter::Step);
-pub static SLICE_FROM_REF: PathLookup = value_path!(core::slice::from_ref);
-
 // Paths in external crates
 pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt);
 pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt);
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index 32a992ccc2d..782b079ce09 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -31,6 +31,8 @@ use rustc_trait_selection::traits::{Obligation, ObligationCause};
 use std::assert_matches::debug_assert_matches;
 use std::collections::hash_map::Entry;
 use std::iter;
+use rustc_attr_data_structures::find_attr;
+use rustc_attr_data_structures::AttributeKind;
 
 use crate::path_res;
 use crate::paths::{PathNS, lookup_path_str};
@@ -326,8 +328,14 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 // Returns whether the type has #[must_use] attribute
 pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     match ty.kind() {
-        ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
-        ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
+        ty::Adt(adt, _) => find_attr!(
+            cx.tcx.get_all_attrs(adt.did()),
+            AttributeKind::MustUse { ..}
+        ),
+        ty::Foreign(did) => find_attr!(
+            cx.tcx.get_all_attrs(*did),
+            AttributeKind::MustUse { ..}
+        ),
         ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => {
             // for the Array case we don't need to care for the len == 0 case
             // because we don't want to lint functions returning empty arrays
@@ -337,7 +345,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
         ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => {
             for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() {
                 if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
-                    && cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use)
+                    && find_attr!(cx.tcx.get_all_attrs(trait_predicate.trait_ref.def_id), AttributeKind::MustUse { ..})
                 {
                     return true;
                 }
@@ -347,7 +355,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
         ty::Dynamic(binder, _, _) => {
             for predicate in *binder {
                 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
-                    && cx.tcx.has_attr(trait_ref.def_id, sym::must_use)
+                    && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { ..})
                 {
                     return true;
                 }
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 37adb14169a..426ba870f5f 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -13,6 +13,11 @@ extern crate rustc_interface;
 extern crate rustc_session;
 extern crate rustc_span;
 
+// See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
+// about jemalloc.
+#[cfg(feature = "jemalloc")]
+extern crate tikv_jemalloc_sys as jemalloc_sys;
+
 use clippy_utils::sym;
 use rustc_interface::interface;
 use rustc_session::EarlyDiagCtxt;
@@ -181,6 +186,36 @@ const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/ne
 #[allow(clippy::too_many_lines)]
 #[allow(clippy::ignored_unit_patterns)]
 pub fn main() {
+    // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
+    // about jemalloc.
+    #[cfg(feature = "jemalloc")]
+    {
+        use std::os::raw::{c_int, c_void};
+
+        #[used]
+        static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc;
+        #[used]
+        static _F2: unsafe extern "C" fn(*mut *mut c_void, usize, usize) -> c_int = jemalloc_sys::posix_memalign;
+        #[used]
+        static _F3: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::aligned_alloc;
+        #[used]
+        static _F4: unsafe extern "C" fn(usize) -> *mut c_void = jemalloc_sys::malloc;
+        #[used]
+        static _F5: unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void = jemalloc_sys::realloc;
+        #[used]
+        static _F6: unsafe extern "C" fn(*mut c_void) = jemalloc_sys::free;
+
+        #[cfg(target_os = "macos")]
+        {
+            unsafe extern "C" {
+                fn _rjem_je_zone_register();
+            }
+
+            #[used]
+            static _F7: unsafe extern "C" fn() = _rjem_je_zone_register;
+        }
+    }
+
     let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
 
     rustc_driver::init_rustc_env_logger(&early_dcx);
diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
index 5f8a4ce2363..49595e2fec2 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
@@ -9,28 +9,35 @@ if let StmtKind::Let(local) = stmt.kind
     && let ExprKind::Call(func, args) = e.kind
     && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
     && args.len() == 1
-    && let ExprKind::Call(func1, args1) = args[0].kind
-    && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
-    && args1.len() == 2
+    && let ExprKind::Block(block1, None) = args[0].kind
+    && block1.stmts.len() == 1
+    && let StmtKind::Let(local1) = block1.stmts[0].kind
+    && let Some(init1) = local1.init
+    && let ExprKind::Array(elements) = init1.kind
+    && elements.len() == 1
+    && let ExprKind::Call(func1, args1) = elements[0].kind
+    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
+    && args1.len() == 1
     && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
-    && let ExprKind::Array(elements) = inner.kind
-    && elements.len() == 2
-    && let ExprKind::Lit(ref lit) = elements[0].kind
+    && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind
+    && name.as_str() == "args"
+    && let Some(trailing_expr) = block1.expr
+    && let ExprKind::Call(func2, args2) = trailing_expr.kind
+    && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
+    && args2.len() == 2
+    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
+    && let ExprKind::Array(elements1) = inner1.kind
+    && elements1.len() == 2
+    && let ExprKind::Lit(ref lit) = elements1[0].kind
     && let LitKind::Str(s, _) = lit.node
     && s.as_str() == ""
-    && let ExprKind::Lit(ref lit1) = elements[1].kind
+    && let ExprKind::Lit(ref lit1) = elements1[1].kind
     && let LitKind::Str(s1, _) = lit1.node
     && s1.as_str() == "\n"
-    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
-    && let ExprKind::Array(elements1) = inner1.kind
-    && elements1.len() == 1
-    && let ExprKind::Call(func2, args2) = elements1[0].kind
-    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
-    && args2.len() == 1
-    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
     && block.expr.is_none()
-    && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
-    && name.as_str() == "print_text"
+    && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
+    && name1.as_str() == "print_text"
 {
     // report your lint here
 }
diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
index ecc25254311..4fc7b49464d 100644
--- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
@@ -19,25 +19,32 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
     && let ExprKind::Call(func, args) = e1.kind
     && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
     && args.len() == 1
-    && let ExprKind::Call(func1, args1) = args[0].kind
-    && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
-    && args1.len() == 2
+    && let ExprKind::Block(block2, None) = args[0].kind
+    && block2.stmts.len() == 1
+    && let StmtKind::Let(local) = block2.stmts[0].kind
+    && let Some(init) = local.init
+    && let ExprKind::Array(elements) = init.kind
+    && elements.len() == 1
+    && let ExprKind::Call(func1, args1) = elements[0].kind
+    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
+    && args1.len() == 1
     && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
-    && let ExprKind::Array(elements) = inner.kind
-    && elements.len() == 2
-    && let ExprKind::Lit(ref lit2) = elements[0].kind
+    && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
+    && name1.as_str() == "args"
+    && let Some(trailing_expr) = block2.expr
+    && let ExprKind::Call(func2, args2) = trailing_expr.kind
+    && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
+    && args2.len() == 2
+    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
+    && let ExprKind::Array(elements1) = inner1.kind
+    && elements1.len() == 2
+    && let ExprKind::Lit(ref lit2) = elements1[0].kind
     && let LitKind::Str(s, _) = lit2.node
     && s.as_str() == ""
-    && let ExprKind::Lit(ref lit3) = elements[1].kind
+    && let ExprKind::Lit(ref lit3) = elements1[1].kind
     && let LitKind::Str(s1, _) = lit3.node
     && s1.as_str() == "\n"
-    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
-    && let ExprKind::Array(elements1) = inner1.kind
-    && elements1.len() == 1
-    && let ExprKind::Call(func2, args2) = elements1[0].kind
-    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
-    && args2.len() == 1
-    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
+    && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
     && block1.expr.is_none()
     && block.expr.is_none()
 {
diff --git a/src/tools/clippy/tests/ui/manual_inspect.fixed b/src/tools/clippy/tests/ui/manual_inspect.fixed
index 9b768dbad70..00a19155a51 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.fixed
+++ b/src/tools/clippy/tests/ui/manual_inspect.fixed
@@ -154,7 +154,6 @@ fn main() {
     });
 
     let _ = [0]
-        //~^ suspicious_map
         .into_iter()
         .inspect(|&x| {
             //~^ manual_inspect
diff --git a/src/tools/clippy/tests/ui/manual_inspect.rs b/src/tools/clippy/tests/ui/manual_inspect.rs
index e679636201e..b3b17139cde 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.rs
+++ b/src/tools/clippy/tests/ui/manual_inspect.rs
@@ -165,7 +165,6 @@ fn main() {
     });
 
     let _ = [0]
-        //~^ suspicious_map
         .into_iter()
         .map(|x| {
             //~^ manual_inspect
diff --git a/src/tools/clippy/tests/ui/manual_inspect.stderr b/src/tools/clippy/tests/ui/manual_inspect.stderr
index 78b085fdfca..70c00c1f755 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.stderr
+++ b/src/tools/clippy/tests/ui/manual_inspect.stderr
@@ -157,25 +157,8 @@ LL |
 LL ~         println!("{}", x);
    |
 
-error: this call to `map()` won't have an effect on the call to `count()`
-  --> tests/ui/manual_inspect.rs:167:13
-   |
-LL |       let _ = [0]
-   |  _____________^
-LL | |
-LL | |         .into_iter()
-LL | |         .map(|x| {
-...  |
-LL | |         })
-LL | |         .count();
-   | |________________^
-   |
-   = help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect`
-   = note: `-D clippy::suspicious-map` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]`
-
 error: using `map` over `inspect`
-  --> tests/ui/manual_inspect.rs:170:10
+  --> tests/ui/manual_inspect.rs:169:10
    |
 LL |         .map(|x| {
    |          ^^^
@@ -188,7 +171,7 @@ LL ~             println!("{}", x);
    |
 
 error: using `map` over `inspect`
-  --> tests/ui/manual_inspect.rs:203:30
+  --> tests/ui/manual_inspect.rs:202:30
    |
 LL |     if let Some(x) = Some(1).map(|x| { println!("{x}");
    |                              ^^^
@@ -200,5 +183,5 @@ LL |         // Do not collapse code into this comment
 LL ~          }) {
    |
 
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs
index b5a2b7feac9..9fa26305f6b 100644
--- a/src/tools/compiletest/src/errors.rs
+++ b/src/tools/compiletest/src/errors.rs
@@ -16,6 +16,8 @@ pub enum ErrorKind {
     Suggestion,
     Warning,
     Raw,
+    /// Used for better recovery and diagnostics in compiletest.
+    Unknown,
 }
 
 impl ErrorKind {
@@ -31,21 +33,25 @@ impl ErrorKind {
 
     /// Either the canonical uppercase string, or some additional versions for compatibility.
     /// FIXME: consider keeping only the canonical versions here.
-    pub fn from_user_str(s: &str) -> ErrorKind {
-        match s {
+    fn from_user_str(s: &str) -> Option<ErrorKind> {
+        Some(match s {
             "HELP" | "help" => ErrorKind::Help,
             "ERROR" | "error" => ErrorKind::Error,
-            // `MONO_ITEM` makes annotations in `codegen-units` tests syntactically correct,
-            // but those tests never use the error kind later on.
-            "NOTE" | "note" | "MONO_ITEM" => ErrorKind::Note,
+            "NOTE" | "note" => ErrorKind::Note,
             "SUGGESTION" => ErrorKind::Suggestion,
             "WARN" | "WARNING" | "warn" | "warning" => ErrorKind::Warning,
             "RAW" => ErrorKind::Raw,
-            _ => panic!(
+            _ => return None,
+        })
+    }
+
+    pub fn expect_from_user_str(s: &str) -> ErrorKind {
+        ErrorKind::from_user_str(s).unwrap_or_else(|| {
+            panic!(
                 "unexpected diagnostic kind `{s}`, expected \
-                 `ERROR`, `WARN`, `NOTE`, `HELP` or `SUGGESTION`"
-            ),
-        }
+                 `ERROR`, `WARN`, `NOTE`, `HELP`, `SUGGESTION` or `RAW`"
+            )
+        })
     }
 }
 
@@ -58,6 +64,7 @@ impl fmt::Display for ErrorKind {
             ErrorKind::Suggestion => write!(f, "SUGGESTION"),
             ErrorKind::Warning => write!(f, "WARN"),
             ErrorKind::Raw => write!(f, "RAW"),
+            ErrorKind::Unknown => write!(f, "UNKNOWN"),
         }
     }
 }
@@ -65,6 +72,7 @@ impl fmt::Display for ErrorKind {
 #[derive(Debug)]
 pub struct Error {
     pub line_num: Option<usize>,
+    pub column_num: Option<usize>,
     /// What kind of message we expect (e.g., warning, error, suggestion).
     pub kind: ErrorKind,
     pub msg: String,
@@ -74,17 +82,6 @@ pub struct Error {
     pub require_annotation: bool,
 }
 
-impl Error {
-    pub fn render_for_expected(&self) -> String {
-        use colored::Colorize;
-        format!("{: <10}line {: >3}: {}", self.kind, self.line_num_str(), self.msg.cyan())
-    }
-
-    pub fn line_num_str(&self) -> String {
-        self.line_num.map_or("?".to_string(), |line_num| line_num.to_string())
-    }
-}
-
 /// Looks for either "//~| KIND MESSAGE" or "//~^^... KIND MESSAGE"
 /// The former is a "follow" that inherits its target from the preceding line;
 /// the latter is an "adjusts" that goes that many lines up.
@@ -168,8 +165,10 @@ fn parse_expected(
     let rest = line[tag.end()..].trim_start();
     let (kind_str, _) =
         rest.split_once(|c: char| c != '_' && !c.is_ascii_alphabetic()).unwrap_or((rest, ""));
-    let kind = ErrorKind::from_user_str(kind_str);
-    let untrimmed_msg = &rest[kind_str.len()..];
+    let (kind, untrimmed_msg) = match ErrorKind::from_user_str(kind_str) {
+        Some(kind) => (kind, &rest[kind_str.len()..]),
+        None => (ErrorKind::Unknown, rest),
+    };
     let msg = untrimmed_msg.strip_prefix(':').unwrap_or(untrimmed_msg).trim().to_owned();
 
     let line_num_adjust = &captures["adjust"];
@@ -182,6 +181,7 @@ fn parse_expected(
     } else {
         (false, Some(line_num - line_num_adjust.len()))
     };
+    let column_num = Some(tag.start() + 1);
 
     debug!(
         "line={:?} tag={:?} follow_prev={:?} kind={:?} msg={:?}",
@@ -191,7 +191,7 @@ fn parse_expected(
         kind,
         msg
     );
-    Some((follow_prev, Error { line_num, kind, msg, require_annotation: true }))
+    Some((follow_prev, Error { line_num, column_num, kind, msg, require_annotation: true }))
 }
 
 #[cfg(test)]
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 8bee9caacc9..2b203bb309c 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -593,7 +593,7 @@ impl TestProps {
                         config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS)
                     {
                         self.dont_require_annotations
-                            .insert(ErrorKind::from_user_str(err_kind.trim()));
+                            .insert(ErrorKind::expect_from_user_str(err_kind.trim()));
                     }
                 },
             );
diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs
index 6ed2b52c66d..a8e6416e56c 100644
--- a/src/tools/compiletest/src/json.rs
+++ b/src/tools/compiletest/src/json.rs
@@ -36,9 +36,7 @@ struct UnusedExternNotification {
 struct DiagnosticSpan {
     file_name: String,
     line_start: usize,
-    line_end: usize,
     column_start: usize,
-    column_end: usize,
     is_primary: bool,
     label: Option<String>,
     suggested_replacement: Option<String>,
@@ -148,6 +146,7 @@ pub fn parse_output(file_name: &str, output: &str) -> Vec<Error> {
             Ok(diagnostic) => push_actual_errors(&mut errors, &diagnostic, &[], file_name),
             Err(_) => errors.push(Error {
                 line_num: None,
+                column_num: None,
                 kind: ErrorKind::Raw,
                 msg: line.to_string(),
                 require_annotation: false,
@@ -193,25 +192,9 @@ fn push_actual_errors(
     // also ensure that `//~ ERROR E123` *always* works. The
     // assumption is that these multi-line error messages are on their
     // way out anyhow.
-    let with_code = |span: Option<&DiagnosticSpan>, text: &str| {
-        // FIXME(#33000) -- it'd be better to use a dedicated
-        // UI harness than to include the line/col number like
-        // this, but some current tests rely on it.
-        //
-        // Note: Do NOT include the filename. These can easily
-        // cause false matches where the expected message
-        // appears in the filename, and hence the message
-        // changes but the test still passes.
-        let span_str = match span {
-            Some(DiagnosticSpan { line_start, column_start, line_end, column_end, .. }) => {
-                format!("{line_start}:{column_start}: {line_end}:{column_end}")
-            }
-            None => format!("?:?: ?:?"),
-        };
-        match &diagnostic.code {
-            Some(code) => format!("{span_str}: {text} [{}]", code.code),
-            None => format!("{span_str}: {text}"),
-        }
+    let with_code = |text| match &diagnostic.code {
+        Some(code) => format!("{text} [{}]", code.code),
+        None => format!("{text}"),
     };
 
     // Convert multi-line messages into multiple errors.
@@ -225,8 +208,9 @@ fn push_actual_errors(
             || Regex::new(r"aborting due to \d+ previous errors?|\d+ warnings? emitted").unwrap();
         errors.push(Error {
             line_num: None,
+            column_num: None,
             kind,
-            msg: with_code(None, first_line),
+            msg: with_code(first_line),
             require_annotation: diagnostic.level != "failure-note"
                 && !RE.get_or_init(re_init).is_match(first_line),
         });
@@ -234,8 +218,9 @@ fn push_actual_errors(
         for span in primary_spans {
             errors.push(Error {
                 line_num: Some(span.line_start),
+                column_num: Some(span.column_start),
                 kind,
-                msg: with_code(Some(span), first_line),
+                msg: with_code(first_line),
                 require_annotation: true,
             });
         }
@@ -244,16 +229,18 @@ fn push_actual_errors(
         if primary_spans.is_empty() {
             errors.push(Error {
                 line_num: None,
+                column_num: None,
                 kind,
-                msg: with_code(None, next_line),
+                msg: with_code(next_line),
                 require_annotation: false,
             });
         } else {
             for span in primary_spans {
                 errors.push(Error {
                     line_num: Some(span.line_start),
+                    column_num: Some(span.column_start),
                     kind,
-                    msg: with_code(Some(span), next_line),
+                    msg: with_code(next_line),
                     require_annotation: false,
                 });
             }
@@ -266,6 +253,7 @@ fn push_actual_errors(
             for (index, line) in suggested_replacement.lines().enumerate() {
                 errors.push(Error {
                     line_num: Some(span.line_start + index),
+                    column_num: Some(span.column_start),
                     kind: ErrorKind::Suggestion,
                     msg: line.to_string(),
                     // Empty suggestions (suggestions to remove something) are common
@@ -288,6 +276,7 @@ fn push_actual_errors(
         if let Some(label) = &span.label {
             errors.push(Error {
                 line_num: Some(span.line_start),
+                column_num: Some(span.column_start),
                 kind: ErrorKind::Note,
                 msg: label.clone(),
                 // Empty labels (only underlining spans) are common and do not need annotations.
@@ -310,6 +299,7 @@ fn push_backtrace(
     if Path::new(&expansion.span.file_name) == Path::new(&file_name) {
         errors.push(Error {
             line_num: Some(expansion.span.line_start),
+            column_num: Some(expansion.span.column_start),
             kind: ErrorKind::Note,
             msg: format!("in this expansion of {}", expansion.macro_decl_name),
             require_annotation: true,
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 42c851ea999..980e89889ab 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -11,7 +11,7 @@ use std::{env, iter, str};
 
 use build_helper::fs::remove_and_create_dir_all;
 use camino::{Utf8Path, Utf8PathBuf};
-use colored::Colorize;
+use colored::{Color, Colorize};
 use regex::{Captures, Regex};
 use tracing::*;
 
@@ -677,9 +677,6 @@ impl<'test> TestCx<'test> {
             return;
         }
 
-        // On Windows, translate all '\' path separators to '/'
-        let file_name = self.testpaths.file.to_string().replace(r"\", "/");
-
         // On Windows, keep all '\' path separators to match the paths reported in the JSON output
         // from the compiler
         let diagnostic_file_name = if self.props.remap_src_base {
@@ -704,6 +701,7 @@ impl<'test> TestCx<'test> {
             .map(|e| Error { msg: self.normalize_output(&e.msg, &[]), ..e });
 
         let mut unexpected = Vec::new();
+        let mut unimportant = Vec::new();
         let mut found = vec![false; expected_errors.len()];
         for actual_error in actual_errors {
             for pattern in &self.props.error_patterns {
@@ -738,14 +736,9 @@ impl<'test> TestCx<'test> {
                         && expected_kinds.contains(&actual_error.kind)
                         && !self.props.dont_require_annotations.contains(&actual_error.kind)
                     {
-                        self.error(&format!(
-                            "{}:{}: unexpected {}: '{}'",
-                            file_name,
-                            actual_error.line_num_str(),
-                            actual_error.kind,
-                            actual_error.msg
-                        ));
                         unexpected.push(actual_error);
+                    } else {
+                        unimportant.push(actual_error);
                     }
                 }
             }
@@ -755,39 +748,140 @@ impl<'test> TestCx<'test> {
         // anything not yet found is a problem
         for (index, expected_error) in expected_errors.iter().enumerate() {
             if !found[index] {
-                self.error(&format!(
-                    "{}:{}: expected {} not found: {}",
-                    file_name,
-                    expected_error.line_num_str(),
-                    expected_error.kind,
-                    expected_error.msg
-                ));
                 not_found.push(expected_error);
             }
         }
 
         if !unexpected.is_empty() || !not_found.is_empty() {
             self.error(&format!(
-                "{} unexpected errors found, {} expected errors not found",
+                "{} unexpected diagnostics reported, {} expected diagnostics not reported",
                 unexpected.len(),
                 not_found.len()
             ));
-            println!("status: {}\ncommand: {}\n", proc_res.status, proc_res.cmdline);
+
+            // Emit locations in a format that is short (relative paths) but "clickable" in editors.
+            // Also normalize path separators to `/`.
+            let file_name = self
+                .testpaths
+                .file
+                .strip_prefix(self.config.src_root.as_str())
+                .unwrap_or(&self.testpaths.file)
+                .to_string()
+                .replace(r"\", "/");
+            let line_str = |e: &Error| {
+                let line_num = e.line_num.map_or("?".to_string(), |line_num| line_num.to_string());
+                // `file:?:NUM` may be confusing to editors and unclickable.
+                let opt_col_num = match e.column_num {
+                    Some(col_num) if line_num != "?" => format!(":{col_num}"),
+                    _ => "".to_string(),
+                };
+                format!("{file_name}:{line_num}{opt_col_num}")
+            };
+            let print_error = |e| println!("{}: {}: {}", line_str(e), e.kind, e.msg.cyan());
+            let push_suggestion =
+                |suggestions: &mut Vec<_>, e: &Error, kind, line, msg, color, rank| {
+                    let mut ret = String::new();
+                    if kind {
+                        ret += &format!("{} {}", "with kind".color(color), e.kind);
+                    }
+                    if line {
+                        if !ret.is_empty() {
+                            ret.push(' ');
+                        }
+                        ret += &format!("{} {}", "on line".color(color), line_str(e));
+                    }
+                    if msg {
+                        if !ret.is_empty() {
+                            ret.push(' ');
+                        }
+                        ret += &format!("{} {}", "with message".color(color), e.msg.cyan());
+                    }
+                    suggestions.push((ret, rank));
+                };
+            let show_suggestions = |mut suggestions: Vec<_>, prefix: &str, color| {
+                // Only show suggestions with the highest rank.
+                suggestions.sort_by_key(|(_, rank)| *rank);
+                if let Some(&(_, top_rank)) = suggestions.first() {
+                    for (suggestion, rank) in suggestions {
+                        if rank == top_rank {
+                            println!("  {} {suggestion}", prefix.color(color));
+                        }
+                    }
+                }
+            };
+
+            // Fuzzy matching quality:
+            // - message and line / message and kind - great, suggested
+            // - only message - good, suggested
+            // - known line and kind - ok, suggested
+            // - only known line - meh, but suggested
+            // - others are not worth suggesting
             if !unexpected.is_empty() {
-                println!("{}", "--- unexpected errors (from JSON output) ---".green());
+                let header = "--- reported in JSON output but not expected in test file ---";
+                println!("{}", header.green());
                 for error in &unexpected {
-                    println!("{}", error.render_for_expected());
+                    print_error(error);
+                    let mut suggestions = Vec::new();
+                    for candidate in &not_found {
+                        let mut push_red_suggestion = |line, msg, rank| {
+                            push_suggestion(
+                                &mut suggestions,
+                                candidate,
+                                candidate.kind != error.kind,
+                                line,
+                                msg,
+                                Color::Red,
+                                rank,
+                            )
+                        };
+                        if error.msg.contains(&candidate.msg) {
+                            push_red_suggestion(candidate.line_num != error.line_num, false, 0);
+                        } else if candidate.line_num.is_some()
+                            && candidate.line_num == error.line_num
+                        {
+                            push_red_suggestion(false, true, 1);
+                        }
+                    }
+
+                    show_suggestions(suggestions, "expected", Color::Red);
                 }
                 println!("{}", "---".green());
             }
             if !not_found.is_empty() {
-                println!("{}", "--- not found errors (from test file) ---".red());
+                let header = "--- expected in test file but not reported in JSON output ---";
+                println!("{}", header.red());
                 for error in &not_found {
-                    println!("{}", error.render_for_expected());
+                    print_error(error);
+                    let mut suggestions = Vec::new();
+                    for candidate in unexpected.iter().chain(&unimportant) {
+                        let mut push_green_suggestion = |line, msg, rank| {
+                            push_suggestion(
+                                &mut suggestions,
+                                candidate,
+                                candidate.kind != error.kind,
+                                line,
+                                msg,
+                                Color::Green,
+                                rank,
+                            )
+                        };
+                        if candidate.msg.contains(&error.msg) {
+                            push_green_suggestion(candidate.line_num != error.line_num, false, 0);
+                        } else if candidate.line_num.is_some()
+                            && candidate.line_num == error.line_num
+                        {
+                            push_green_suggestion(false, true, 1);
+                        }
+                    }
+
+                    show_suggestions(suggestions, "reported", Color::Green);
                 }
-                println!("{}", "---\n".red());
+                println!("{}", "---".red());
             }
-            panic!("errors differ from expected");
+            panic!(
+                "errors differ from expected\nstatus: {}\ncommand: {}\n",
+                proc_res.status, proc_res.cmdline
+            );
         }
     }
 
@@ -2073,7 +2167,6 @@ impl<'test> TestCx<'test> {
             println!("{}", String::from_utf8_lossy(&output.stdout));
             eprintln!("{}", String::from_utf8_lossy(&output.stderr));
         } else {
-            use colored::Colorize;
             eprintln!("warning: no pager configured, falling back to unified diff");
             eprintln!(
                 "help: try configuring a git pager (e.g. `delta`) with `git config --global core.pager delta`"
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index 029da1c1898..60e8e16e25e 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -221,6 +221,10 @@ impl TestCx<'_> {
             cmd.env("REMOTE_TEST_CLIENT", remote_test_client);
         }
 
+        if let Some(runner) = &self.config.runner {
+            cmd.env("RUNNER", runner);
+        }
+
         // We don't want RUSTFLAGS set from the outside to interfere with
         // compiler flags set in the test cases:
         cmd.env_remove("RUSTFLAGS");
diff --git a/src/tools/enzyme b/src/tools/enzyme
-Subproject a35f4f773118ccfbd8d05102eb12a34097b1ee5
+Subproject b5098d515d5e1bd0f5470553bc0d18da9794ca8
diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs
index 8c9e4c8bb3a..0a4051fcbe8 100644
--- a/src/tools/jsondoclint/src/validator.rs
+++ b/src/tools/jsondoclint/src/validator.rs
@@ -271,7 +271,7 @@ impl<'a> Validator<'a> {
             Type::RawPointer { is_mutable: _, type_ } => self.check_type(&**type_),
             Type::BorrowedRef { lifetime: _, is_mutable: _, type_ } => self.check_type(&**type_),
             Type::QualifiedPath { name: _, args, self_type, trait_ } => {
-                self.check_generic_args(&**args);
+                self.check_opt_generic_args(&args);
                 self.check_type(&**self_type);
                 if let Some(trait_) = trait_ {
                     self.check_path(trait_, PathKind::Trait);
@@ -309,13 +309,12 @@ impl<'a> Validator<'a> {
             self.fail(&x.id, ErrorKind::Custom(format!("No entry in '$.paths' for {x:?}")));
         }
 
-        if let Some(args) = &x.args {
-            self.check_generic_args(&**args);
-        }
+        self.check_opt_generic_args(&x.args);
     }
 
-    fn check_generic_args(&mut self, x: &'a GenericArgs) {
-        match x {
+    fn check_opt_generic_args(&mut self, x: &'a Option<Box<GenericArgs>>) {
+        let Some(x) = x else { return };
+        match &**x {
             GenericArgs::AngleBracketed { args, constraints } => {
                 args.iter().for_each(|arg| self.check_generic_arg(arg));
                 constraints.iter().for_each(|bind| self.check_assoc_item_constraint(bind));
@@ -355,7 +354,7 @@ impl<'a> Validator<'a> {
     }
 
     fn check_assoc_item_constraint(&mut self, bind: &'a AssocItemConstraint) {
-        self.check_generic_args(&bind.args);
+        self.check_opt_generic_args(&bind.args);
         match &bind.binding {
             AssocItemConstraintKind::Equality(term) => self.check_term(term),
             AssocItemConstraintKind::Constraint(bounds) => {
diff --git a/src/tools/linkchecker/linkcheck.sh b/src/tools/linkchecker/linkcheck.sh
index 6c1e668a7f0..d230610a6e7 100755
--- a/src/tools/linkchecker/linkcheck.sh
+++ b/src/tools/linkchecker/linkcheck.sh
@@ -98,6 +98,7 @@ then
     nightly_hash=$(rustc +nightly -Vv | grep commit-hash | cut -f2 -d" ")
     url="https://raw.githubusercontent.com/rust-lang/rust"
     mkdir linkchecker
+    curl -o linkchecker/Cargo.lock ${url}/${nightly_hash}/Cargo.lock
     curl -o linkchecker/Cargo.toml ${url}/${nightly_hash}/src/tools/linkchecker/Cargo.toml
     curl -o linkchecker/main.rs ${url}/${nightly_hash}/src/tools/linkchecker/main.rs
 fi
diff --git a/src/tools/lint-docs/Cargo.toml b/src/tools/lint-docs/Cargo.toml
index f1ffda75ac0..e914a2df2ba 100644
--- a/src/tools/lint-docs/Cargo.toml
+++ b/src/tools/lint-docs/Cargo.toml
@@ -7,7 +7,7 @@ description = "A script to extract the lint documentation for the rustc book."
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-rustc-literal-escaper = "0.0.2"
+rustc-literal-escaper = "0.0.4"
 serde_json = "1.0.57"
 tempfile = "3.1.0"
 walkdir = "2.3.1"
diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs
index 6bb18c2bced..b33344ca5dd 100644
--- a/src/tools/lint-docs/src/lib.rs
+++ b/src/tools/lint-docs/src/lib.rs
@@ -4,7 +4,7 @@ use std::fs;
 use std::path::{Path, PathBuf};
 use std::process::Command;
 
-use rustc_literal_escaper::{Mode, unescape_unicode};
+use rustc_literal_escaper::unescape_str;
 use walkdir::WalkDir;
 
 mod groups;
@@ -218,7 +218,7 @@ impl<'a> LintExtractor<'a> {
                         } else if let Some(text) = line.strip_prefix("#[doc = \"") {
                             let escaped = text.strip_suffix("\"]").unwrap();
                             let mut buf = String::new();
-                            unescape_unicode(escaped, Mode::Str, &mut |_, c| match c {
+                            unescape_str(escaped, |_, res| match res {
                                 Ok(c) => buf.push(c),
                                 Err(err) => {
                                     assert!(!err.is_fatal(), "failed to unescape string literal")
diff --git a/src/tools/miri/tests/pass/fn_align.rs b/src/tools/miri/tests/pass/fn_align.rs
index 550bb1cb4d7..28f92995880 100644
--- a/src/tools/miri/tests/pass/fn_align.rs
+++ b/src/tools/miri/tests/pass/fn_align.rs
@@ -1,21 +1,21 @@
 //@compile-flags: -Zmin-function-alignment=8
 #![feature(fn_align)]
 
-// When a function uses `repr(align(N))`, the function address should be a multiple of `N`.
+// When a function uses `align(N)`, the function address should be a multiple of `N`.
 
-#[repr(align(256))]
+#[align(256)]
 fn foo() {}
 
-#[repr(align(16))]
+#[align(16)]
 fn bar() {}
 
-#[repr(align(4))]
+#[align(4)]
 fn baz() {}
 
 fn main() {
     assert!((foo as usize).is_multiple_of(256));
     assert!((bar as usize).is_multiple_of(16));
 
-    // The maximum of `repr(align(N))` and `-Zmin-function-alignment=N` is used.
+    // The maximum of `align(N)` and `-Zmin-function-alignment=N` is used.
     assert!((baz as usize).is_multiple_of(8));
 }
diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs
index a7081d4f86a..72a1e062a38 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -6,7 +6,7 @@ use crate::command::Command;
 use crate::env::env_var;
 use crate::path_helpers::cwd;
 use crate::util::set_host_compiler_dylib_path;
-use crate::{is_aix, is_darwin, is_msvc, is_windows, uname};
+use crate::{is_aix, is_darwin, is_msvc, is_windows, target, uname};
 
 /// Construct a new `rustc` invocation. This will automatically set the library
 /// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this.
@@ -27,9 +27,15 @@ pub fn bare_rustc() -> Rustc {
 #[must_use]
 pub struct Rustc {
     cmd: Command,
+    target: Option<String>,
 }
 
-crate::macros::impl_common_helpers!(Rustc);
+// Only fill in the target just before execution, so that it can be overridden.
+crate::macros::impl_common_helpers!(Rustc, |rustc: &mut Rustc| {
+    if let Some(target) = &rustc.target {
+        rustc.cmd.arg(&format!("--target={target}"));
+    }
+});
 
 pub fn rustc_path() -> String {
     env_var("RUSTC")
@@ -46,19 +52,22 @@ impl Rustc {
     // `rustc` invocation constructor methods
 
     /// Construct a new `rustc` invocation. This will automatically set the library
-    /// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this.
+    /// search path as `-L cwd()` and also the compilation target.
+    /// Use [`bare_rustc`] to avoid this.
     #[track_caller]
     pub fn new() -> Self {
         let mut cmd = setup_common();
         cmd.arg("-L").arg(cwd());
-        Self { cmd }
+
+        // Automatically default to cross-compilation
+        Self { cmd, target: Some(target()) }
     }
 
     /// Construct a bare `rustc` invocation with no flags set.
     #[track_caller]
     pub fn bare() -> Self {
         let cmd = setup_common();
-        Self { cmd }
+        Self { cmd, target: None }
     }
 
     // Argument provider methods
@@ -234,8 +243,9 @@ impl Rustc {
 
     /// Specify the target triple, or a path to a custom target json spec file.
     pub fn target<S: AsRef<str>>(&mut self, target: S) -> &mut Self {
-        let target = target.as_ref();
-        self.cmd.arg(format!("--target={target}"));
+        // We store the target as a separate field, so that it can be specified multiple times.
+        // This is in particular useful to override the default target set in Rustc::new().
+        self.target = Some(target.as_ref().to_string());
         self
     }
 
diff --git a/src/tools/run-make-support/src/external_deps/rustdoc.rs b/src/tools/run-make-support/src/external_deps/rustdoc.rs
index 7040fb667cf..33e5f04d303 100644
--- a/src/tools/run-make-support/src/external_deps/rustdoc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs
@@ -3,21 +3,36 @@ use std::path::Path;
 
 use crate::command::Command;
 use crate::env::env_var;
+use crate::target;
 use crate::util::set_host_compiler_dylib_path;
 
-/// Construct a new `rustdoc` invocation. This will configure the host compiler runtime libs.
+/// Construct a new `rustdoc` invocation with target automatically set to cross-compile target and
+/// with host compiler runtime libs configured. Use [`bare_rustdoc`] to avoid automatically setting
+/// cross-compile target.
 #[track_caller]
 pub fn rustdoc() -> Rustdoc {
     Rustdoc::new()
 }
 
+/// Bare `rustdoc` invocation, no args set.
+#[track_caller]
+pub fn bare_rustdoc() -> Rustdoc {
+    Rustdoc::bare()
+}
+
 #[derive(Debug)]
 #[must_use]
 pub struct Rustdoc {
     cmd: Command,
+    target: Option<String>,
 }
 
-crate::macros::impl_common_helpers!(Rustdoc);
+// Only fill in the target just before execution, so that it can be overridden.
+crate::macros::impl_common_helpers!(Rustdoc, |rustdoc: &mut Rustdoc| {
+    if let Some(target) = &rustdoc.target {
+        rustdoc.cmd.arg(&format!("--target={target}"));
+    }
+});
 
 #[track_caller]
 fn setup_common() -> Command {
@@ -28,11 +43,20 @@ fn setup_common() -> Command {
 }
 
 impl Rustdoc {
-    /// Construct a bare `rustdoc` invocation. This will configure the host compiler runtime libs.
+    /// Construct a new `rustdoc` invocation with target automatically set to cross-compile target
+    /// and with host compiler runtime libs configured. Use [`bare_rustdoc`] to avoid automatically
+    /// setting cross-compile target.
     #[track_caller]
     pub fn new() -> Self {
         let cmd = setup_common();
-        Self { cmd }
+        Self { cmd, target: Some(target()) }
+    }
+
+    /// Bare `rustdoc` invocation, no args set.
+    #[track_caller]
+    pub fn bare() -> Self {
+        let cmd = setup_common();
+        Self { cmd, target: None }
     }
 
     /// Specify where an external library is located.
@@ -85,8 +109,9 @@ impl Rustdoc {
 
     /// Specify the target triple, or a path to a custom target json spec file.
     pub fn target<S: AsRef<str>>(&mut self, target: S) -> &mut Self {
-        let target = target.as_ref();
-        self.cmd.arg(format!("--target={target}"));
+        // We store the target as a separate field, so that it can be specified multiple times.
+        // This is in particular useful to override the default target set in `Rustdoc::new()`.
+        self.target = Some(target.as_ref().to_string());
         self
     }
 
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index f37b38ac0b1..67d8c351a59 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -68,7 +68,7 @@ pub use llvm::{
 };
 pub use python::python_command;
 pub use rustc::{bare_rustc, rustc, rustc_path, Rustc};
-pub use rustdoc::{rustdoc, Rustdoc};
+pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
 
 /// [`diff`][mod@diff] is implemented in terms of the [similar] library.
 ///
@@ -83,7 +83,7 @@ pub use run::{cmd, run, run_fail, run_with_args};
 
 /// Helpers for checking target information.
 pub use targets::{
-    apple_os, is_aix, is_darwin, is_msvc, is_windows, is_windows_gnu, is_win7, llvm_components_contain,
+    apple_os, is_aix, is_darwin, is_msvc, is_windows, is_windows_gnu, is_windows_msvc, is_win7, llvm_components_contain,
     target, uname,
 };
 
diff --git a/src/tools/run-make-support/src/macros.rs b/src/tools/run-make-support/src/macros.rs
index 9d5cc4e5876..b9208382a98 100644
--- a/src/tools/run-make-support/src/macros.rs
+++ b/src/tools/run-make-support/src/macros.rs
@@ -23,10 +23,16 @@
 /// }
 /// ```
 ///
+/// You can pass an optional second parameter which should be a function that is passed
+/// `&mut self` just before the command is executed.
+///
 /// [`Command`]: crate::command::Command
 /// [`CompletedProcess`]: crate::command::CompletedProcess
 macro_rules! impl_common_helpers {
     ($wrapper: ident) => {
+        $crate::macros::impl_common_helpers!($wrapper, |_| {});
+    };
+    ($wrapper: ident, $before_exec: expr) => {
         impl $wrapper {
             /// In very rare circumstances, you may need a e.g. `bare_rustc()` or `bare_rustdoc()`
             /// with host runtime libs configured, but want the underlying raw
@@ -130,12 +136,14 @@ macro_rules! impl_common_helpers {
             /// Run the constructed command and assert that it is successfully run.
             #[track_caller]
             pub fn run(&mut self) -> crate::command::CompletedProcess {
+                $before_exec(&mut *self);
                 self.cmd.run()
             }
 
             /// Run the constructed command and assert that it does not successfully run.
             #[track_caller]
             pub fn run_fail(&mut self) -> crate::command::CompletedProcess {
+                $before_exec(&mut *self);
                 self.cmd.run_fail()
             }
 
@@ -145,6 +153,7 @@ macro_rules! impl_common_helpers {
             /// whenever possible.
             #[track_caller]
             pub fn run_unchecked(&mut self) -> crate::command::CompletedProcess {
+                $before_exec(&mut *self);
                 self.cmd.run_unchecked()
             }
 
diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs
index 60e711d3402..b95f3a5cfe5 100644
--- a/src/tools/run-make-support/src/run.rs
+++ b/src/tools/run-make-support/src/run.rs
@@ -1,4 +1,4 @@
-use std::ffi::OsStr;
+use std::ffi::{OsStr, OsString};
 use std::path::PathBuf;
 use std::{env, panic};
 
@@ -22,6 +22,20 @@ fn run_common(name: &str, args: Option<&[&str]>) -> Command {
         cmd.arg("0");
         cmd.arg(bin_path);
         cmd
+    } else if let Ok(runner) = std::env::var("RUNNER") {
+        let mut args = split_maybe_args(&runner);
+
+        let prog = args.remove(0);
+        let mut cmd = Command::new(prog);
+
+        for arg in args {
+            cmd.arg(arg);
+        }
+
+        cmd.arg("--");
+        cmd.arg(bin_path);
+
+        cmd
     } else {
         Command::new(bin_path)
     };
@@ -92,3 +106,12 @@ pub fn cmd<S: AsRef<OsStr>>(program: S) -> Command {
     command.env("LC_ALL", "C"); // force english locale
     command
 }
+
+fn split_maybe_args(s: &str) -> Vec<OsString> {
+    // FIXME(132599): implement proper env var/shell argument splitting.
+    s.split(' ')
+        .filter_map(|s| {
+            if s.chars().all(|c| c.is_whitespace()) { None } else { Some(OsString::from(s)) }
+        })
+        .collect()
+}
diff --git a/src/tools/run-make-support/src/targets.rs b/src/tools/run-make-support/src/targets.rs
index 86edbdf750b..1ab2e2ab2be 100644
--- a/src/tools/run-make-support/src/targets.rs
+++ b/src/tools/run-make-support/src/targets.rs
@@ -28,6 +28,12 @@ pub fn is_windows_gnu() -> bool {
     target().ends_with("windows-gnu")
 }
 
+/// Check if target is windows-msvc.
+#[must_use]
+pub fn is_windows_msvc() -> bool {
+    target().ends_with("windows-msvc")
+}
+
 /// Check if target is win7.
 #[must_use]
 pub fn is_win7() -> bool {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index c618e4bdce7..00408e95ae6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -11,12 +11,12 @@ use syntax::{AstPtr, ast};
 use triomphe::Arc;
 
 use crate::{
-    AssocItemId, AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, EnumVariantId,
-    EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId,
-    FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroExpander,
-    MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId,
-    StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId,
-    TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId,
+    AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc,
+    EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc,
+    FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc,
+    MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId,
+    ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId,
+    TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId,
     attr::{Attrs, AttrsWithOwner},
     expr_store::{
         Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, scope::ExprScopes,
@@ -90,7 +90,10 @@ pub trait InternDatabase: RootQueryDb {
 
     #[salsa::interned]
     fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
-    // // endregion: items
+    // endregion: items
+
+    #[salsa::interned]
+    fn intern_block(&self, loc: BlockLoc) -> BlockId;
 }
 
 #[query_group::query_group]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
index 03683ec9203..efa1374a446 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
@@ -11,7 +11,7 @@ use base_db::FxIndexSet;
 use cfg::CfgOptions;
 use either::Either;
 use hir_expand::{
-    HirFileId, InFile, Intern, MacroDefId,
+    HirFileId, InFile, MacroDefId,
     mod_path::tool_path,
     name::{AsName, Name},
     span_map::SpanMapRef,
@@ -2148,7 +2148,7 @@ impl ExprCollector<'_> {
     ) -> ExprId {
         let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| {
             let ast_id = self.expander.in_file(file_local_id);
-            BlockLoc { ast_id, module: self.module }.intern(self.db)
+            self.db.intern_block(BlockLoc { ast_id, module: self.module })
         });
 
         let (module, def_map) =
@@ -2815,6 +2815,51 @@ impl ExprCollector<'_> {
                 mutability: Mutability::Shared,
             })
         };
+
+        // Assume that rustc version >= 1.89.0 iff lang item `format_arguments` exists
+        // but `format_unsafe_arg` does not
+        let fmt_args =
+            || crate::lang_item::lang_item(self.db, self.module.krate(), LangItem::FormatArguments);
+        let fmt_unsafe_arg =
+            || crate::lang_item::lang_item(self.db, self.module.krate(), LangItem::FormatUnsafeArg);
+        let use_format_args_since_1_89_0 = fmt_args().is_some() && fmt_unsafe_arg().is_none();
+
+        let idx = if use_format_args_since_1_89_0 {
+            self.collect_format_args_impl(
+                syntax_ptr,
+                fmt,
+                hygiene,
+                argmap,
+                lit_pieces,
+                format_options,
+            )
+        } else {
+            self.collect_format_args_before_1_89_0_impl(
+                syntax_ptr,
+                fmt,
+                argmap,
+                lit_pieces,
+                format_options,
+            )
+        };
+
+        self.source_map
+            .template_map
+            .get_or_insert_with(Default::default)
+            .format_args_to_captures
+            .insert(idx, (hygiene, mappings));
+        idx
+    }
+
+    /// `format_args!` expansion implementation for rustc versions < `1.89.0`
+    fn collect_format_args_before_1_89_0_impl(
+        &mut self,
+        syntax_ptr: AstPtr<ast::Expr>,
+        fmt: FormatArgs,
+        argmap: FxIndexSet<(usize, ArgumentType)>,
+        lit_pieces: ExprId,
+        format_options: ExprId,
+    ) -> ExprId {
         let arguments = &*fmt.arguments.arguments;
 
         let args = if arguments.is_empty() {
@@ -2902,19 +2947,181 @@ impl ExprCollector<'_> {
             });
         }
 
-        let idx = self.alloc_expr(
+        self.alloc_expr(
             Expr::Call {
                 callee: new_v1_formatted,
                 args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
             },
             syntax_ptr,
-        );
-        self.source_map
-            .template_map
-            .get_or_insert_with(Default::default)
-            .format_args_to_captures
-            .insert(idx, (hygiene, mappings));
-        idx
+        )
+    }
+
+    /// `format_args!` expansion implementation for rustc versions >= `1.89.0`,
+    /// especially since [this PR](https://github.com/rust-lang/rust/pull/140748)
+    fn collect_format_args_impl(
+        &mut self,
+        syntax_ptr: AstPtr<ast::Expr>,
+        fmt: FormatArgs,
+        hygiene: HygieneId,
+        argmap: FxIndexSet<(usize, ArgumentType)>,
+        lit_pieces: ExprId,
+        format_options: ExprId,
+    ) -> ExprId {
+        let arguments = &*fmt.arguments.arguments;
+
+        let (let_stmts, args) = if arguments.is_empty() {
+            (
+                // Generate:
+                //     []
+                vec![],
+                self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+                    elements: Box::default(),
+                })),
+            )
+        } else if argmap.len() == 1 && arguments.len() == 1 {
+            // Only one argument, so we don't need to make the `args` tuple.
+            //
+            // Generate:
+            //     super let args = [<core::fmt::Arguments>::new_display(&arg)];
+            let args = argmap
+                .iter()
+                .map(|&(arg_index, ty)| {
+                    let ref_arg = self.alloc_expr_desugared(Expr::Ref {
+                        expr: arguments[arg_index].expr,
+                        rawness: Rawness::Ref,
+                        mutability: Mutability::Shared,
+                    });
+                    self.make_argument(ref_arg, ty)
+                })
+                .collect();
+            let args =
+                self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
+            let args_name = Name::new_symbol_root(sym::args);
+            let args_binding =
+                self.alloc_binding(args_name.clone(), BindingAnnotation::Unannotated, hygiene);
+            let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
+            self.add_definition_to_binding(args_binding, args_pat);
+            // TODO: We don't have `super let` yet.
+            let let_stmt = Statement::Let {
+                pat: args_pat,
+                type_ref: None,
+                initializer: Some(args),
+                else_branch: None,
+            };
+            (vec![let_stmt], self.alloc_expr_desugared(Expr::Path(Path::from(args_name))))
+        } else {
+            // Generate:
+            //     super let args = (&arg0, &arg1, &...);
+            let args_name = Name::new_symbol_root(sym::args);
+            let args_binding =
+                self.alloc_binding(args_name.clone(), BindingAnnotation::Unannotated, hygiene);
+            let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
+            self.add_definition_to_binding(args_binding, args_pat);
+            let elements = arguments
+                .iter()
+                .map(|arg| {
+                    self.alloc_expr_desugared(Expr::Ref {
+                        expr: arg.expr,
+                        rawness: Rawness::Ref,
+                        mutability: Mutability::Shared,
+                    })
+                })
+                .collect();
+            let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements });
+            // TODO: We don't have `super let` yet
+            let let_stmt1 = Statement::Let {
+                pat: args_pat,
+                type_ref: None,
+                initializer: Some(args_tuple),
+                else_branch: None,
+            };
+
+            // Generate:
+            //     super let args = [
+            //         <core::fmt::Argument>::new_display(args.0),
+            //         <core::fmt::Argument>::new_lower_hex(args.1),
+            //         <core::fmt::Argument>::new_debug(args.0),
+            //         …
+            //     ];
+            let args = argmap
+                .iter()
+                .map(|&(arg_index, ty)| {
+                    let args_ident_expr =
+                        self.alloc_expr_desugared(Expr::Path(args_name.clone().into()));
+                    let arg = self.alloc_expr_desugared(Expr::Field {
+                        expr: args_ident_expr,
+                        name: Name::new_tuple_field(arg_index),
+                    });
+                    self.make_argument(arg, ty)
+                })
+                .collect();
+            let array =
+                self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
+            let args_binding =
+                self.alloc_binding(args_name.clone(), BindingAnnotation::Unannotated, hygiene);
+            let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
+            self.add_definition_to_binding(args_binding, args_pat);
+            let let_stmt2 = Statement::Let {
+                pat: args_pat,
+                type_ref: None,
+                initializer: Some(array),
+                else_branch: None,
+            };
+            (vec![let_stmt1, let_stmt2], self.alloc_expr_desugared(Expr::Path(args_name.into())))
+        };
+
+        // Generate:
+        //     &args
+        let args = self.alloc_expr_desugared(Expr::Ref {
+            expr: args,
+            rawness: Rawness::Ref,
+            mutability: Mutability::Shared,
+        });
+
+        let call_block = {
+            // Generate:
+            //     unsafe {
+            //         <core::fmt::Arguments>::new_v1_formatted(
+            //             lit_pieces,
+            //             args,
+            //             format_options,
+            //         )
+            //     }
+
+            let new_v1_formatted = LangItem::FormatArguments.ty_rel_path(
+                self.db,
+                self.module.krate(),
+                Name::new_symbol_root(sym::new_v1_formatted),
+            );
+            let new_v1_formatted =
+                self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path));
+            let args = [lit_pieces, args, format_options];
+            let call = self
+                .alloc_expr_desugared(Expr::Call { callee: new_v1_formatted, args: args.into() });
+
+            Expr::Unsafe { id: None, statements: Box::default(), tail: Some(call) }
+        };
+
+        if !let_stmts.is_empty() {
+            // Generate:
+            //     {
+            //         super let …
+            //         super let …
+            //         <core::fmt::Arguments>::new_…(…)
+            //     }
+            let call = self.alloc_expr_desugared(call_block);
+            self.alloc_expr(
+                Expr::Block {
+                    id: None,
+                    statements: let_stmts.into(),
+                    tail: Some(call),
+                    label: None,
+                },
+                syntax_ptr,
+            )
+        } else {
+            self.alloc_expr(call_block, syntax_ptr)
+        }
     }
 
     /// Generate a hir expression for a format_args placeholder specification.
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
index 29e249b07a7..927e280d739 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
@@ -178,14 +178,14 @@ fn main() {
 }
 
 #[test]
-fn desugar_builtin_format_args() {
+fn desugar_builtin_format_args_before_1_89_0() {
     let (db, body, def) = lower(
         r#"
-//- minicore: fmt
+//- minicore: fmt_before_1_89_0
 fn main() {
     let are = "are";
     let count = 10;
-    builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
+    builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", orphan = (), last = "!");
 }
 "#,
     );
@@ -249,8 +249,11 @@ fn main() {
                         builtin#lang(Count::Implied),
                     ),
                 ],
-                unsafe {
-                    builtin#lang(UnsafeArg::new)()
+                {
+                    ();
+                    unsafe {
+                        builtin#lang(UnsafeArg::new)()
+                    }
                 },
             );
         }"#]]
@@ -258,6 +261,89 @@ fn main() {
 }
 
 #[test]
+fn desugar_builtin_format_args() {
+    let (db, body, def) = lower(
+        r#"
+//- minicore: fmt
+fn main() {
+    let are = "are";
+    let count = 10;
+    builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", orphan = (), last = "!");
+}
+"#,
+    );
+
+    expect![[r#"
+        fn main() {
+            let are = "are";
+            let count = 10;
+            {
+                let args = (&"fancy", &(), &"!", &count, &are, );
+                let args = [
+                    builtin#lang(Argument::new_display)(
+                        args.3,
+                    ), builtin#lang(Argument::new_display)(
+                        args.0,
+                    ), builtin#lang(Argument::new_debug)(
+                        args.4,
+                    ), builtin#lang(Argument::new_display)(
+                        args.2,
+                    ),
+                ];
+                unsafe {
+                    builtin#lang(Arguments::new_v1_formatted)(
+                        &[
+                            "\u{1b}hello ", " ", " friends, we ", " ", "",
+                        ],
+                        &args,
+                        &[
+                            builtin#lang(Placeholder::new)(
+                                0usize,
+                                ' ',
+                                builtin#lang(Alignment::Unknown),
+                                8u32,
+                                builtin#lang(Count::Implied),
+                                builtin#lang(Count::Is)(
+                                    2,
+                                ),
+                            ), builtin#lang(Placeholder::new)(
+                                1usize,
+                                ' ',
+                                builtin#lang(Alignment::Unknown),
+                                0u32,
+                                builtin#lang(Count::Implied),
+                                builtin#lang(Count::Implied),
+                            ), builtin#lang(Placeholder::new)(
+                                2usize,
+                                ' ',
+                                builtin#lang(Alignment::Unknown),
+                                0u32,
+                                builtin#lang(Count::Implied),
+                                builtin#lang(Count::Implied),
+                            ), builtin#lang(Placeholder::new)(
+                                1usize,
+                                ' ',
+                                builtin#lang(Alignment::Unknown),
+                                0u32,
+                                builtin#lang(Count::Implied),
+                                builtin#lang(Count::Implied),
+                            ), builtin#lang(Placeholder::new)(
+                                3usize,
+                                ' ',
+                                builtin#lang(Alignment::Unknown),
+                                0u32,
+                                builtin#lang(Count::Implied),
+                                builtin#lang(Count::Implied),
+                            ),
+                        ],
+                    )
+                }
+            };
+        }"#]]
+    .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
+}
+
+#[test]
 fn test_macro_hygiene() {
     let (db, body, def) = lower(
         r##"
@@ -295,29 +381,31 @@ impl SsrError {
     expect![[r#"
         fn main() {
             _ = ra_test_fixture::error::SsrError::new(
-                builtin#lang(Arguments::new_v1_formatted)(
-                    &[
-                        "Failed to resolve path `", "`",
-                    ],
-                    &[
+                {
+                    let args = [
                         builtin#lang(Argument::new_display)(
                             &node.text(),
                         ),
-                    ],
-                    &[
-                        builtin#lang(Placeholder::new)(
-                            0usize,
-                            ' ',
-                            builtin#lang(Alignment::Unknown),
-                            0u32,
-                            builtin#lang(Count::Implied),
-                            builtin#lang(Count::Implied),
-                        ),
-                    ],
+                    ];
                     unsafe {
-                        builtin#lang(UnsafeArg::new)()
-                    },
-                ),
+                        builtin#lang(Arguments::new_v1_formatted)(
+                            &[
+                                "Failed to resolve path `", "`",
+                            ],
+                            &args,
+                            &[
+                                builtin#lang(Placeholder::new)(
+                                    0usize,
+                                    ' ',
+                                    builtin#lang(Alignment::Unknown),
+                                    0u32,
+                                    builtin#lang(Count::Implied),
+                                    builtin#lang(Count::Implied),
+                                ),
+                            ],
+                        )
+                    }
+                },
             );
         }"#]]
     .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
@@ -327,7 +415,7 @@ impl SsrError {
 fn regression_10300() {
     let (db, body, def) = lower(
         r#"
-//- minicore: concat, panic
+//- minicore: concat, panic, fmt_before_1_89_0
 mod private {
     pub use core::concat;
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs
index bb0b70bc5bf..c7707378a5b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body/block.rs
@@ -189,8 +189,8 @@ fn f() {
 }
     "#,
         expect![[r#"
-            BlockIdLt { [salsa id]: Id(3c01) } in BlockRelativeModuleId { block: Some(BlockIdLt { [salsa id]: Id(3c00) }), local_id: Idx::<ModuleData>(1) }
-            BlockIdLt { [salsa id]: Id(3c00) } in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
+            BlockId(3c01) in BlockRelativeModuleId { block: Some(BlockId(3c00)), local_id: Idx::<ModuleData>(1) }
+            BlockId(3c00) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
             crate scope
         "#]],
     );
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index a542214d303..a562f2d0af2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -384,26 +384,7 @@ pub struct BlockLoc {
     /// The containing module.
     pub module: ModuleId,
 }
-#[salsa_macros::tracked(debug)]
-#[derive(PartialOrd, Ord)]
-pub struct BlockIdLt<'db> {
-    pub loc: BlockLoc,
-}
-pub type BlockId = BlockIdLt<'static>;
-impl hir_expand::Intern for BlockLoc {
-    type Database = dyn DefDatabase;
-    type ID = BlockId;
-    fn intern(self, db: &Self::Database) -> Self::ID {
-        unsafe { std::mem::transmute::<BlockIdLt<'_>, BlockId>(BlockIdLt::new(db, self)) }
-    }
-}
-impl hir_expand::Lookup for BlockId {
-    type Database = dyn DefDatabase;
-    type Data = BlockLoc;
-    fn lookup(&self, db: &Self::Database) -> Self::Data {
-        self.loc(db)
-    }
-}
+impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block);
 
 /// A `ModuleId` that is always a crate's root module.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index d1432cacf8d..b756bb859d3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -1230,11 +1230,15 @@ impl InferenceContext<'_> {
                     self.select_from_expr(*expr);
                 }
             }
+            Expr::Let { pat: _, expr } => {
+                self.walk_expr(*expr);
+                let place = self.place_of_expr(*expr);
+                self.ref_expr(*expr, place);
+            }
             Expr::UnaryOp { expr, op: _ }
             | Expr::Array(Array::Repeat { initializer: expr, repeat: _ })
             | Expr::Await { expr }
             | Expr::Loop { body: expr, label: _ }
-            | Expr::Let { pat: _, expr }
             | Expr::Box { expr }
             | Expr::Cast { expr, type_ref: _ } => {
                 self.consume_expr(*expr);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index c253fe25672..c58bd1b773e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -268,7 +268,7 @@ pub fn layout_of_ty_query(
 
             // let pointee = tcx.normalize_erasing_regions(param_env, pointee);
             // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) {
-            //     return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
+            //     return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr)));
             // }
 
             let mut unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone());
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
index 88d21be81ea..7fb981752de 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
@@ -444,3 +444,22 @@ fn main() {
         expect!["99..165;49..54;120..121,133..134 ByRef(Mut { kind: Default }) a &'? mut A"],
     );
 }
+
+#[test]
+fn let_binding_is_a_ref_capture() {
+    check_closure_captures(
+        r#"
+//- minicore:copy
+struct S;
+fn main() {
+    let mut s = S;
+    let s_ref = &mut s;
+    let closure = || {
+        if let ref cb = s_ref {
+        }
+    };
+}
+"#,
+        expect!["83..135;49..54;112..117 ByRef(Shared) s_ref &'? &'? mut S"],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index b1cf30b98f5..0bce69a179b 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -242,9 +242,9 @@ fn resolve_assoc_or_field(
     resolve_field(db, variant_def, name, ns)
 }
 
-fn resolve_assoc_item(
-    db: &dyn HirDatabase,
-    ty: &Type,
+fn resolve_assoc_item<'db>(
+    db: &'db dyn HirDatabase,
+    ty: &Type<'db>,
     name: &Name,
     ns: Option<Namespace>,
 ) -> Option<DocLinkDef> {
@@ -256,10 +256,10 @@ fn resolve_assoc_item(
     })
 }
 
-fn resolve_impl_trait_item(
-    db: &dyn HirDatabase,
+fn resolve_impl_trait_item<'db>(
+    db: &'db dyn HirDatabase,
     resolver: Resolver<'_>,
-    ty: &Type,
+    ty: &Type<'db>,
     name: &Name,
     ns: Option<Namespace>,
 ) -> Option<DocLinkDef> {
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index f7b140e03d4..074bde91fb6 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -36,15 +36,15 @@ pub use hir_ty::{
 };
 
 macro_rules! diagnostics {
-    ($($diag:ident,)*) => {
+    ($($diag:ident $(<$lt:lifetime>)?,)*) => {
         #[derive(Debug)]
-        pub enum AnyDiagnostic {$(
-            $diag(Box<$diag>),
+        pub enum AnyDiagnostic<'db> {$(
+            $diag(Box<$diag $(<$lt>)?>),
         )*}
 
         $(
-            impl From<$diag> for AnyDiagnostic {
-                fn from(d: $diag) -> AnyDiagnostic {
+            impl<'db> From<$diag $(<$lt>)?> for AnyDiagnostic<'db> {
+                fn from(d: $diag $(<$lt>)?) -> AnyDiagnostic<'db> {
                     AnyDiagnostic::$diag(Box::new(d))
                 }
             }
@@ -69,12 +69,12 @@ macro_rules! diagnostics {
 diagnostics![
     AwaitOutsideOfAsync,
     BreakOutsideOfLoop,
-    CastToUnsized,
-    ExpectedFunction,
+    CastToUnsized<'db>,
+    ExpectedFunction<'db>,
     InactiveCode,
     IncoherentImpl,
     IncorrectCase,
-    InvalidCast,
+    InvalidCast<'db>,
     InvalidDeriveTarget,
     MacroDefError,
     MacroError,
@@ -85,7 +85,7 @@ diagnostics![
     MissingFields,
     MissingMatchArms,
     MissingUnsafe,
-    MovedOutOfRef,
+    MovedOutOfRef<'db>,
     NeedMut,
     NonExhaustiveLet,
     NoSuchField,
@@ -98,17 +98,17 @@ diagnostics![
     TraitImplMissingAssocItems,
     TraitImplOrphan,
     TraitImplRedundantAssocItems,
-    TypedHole,
-    TypeMismatch,
+    TypedHole<'db>,
+    TypeMismatch<'db>,
     UndeclaredLabel,
     UnimplementedBuiltinMacro,
     UnreachableLabel,
     UnresolvedAssocItem,
     UnresolvedExternCrate,
-    UnresolvedField,
+    UnresolvedField<'db>,
     UnresolvedImport,
     UnresolvedMacroCall,
-    UnresolvedMethodCall,
+    UnresolvedMethodCall<'db>,
     UnresolvedModule,
     UnresolvedIdent,
     UnusedMut,
@@ -130,9 +130,9 @@ pub struct BreakOutsideOfLoop {
 }
 
 #[derive(Debug)]
-pub struct TypedHole {
+pub struct TypedHole<'db> {
     pub expr: InFile<ExprOrPatPtr>,
-    pub expected: Type,
+    pub expected: Type<'db>,
 }
 
 #[derive(Debug)]
@@ -242,25 +242,25 @@ pub struct MismatchedTupleStructPatArgCount {
 }
 
 #[derive(Debug)]
-pub struct ExpectedFunction {
+pub struct ExpectedFunction<'db> {
     pub call: InFile<ExprOrPatPtr>,
-    pub found: Type,
+    pub found: Type<'db>,
 }
 
 #[derive(Debug)]
-pub struct UnresolvedField {
+pub struct UnresolvedField<'db> {
     pub expr: InFile<ExprOrPatPtr>,
-    pub receiver: Type,
+    pub receiver: Type<'db>,
     pub name: Name,
     pub method_with_same_name_exists: bool,
 }
 
 #[derive(Debug)]
-pub struct UnresolvedMethodCall {
+pub struct UnresolvedMethodCall<'db> {
     pub expr: InFile<ExprOrPatPtr>,
-    pub receiver: Type,
+    pub receiver: Type<'db>,
     pub name: Name,
-    pub field_with_same_name: Option<Type>,
+    pub field_with_same_name: Option<Type<'db>>,
     pub assoc_func_with_same_name: Option<Function>,
 }
 
@@ -329,10 +329,10 @@ pub struct NonExhaustiveLet {
 }
 
 #[derive(Debug)]
-pub struct TypeMismatch {
+pub struct TypeMismatch<'db> {
     pub expr_or_pat: InFile<ExprOrPatPtr>,
-    pub expected: Type,
-    pub actual: Type,
+    pub expected: Type<'db>,
+    pub actual: Type<'db>,
 }
 
 #[derive(Debug)]
@@ -352,8 +352,8 @@ pub struct UnusedVariable {
 }
 
 #[derive(Debug)]
-pub struct MovedOutOfRef {
-    pub ty: Type,
+pub struct MovedOutOfRef<'db> {
+    pub ty: Type<'db>,
     pub span: InFile<SyntaxNodePtr>,
 }
 
@@ -403,17 +403,17 @@ pub struct RemoveUnnecessaryElse {
 }
 
 #[derive(Debug)]
-pub struct CastToUnsized {
+pub struct CastToUnsized<'db> {
     pub expr: InFile<ExprOrPatPtr>,
-    pub cast_ty: Type,
+    pub cast_ty: Type<'db>,
 }
 
 #[derive(Debug)]
-pub struct InvalidCast {
+pub struct InvalidCast<'db> {
     pub expr: InFile<ExprOrPatPtr>,
     pub error: CastError,
-    pub expr_ty: Type,
-    pub cast_ty: Type,
+    pub expr_ty: Type<'db>,
+    pub cast_ty: Type<'db>,
 }
 
 #[derive(Debug)]
@@ -482,12 +482,12 @@ pub struct IncorrectGenericsOrder {
     pub expected_kind: GenericArgKind,
 }
 
-impl AnyDiagnostic {
+impl<'db> AnyDiagnostic<'db> {
     pub(crate) fn body_validation_diagnostic(
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         diagnostic: BodyValidationDiagnostic,
         source_map: &hir_def::expr_store::BodySourceMap,
-    ) -> Option<AnyDiagnostic> {
+    ) -> Option<AnyDiagnostic<'db>> {
         match diagnostic {
             BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => {
                 let variant_data = variant.variant_data(db);
@@ -618,12 +618,12 @@ impl AnyDiagnostic {
     }
 
     pub(crate) fn inference_diagnostic(
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         def: DefWithBodyId,
         d: &InferenceDiagnostic,
         source_map: &hir_def::expr_store::BodySourceMap,
         sig_map: &hir_def::expr_store::ExpressionStoreSourceMap,
-    ) -> Option<AnyDiagnostic> {
+    ) -> Option<AnyDiagnostic<'db>> {
         let expr_syntax = |expr| {
             source_map
                 .expr_syntax(expr)
@@ -819,7 +819,7 @@ impl AnyDiagnostic {
     fn path_diagnostic(
         diag: &PathLoweringDiagnostic,
         path: InFile<ast::Path>,
-    ) -> Option<AnyDiagnostic> {
+    ) -> Option<AnyDiagnostic<'db>> {
         Some(match *diag {
             PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
                 let segment = hir_segment_to_ast_segment(&path.value, segment)?;
@@ -912,8 +912,8 @@ impl AnyDiagnostic {
     pub(crate) fn ty_diagnostic(
         diag: &TyLoweringDiagnostic,
         source_map: &ExpressionStoreSourceMap,
-        db: &dyn HirDatabase,
-    ) -> Option<AnyDiagnostic> {
+        db: &'db dyn HirDatabase,
+    ) -> Option<AnyDiagnostic<'db>> {
         let Ok(source) = source_map.type_syntax(diag.source) else {
             stdx::never!("error on synthetic type syntax");
             return None;
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index 124ab8e274a..112558bdd04 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -431,7 +431,7 @@ impl HirDisplay for Variant {
     }
 }
 
-impl HirDisplay for Type {
+impl HirDisplay for Type<'_> {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         self.ty.hir_fmt(f)
     }
@@ -743,7 +743,7 @@ impl HirDisplay for Static {
     }
 }
 
-impl HirDisplay for TraitRef {
+impl HirDisplay for TraitRef<'_> {
     fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
         self.trait_ref.hir_fmt(f)
     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs
index fe7429c8672..4767d4792e7 100644
--- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs
@@ -225,7 +225,7 @@ impl HasSource for LocalSource {
     }
 }
 
-impl HasSource for Param {
+impl HasSource for Param<'_> {
     type Ast = Either<ast::SelfParam, ast::Param>;
 
     fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index adae335627b..3b39707cf60 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -84,7 +84,7 @@ use nameres::diagnostics::DefDiagnosticKind;
 use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
 use span::{AstIdNode, Edition, FileId};
-use stdx::{format_to, impl_from, never};
+use stdx::{format_to, impl_from, never, variance::PhantomCovariantLifetime};
 use syntax::{
     AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, T, TextRange, ToSmolStr,
     ast::{self, HasAttrs as _, HasName, HasVisibility as _},
@@ -400,7 +400,11 @@ impl ModuleDef {
         Some(name)
     }
 
-    pub fn diagnostics(self, db: &dyn HirDatabase, style_lints: bool) -> Vec<AnyDiagnostic> {
+    pub fn diagnostics<'db>(
+        self,
+        db: &'db dyn HirDatabase,
+        style_lints: bool,
+    ) -> Vec<AnyDiagnostic<'db>> {
         let id = match self {
             ModuleDef::Adt(it) => match it {
                 Adt::Struct(it) => it.id.into(),
@@ -612,10 +616,10 @@ impl Module {
     }
 
     /// Fills `acc` with the module's diagnostics.
-    pub fn diagnostics(
+    pub fn diagnostics<'db>(
         self,
-        db: &dyn HirDatabase,
-        acc: &mut Vec<AnyDiagnostic>,
+        db: &'db dyn HirDatabase,
+        acc: &mut Vec<AnyDiagnostic<'db>>,
         style_lints: bool,
     ) {
         let _p = tracing::info_span!("diagnostics", name = ?self.name(db)).entered();
@@ -970,10 +974,10 @@ impl Module {
     }
 }
 
-fn macro_call_diagnostics(
-    db: &dyn HirDatabase,
+fn macro_call_diagnostics<'db>(
+    db: &'db dyn HirDatabase,
     macro_call_id: MacroCallId,
-    acc: &mut Vec<AnyDiagnostic>,
+    acc: &mut Vec<AnyDiagnostic<'db>>,
 ) {
     let Some(e) = db.parse_macro_expansion_error(macro_call_id) else {
         return;
@@ -1010,7 +1014,11 @@ fn macro_call_diagnostics(
     }
 }
 
-fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, m: Macro) {
+fn emit_macro_def_diagnostics<'db>(
+    db: &'db dyn HirDatabase,
+    acc: &mut Vec<AnyDiagnostic<'db>>,
+    m: Macro,
+) {
     let id = db.macro_def(m.id);
     if let hir_expand::db::TokenExpander::DeclarativeMacro(expander) = db.macro_expander(id) {
         if let Some(e) = expander.mac.err() {
@@ -1030,18 +1038,18 @@ fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>
     }
 }
 
-fn emit_def_diagnostic(
-    db: &dyn HirDatabase,
-    acc: &mut Vec<AnyDiagnostic>,
+fn emit_def_diagnostic<'db>(
+    db: &'db dyn HirDatabase,
+    acc: &mut Vec<AnyDiagnostic<'db>>,
     diag: &DefDiagnostic,
     edition: Edition,
 ) {
     emit_def_diagnostic_(db, acc, &diag.kind, edition)
 }
 
-fn emit_def_diagnostic_(
-    db: &dyn HirDatabase,
-    acc: &mut Vec<AnyDiagnostic>,
+fn emit_def_diagnostic_<'db>(
+    db: &'db dyn HirDatabase,
+    acc: &mut Vec<AnyDiagnostic<'db>>,
     diag: &DefDiagnosticKind,
     edition: Edition,
 ) {
@@ -1251,14 +1259,18 @@ impl TupleField {
         Name::new_tuple_field(self.index as usize)
     }
 
-    pub fn ty(&self, db: &dyn HirDatabase) -> Type {
+    pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> {
         let ty = db.infer(self.owner).tuple_field_access_types[&self.tuple]
             .as_slice(Interner)
             .get(self.index as usize)
             .and_then(|arg| arg.ty(Interner))
             .cloned()
             .unwrap_or_else(|| TyKind::Error.intern(Interner));
-        Type { env: db.trait_environment_for_body(self.owner), ty }
+        Type {
+            env: db.trait_environment_for_body(self.owner),
+            ty,
+            _pd: PhantomCovariantLifetime::new(),
+        }
     }
 }
 
@@ -1309,7 +1321,7 @@ impl Field {
     /// Returns the type as in the signature of the struct (i.e., with
     /// placeholder types for type parameters). Only use this in the context of
     /// the field definition.
-    pub fn ty(&self, db: &dyn HirDatabase) -> Type {
+    pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> {
         let var_id = self.parent.into();
         let generic_def_id: GenericDefId = match self.parent {
             VariantDef::Struct(it) => it.id.into(),
@@ -1322,7 +1334,11 @@ impl Field {
     }
 
     // FIXME: Find better API to also handle const generics
-    pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
+    pub fn ty_with_args<'db>(
+        &self,
+        db: &'db dyn HirDatabase,
+        generics: impl Iterator<Item = Type<'db>>,
+    ) -> Type<'db> {
         let var_id = self.parent.into();
         let def_id: AdtId = match self.parent {
             VariantDef::Struct(it) => it.id.into(),
@@ -1394,15 +1410,15 @@ impl Struct {
             .collect()
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_def(db, self.id)
     }
 
-    pub fn ty_placeholders(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty_placeholders(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_def_placeholders(db, self.id)
     }
 
-    pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_value_def(db, self.id)
     }
 
@@ -1449,15 +1465,15 @@ impl Union {
         Module { id: self.id.lookup(db).container }
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_def(db, self.id)
     }
 
-    pub fn ty_placeholders(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty_placeholders(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_def_placeholders(db, self.id)
     }
 
-    pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_value_def(db, self.id)
     }
 
@@ -1515,16 +1531,16 @@ impl Enum {
         db.enum_signature(self.id).repr
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> {
         Type::from_def(db, self.id)
     }
 
-    pub fn ty_placeholders(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty_placeholders<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> {
         Type::from_def_placeholders(db, self.id)
     }
 
     /// The type of the enum variant bodies.
-    pub fn variant_body_ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn variant_body_ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> {
         Type::new_for_crate(
             self.id.lookup(db).container.krate(),
             TyBuilder::builtin(match db.enum_signature(self.id).variant_body_type() {
@@ -1599,7 +1615,7 @@ impl Variant {
         self.id.lookup(db).parent.into()
     }
 
-    pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_value_def(db, self.id)
     }
 
@@ -1701,14 +1717,18 @@ impl Adt {
     /// Turns this ADT into a type. Any type parameters of the ADT will be
     /// turned into unknown types, which is good for e.g. finding the most
     /// general set of completions, but will not look very nice when printed.
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         let id = AdtId::from(self);
         Type::from_def(db, id)
     }
 
     /// Turns this ADT into a type with the given type parameters. This isn't
     /// the greatest API, FIXME find a better one.
-    pub fn ty_with_args(self, db: &dyn HirDatabase, args: impl Iterator<Item = Type>) -> Type {
+    pub fn ty_with_args<'db>(
+        self,
+        db: &'db dyn HirDatabase,
+        args: impl Iterator<Item = Type<'db>>,
+    ) -> Type<'db> {
         let id = AdtId::from(self);
         let mut it = args.map(|t| t.ty);
         let ty = TyBuilder::def_ty(db, id.into(), None)
@@ -1841,7 +1861,7 @@ impl DefWithBody {
     }
 
     /// Returns the type this def's body has to evaluate to.
-    pub fn body_type(self, db: &dyn HirDatabase) -> Type {
+    pub fn body_type(self, db: &dyn HirDatabase) -> Type<'_> {
         match self {
             DefWithBody::Function(it) => it.ret_type(db),
             DefWithBody::Static(it) => it.ty(db),
@@ -1874,10 +1894,10 @@ impl DefWithBody {
         }
     }
 
-    pub fn diagnostics(
+    pub fn diagnostics<'db>(
         self,
-        db: &dyn HirDatabase,
-        acc: &mut Vec<AnyDiagnostic>,
+        db: &'db dyn HirDatabase,
+        acc: &mut Vec<AnyDiagnostic<'db>>,
         style_lints: bool,
     ) {
         let krate = self.module(db).id.krate();
@@ -2107,7 +2127,7 @@ impl DefWithBody {
 
 fn expr_store_diagnostics(
     db: &dyn HirDatabase,
-    acc: &mut Vec<AnyDiagnostic>,
+    acc: &mut Vec<AnyDiagnostic<'_>>,
     source_map: &ExpressionStoreSourceMap,
 ) {
     for diag in source_map.diagnostics() {
@@ -2172,11 +2192,11 @@ impl Function {
         db.function_signature(self.id).name.clone()
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_value_def(db, self.id)
     }
 
-    pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type {
+    pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type<'_> {
         let resolver = self.id.resolver(db);
         let substs = TyBuilder::placeholder_subst(db, self.id);
         let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
@@ -2185,7 +2205,7 @@ impl Function {
     }
 
     /// Get this function's return type
-    pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
+    pub fn ret_type(self, db: &dyn HirDatabase) -> Type<'_> {
         let resolver = self.id.resolver(db);
         let substs = TyBuilder::placeholder_subst(db, self.id);
         let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
@@ -2194,11 +2214,11 @@ impl Function {
     }
 
     // FIXME: Find better API to also handle const generics
-    pub fn ret_type_with_args(
+    pub fn ret_type_with_args<'db>(
         self,
-        db: &dyn HirDatabase,
-        generics: impl Iterator<Item = Type>,
-    ) -> Type {
+        db: &'db dyn HirDatabase,
+        generics: impl Iterator<Item = Type<'db>>,
+    ) -> Type<'db> {
         let resolver = self.id.resolver(db);
         let parent_id: Option<GenericDefId> = match self.id.lookup(db).container {
             ItemContainerId::ImplId(it) => Some(it.into()),
@@ -2223,7 +2243,7 @@ impl Function {
         Type::new_with_resolver_inner(db, &resolver, ty)
     }
 
-    pub fn async_ret_type(self, db: &dyn HirDatabase) -> Option<Type> {
+    pub fn async_ret_type<'db>(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> {
         if !self.is_async(db) {
             return None;
         }
@@ -2247,7 +2267,7 @@ impl Function {
         self.has_self_param(db).then_some(SelfParam { func: self.id })
     }
 
-    pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> {
+    pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param<'_>> {
         let environment = db.trait_environment(self.id.into());
         let substs = TyBuilder::placeholder_subst(db, self.id);
         let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
@@ -2256,7 +2276,11 @@ impl Function {
             .iter()
             .enumerate()
             .map(|(idx, ty)| {
-                let ty = Type { env: environment.clone(), ty: ty.clone() };
+                let ty = Type {
+                    env: environment.clone(),
+                    ty: ty.clone(),
+                    _pd: PhantomCovariantLifetime::new(),
+                };
                 Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
             })
             .collect()
@@ -2266,12 +2290,12 @@ impl Function {
         db.function_signature(self.id).params.len()
     }
 
-    pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> {
+    pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param<'_>>> {
         self.self_param(db)?;
         Some(self.params_without_self(db))
     }
 
-    pub fn params_without_self(self, db: &dyn HirDatabase) -> Vec<Param> {
+    pub fn params_without_self(self, db: &dyn HirDatabase) -> Vec<Param<'_>> {
         let environment = db.trait_environment(self.id.into());
         let substs = TyBuilder::placeholder_subst(db, self.id);
         let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
@@ -2282,18 +2306,22 @@ impl Function {
             .enumerate()
             .skip(skip)
             .map(|(idx, ty)| {
-                let ty = Type { env: environment.clone(), ty: ty.clone() };
+                let ty = Type {
+                    env: environment.clone(),
+                    ty: ty.clone(),
+                    _pd: PhantomCovariantLifetime::new(),
+                };
                 Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
             })
             .collect()
     }
 
     // FIXME: Find better API to also handle const generics
-    pub fn params_without_self_with_args(
+    pub fn params_without_self_with_args<'db>(
         self,
-        db: &dyn HirDatabase,
-        generics: impl Iterator<Item = Type>,
-    ) -> Vec<Param> {
+        db: &'db dyn HirDatabase,
+        generics: impl Iterator<Item = Type<'db>>,
+    ) -> Vec<Param<'db>> {
         let environment = db.trait_environment(self.id.into());
         let parent_id: Option<GenericDefId> = match self.id.lookup(db).container {
             ItemContainerId::ImplId(it) => Some(it.into()),
@@ -2328,7 +2356,11 @@ impl Function {
             .enumerate()
             .skip(skip)
             .map(|(idx, ty)| {
-                let ty = Type { env: environment.clone(), ty: ty.clone() };
+                let ty = Type {
+                    env: environment.clone(),
+                    ty: ty.clone(),
+                    _pd: PhantomCovariantLifetime::new(),
+                };
                 Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
             })
             .collect()
@@ -2358,7 +2390,8 @@ impl Function {
             return true;
         }
 
-        let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false };
+        let ret_type = self.ret_type(db);
+        let Some(impl_traits) = ret_type.as_impl_traits(db) else { return false };
         let Some(future_trait_id) = LangItem::Future.resolve_trait(db, self.ty(db).env.krate)
         else {
             return false;
@@ -2501,14 +2534,14 @@ impl From<hir_ty::Mutability> for Access {
 }
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
-pub struct Param {
+pub struct Param<'db> {
     func: Callee,
     /// The index in parameter list, including self parameter.
     idx: usize,
-    ty: Type,
+    ty: Type<'db>,
 }
 
-impl Param {
+impl<'db> Param<'db> {
     pub fn parent_fn(&self) -> Option<Function> {
         match self.func {
             Callee::Def(CallableDefId::FunctionId(f)) => Some(f.into()),
@@ -2524,7 +2557,7 @@ impl Param {
         self.idx
     }
 
-    pub fn ty(&self) -> &Type {
+    pub fn ty(&self) -> &Type<'db> {
         &self.ty
     }
 
@@ -2591,17 +2624,21 @@ impl SelfParam {
         Function::from(self.func)
     }
 
-    pub fn ty(&self, db: &dyn HirDatabase) -> Type {
+    pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> {
         let substs = TyBuilder::placeholder_subst(db, self.func);
         let callable_sig =
             db.callable_item_signature(self.func.into()).substitute(Interner, &substs);
         let environment = db.trait_environment(self.func.into());
         let ty = callable_sig.params()[0].clone();
-        Type { env: environment, ty }
+        Type { env: environment, ty, _pd: PhantomCovariantLifetime::new() }
     }
 
     // FIXME: Find better API to also handle const generics
-    pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
+    pub fn ty_with_args<'db>(
+        &self,
+        db: &'db dyn HirDatabase,
+        generics: impl Iterator<Item = Type<'db>>,
+    ) -> Type<'db> {
         let parent_id: GenericDefId = match self.func.lookup(db).container {
             ItemContainerId::ImplId(it) => it.into(),
             ItemContainerId::TraitId(it) => it.into(),
@@ -2626,7 +2663,7 @@ impl SelfParam {
             db.callable_item_signature(self.func.into()).substitute(Interner, &substs);
         let environment = db.trait_environment(self.func.into());
         let ty = callable_sig.params()[0].clone();
-        Type { env: environment, ty }
+        Type { env: environment, ty, _pd: PhantomCovariantLifetime::new() }
     }
 }
 
@@ -2714,7 +2751,7 @@ impl Const {
         self.source(db)?.value.body()
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_value_def(db, self.id)
     }
 
@@ -2791,7 +2828,7 @@ impl Static {
         self.source(db)?.value.body()
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_value_def(db, self.id)
     }
 
@@ -2961,11 +2998,11 @@ impl TypeAlias {
         Module { id: self.id.module(db) }
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_def(db, self.id)
     }
 
-    pub fn ty_placeholders(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty_placeholders(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::from_def_placeholders(db, self.id)
     }
 
@@ -3010,7 +3047,7 @@ impl BuiltinType {
         BuiltinType { inner: hir_def::builtin_type::BuiltinType::Str }
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> {
         let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| db.all_crates()[0]);
         Type::new_for_crate(core, TyBuilder::builtin(self.inner))
     }
@@ -3472,7 +3509,7 @@ impl AssocItem {
         }
     }
 
-    pub fn implementing_ty(self, db: &dyn HirDatabase) -> Option<Type> {
+    pub fn implementing_ty(self, db: &dyn HirDatabase) -> Option<Type<'_>> {
         match self.container(db) {
             AssocItemContainer::Impl(i) => Some(i.self_ty(db)),
             _ => None,
@@ -3500,10 +3537,10 @@ impl AssocItem {
         }
     }
 
-    pub fn diagnostics(
+    pub fn diagnostics<'db>(
         self,
-        db: &dyn HirDatabase,
-        acc: &mut Vec<AnyDiagnostic>,
+        db: &'db dyn HirDatabase,
+        acc: &mut Vec<AnyDiagnostic<'db>>,
         style_lints: bool,
     ) {
         match self {
@@ -3625,7 +3662,7 @@ impl GenericDef {
         }
     }
 
-    pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
+    pub fn diagnostics<'db>(self, db: &'db dyn HirDatabase, acc: &mut Vec<AnyDiagnostic<'db>>) {
         let def = self.id();
 
         let generics = db.generic_params(def);
@@ -3690,18 +3727,19 @@ impl GenericDef {
 
 // We cannot call this `Substitution` unfortunately...
 #[derive(Debug)]
-pub struct GenericSubstitution {
+pub struct GenericSubstitution<'db> {
     def: GenericDefId,
     subst: Substitution,
     env: Arc<TraitEnvironment>,
+    _pd: PhantomCovariantLifetime<'db>,
 }
 
-impl GenericSubstitution {
+impl<'db> GenericSubstitution<'db> {
     fn new(def: GenericDefId, subst: Substitution, env: Arc<TraitEnvironment>) -> Self {
-        Self { def, subst, env }
+        Self { def, subst, env, _pd: PhantomCovariantLifetime::new() }
     }
 
-    pub fn types(&self, db: &dyn HirDatabase) -> Vec<(Symbol, Type)> {
+    pub fn types(&self, db: &'db dyn HirDatabase) -> Vec<(Symbol, Type<'db>)> {
         let container = match self.def {
             GenericDefId::ConstId(id) => Some(id.lookup(db).container),
             GenericDefId::FunctionId(id) => Some(id.lookup(db).container),
@@ -3744,7 +3782,10 @@ impl GenericSubstitution {
         container_params
             .chain(self_params)
             .filter_map(|(ty, name)| {
-                Some((name?.symbol().clone(), Type { ty, env: self.env.clone() }))
+                Some((
+                    name?.symbol().clone(),
+                    Type { ty, env: self.env.clone(), _pd: PhantomCovariantLifetime::new() },
+                ))
             })
             .collect()
     }
@@ -3847,7 +3888,7 @@ impl Local {
         self.parent(db).module(db)
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         let def = self.parent;
         let infer = db.infer(def);
         let ty = infer[self.binding_id].clone();
@@ -4109,6 +4150,10 @@ impl TypeParam {
         self.merge().name(db)
     }
 
+    pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
+        self.id.parent().into()
+    }
+
     pub fn module(self, db: &dyn HirDatabase) -> Module {
         self.id.parent().module(db).into()
     }
@@ -4124,7 +4169,7 @@ impl TypeParam {
         }
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         let resolver = self.id.parent().resolver(db);
         let ty =
             TyKind::Placeholder(hir_ty::to_placeholder_idx(db, self.id.into())).intern(Interner);
@@ -4146,7 +4191,7 @@ impl TypeParam {
             .collect()
     }
 
-    pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
+    pub fn default(self, db: &dyn HirDatabase) -> Option<Type<'_>> {
         let ty = generic_arg_from_param(db, self.id.into())?;
         let resolver = self.id.parent().resolver(db);
         match ty.data(Interner) {
@@ -4211,7 +4256,7 @@ impl ConstParam {
         self.id.parent().into()
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         Type::new(db, self.id.parent(), db.const_param_ty(self.id))
     }
 
@@ -4268,7 +4313,7 @@ impl TypeOrConstParam {
         }
     }
 
-    pub fn ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
         match self.split(db) {
             Either::Left(it) => it.ty(db),
             Either::Right(it) => it.ty(db),
@@ -4313,7 +4358,10 @@ impl Impl {
         module.id.def_map(db)[module.id.local_id].scope.impls().map(Into::into).collect()
     }
 
-    pub fn all_for_type(db: &dyn HirDatabase, Type { ty, env }: Type) -> Vec<Impl> {
+    pub fn all_for_type<'db>(
+        db: &'db dyn HirDatabase,
+        Type { ty, env, _pd: _ }: Type<'db>,
+    ) -> Vec<Impl> {
         let def_crates = match method_resolution::def_crates(db, &ty, env.krate) {
             Some(def_crates) => def_crates,
             None => return Vec::new(),
@@ -4398,14 +4446,14 @@ impl Impl {
         Some(Trait { id })
     }
 
-    pub fn trait_ref(self, db: &dyn HirDatabase) -> Option<TraitRef> {
+    pub fn trait_ref(self, db: &dyn HirDatabase) -> Option<TraitRef<'_>> {
         let substs = TyBuilder::placeholder_subst(db, self.id);
         let trait_ref = db.impl_trait(self.id)?.substitute(Interner, &substs);
         let resolver = self.id.resolver(db);
         Some(TraitRef::new_with_resolver(db, &resolver, trait_ref))
     }
 
-    pub fn self_ty(self, db: &dyn HirDatabase) -> Type {
+    pub fn self_ty(self, db: &dyn HirDatabase) -> Type<'_> {
         let resolver = self.id.resolver(db);
         let substs = TyBuilder::placeholder_subst(db, self.id);
         let ty = db.impl_self_ty(self.id).substitute(Interner, &substs);
@@ -4467,21 +4515,22 @@ impl Impl {
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
-pub struct TraitRef {
+pub struct TraitRef<'db> {
     env: Arc<TraitEnvironment>,
     trait_ref: hir_ty::TraitRef,
+    _pd: PhantomCovariantLifetime<'db>,
 }
 
-impl TraitRef {
+impl<'db> TraitRef<'db> {
     pub(crate) fn new_with_resolver(
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         resolver: &Resolver<'_>,
         trait_ref: hir_ty::TraitRef,
-    ) -> TraitRef {
+    ) -> Self {
         let env = resolver
             .generic_def()
             .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d));
-        TraitRef { env, trait_ref }
+        TraitRef { env, trait_ref, _pd: PhantomCovariantLifetime::new() }
     }
 
     pub fn trait_(&self) -> Trait {
@@ -4489,21 +4538,21 @@ impl TraitRef {
         Trait { id }
     }
 
-    pub fn self_ty(&self) -> Type {
+    pub fn self_ty(&self) -> Type<'_> {
         let ty = self.trait_ref.self_type_parameter(Interner);
-        Type { env: self.env.clone(), ty }
+        Type { env: self.env.clone(), ty, _pd: PhantomCovariantLifetime::new() }
     }
 
     /// Returns `idx`-th argument of this trait reference if it is a type argument. Note that the
     /// first argument is the `Self` type.
-    pub fn get_type_argument(&self, idx: usize) -> Option<Type> {
+    pub fn get_type_argument(&self, idx: usize) -> Option<Type<'db>> {
         self.trait_ref
             .substitution
             .as_slice(Interner)
             .get(idx)
             .and_then(|arg| arg.ty(Interner))
             .cloned()
-            .map(|ty| Type { env: self.env.clone(), ty })
+            .map(|ty| Type { env: self.env.clone(), ty, _pd: PhantomCovariantLifetime::new() })
     }
 }
 
@@ -4551,7 +4600,7 @@ impl Closure {
             .collect()
     }
 
-    pub fn capture_types(&self, db: &dyn HirDatabase) -> Vec<Type> {
+    pub fn capture_types<'db>(&self, db: &'db dyn HirDatabase) -> Vec<Type<'db>> {
         let owner = db.lookup_intern_closure((self.id).into()).0;
         let infer = &db.infer(owner);
         let (captures, _) = infer.closure_info(&self.id);
@@ -4560,6 +4609,7 @@ impl Closure {
             .map(|capture| Type {
                 env: db.trait_environment_for_body(owner),
                 ty: capture.ty(&self.subst),
+                _pd: PhantomCovariantLifetime::new(),
             })
             .collect()
     }
@@ -4691,40 +4741,45 @@ impl CaptureUsageSource {
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
-pub struct Type {
+pub struct Type<'db> {
     env: Arc<TraitEnvironment>,
     ty: Ty,
+    _pd: PhantomCovariantLifetime<'db>,
 }
 
-impl Type {
-    pub(crate) fn new_with_resolver(db: &dyn HirDatabase, resolver: &Resolver<'_>, ty: Ty) -> Type {
+impl<'db> Type<'db> {
+    pub(crate) fn new_with_resolver(
+        db: &'db dyn HirDatabase,
+        resolver: &Resolver<'_>,
+        ty: Ty,
+    ) -> Self {
         Type::new_with_resolver_inner(db, resolver, ty)
     }
 
     pub(crate) fn new_with_resolver_inner(
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         resolver: &Resolver<'_>,
         ty: Ty,
-    ) -> Type {
+    ) -> Self {
         let environment = resolver
             .generic_def()
             .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d));
-        Type { env: environment, ty }
+        Type { env: environment, ty, _pd: PhantomCovariantLifetime::new() }
     }
 
-    pub(crate) fn new_for_crate(krate: base_db::Crate, ty: Ty) -> Type {
-        Type { env: TraitEnvironment::empty(krate), ty }
+    pub(crate) fn new_for_crate(krate: base_db::Crate, ty: Ty) -> Self {
+        Type { env: TraitEnvironment::empty(krate), ty, _pd: PhantomCovariantLifetime::new() }
     }
 
-    fn new(db: &dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Type {
+    fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Self {
         let resolver = lexical_env.resolver(db);
         let environment = resolver
             .generic_def()
             .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d));
-        Type { env: environment, ty }
+        Type { env: environment, ty, _pd: PhantomCovariantLifetime::new() }
     }
 
-    fn from_def(db: &dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Type {
+    fn from_def(db: &'db dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Self {
         let ty = db.ty(def.into());
         let substs = TyBuilder::unknown_subst(
             db,
@@ -4737,7 +4792,10 @@ impl Type {
         Type::new(db, def, ty.substitute(Interner, &substs))
     }
 
-    fn from_def_placeholders(db: &dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Type {
+    fn from_def_placeholders(
+        db: &'db dyn HirDatabase,
+        def: impl Into<TyDefId> + HasResolver,
+    ) -> Self {
         let ty = db.ty(def.into());
         let substs = TyBuilder::placeholder_subst(
             db,
@@ -4750,7 +4808,10 @@ impl Type {
         Type::new(db, def, ty.substitute(Interner, &substs))
     }
 
-    fn from_value_def(db: &dyn HirDatabase, def: impl Into<ValueTyDefId> + HasResolver) -> Type {
+    fn from_value_def(
+        db: &'db dyn HirDatabase,
+        def: impl Into<ValueTyDefId> + HasResolver,
+    ) -> Self {
         let Some(ty) = db.value_ty(def.into()) else {
             return Type::new(db, def, TyKind::Error.intern(Interner));
         };
@@ -4770,13 +4831,17 @@ impl Type {
         Type::new(db, def, ty.substitute(Interner, &substs))
     }
 
-    pub fn new_slice(ty: Type) -> Type {
-        Type { env: ty.env, ty: TyBuilder::slice(ty.ty) }
+    pub fn new_slice(ty: Self) -> Self {
+        Type { env: ty.env, ty: TyBuilder::slice(ty.ty), _pd: PhantomCovariantLifetime::new() }
     }
 
-    pub fn new_tuple(krate: base_db::Crate, tys: &[Type]) -> Type {
+    pub fn new_tuple(krate: base_db::Crate, tys: &[Self]) -> Self {
         let tys = tys.iter().map(|it| it.ty.clone());
-        Type { env: TraitEnvironment::empty(krate), ty: TyBuilder::tuple_with(tys) }
+        Type {
+            env: TraitEnvironment::empty(krate),
+            ty: TyBuilder::tuple_with(tys),
+            _pd: PhantomCovariantLifetime::new(),
+        }
     }
 
     pub fn is_unit(&self) -> bool {
@@ -4803,7 +4868,7 @@ impl Type {
         matches!(self.ty.kind(Interner), TyKind::Ref(..))
     }
 
-    pub fn contains_reference(&self, db: &dyn HirDatabase) -> bool {
+    pub fn contains_reference(&self, db: &'db dyn HirDatabase) -> bool {
         return go(db, self.env.krate, &self.ty);
 
         fn go(db: &dyn HirDatabase, krate: base_db::Crate, ty: &Ty) -> bool {
@@ -4847,13 +4912,13 @@ impl Type {
         }
     }
 
-    pub fn as_reference(&self) -> Option<(Type, Mutability)> {
+    pub fn as_reference(&self) -> Option<(Type<'db>, Mutability)> {
         let (ty, _lt, m) = self.ty.as_reference()?;
         let m = Mutability::from_mutable(matches!(m, hir_ty::Mutability::Mut));
         Some((self.derived(ty.clone()), m))
     }
 
-    pub fn add_reference(&self, mutability: Mutability) -> Type {
+    pub fn add_reference(&self, mutability: Mutability) -> Self {
         let ty_mutability = match mutability {
             Mutability::Shared => hir_ty::Mutability::Not,
             Mutability::Mut => hir_ty::Mutability::Mut,
@@ -4889,25 +4954,25 @@ impl Type {
         matches!(self.ty.kind(Interner), TyKind::Tuple(..))
     }
 
-    pub fn remove_ref(&self) -> Option<Type> {
+    pub fn remove_ref(&self) -> Option<Type<'db>> {
         match &self.ty.kind(Interner) {
             TyKind::Ref(.., ty) => Some(self.derived(ty.clone())),
             _ => None,
         }
     }
 
-    pub fn as_slice(&self) -> Option<Type> {
+    pub fn as_slice(&self) -> Option<Type<'db>> {
         match &self.ty.kind(Interner) {
             TyKind::Slice(ty) => Some(self.derived(ty.clone())),
             _ => None,
         }
     }
 
-    pub fn strip_references(&self) -> Type {
+    pub fn strip_references(&self) -> Self {
         self.derived(self.ty.strip_references().clone())
     }
 
-    pub fn strip_reference(&self) -> Type {
+    pub fn strip_reference(&self) -> Self {
         self.derived(self.ty.strip_reference().clone())
     }
 
@@ -4918,7 +4983,7 @@ impl Type {
     /// Checks that particular type `ty` implements `std::future::IntoFuture` or
     /// `std::future::Future` and returns the `Output` associated type.
     /// This function is used in `.await` syntax completion.
-    pub fn into_future_output(&self, db: &dyn HirDatabase) -> Option<Type> {
+    pub fn into_future_output(&self, db: &'db dyn HirDatabase) -> Option<Type<'db>> {
         let trait_ = LangItem::IntoFutureIntoFuture
             .resolve_function(db, self.env.krate)
             .and_then(|into_future_fn| {
@@ -4940,13 +5005,13 @@ impl Type {
     }
 
     /// This does **not** resolve `IntoFuture`, only `Future`.
-    pub fn future_output(self, db: &dyn HirDatabase) -> Option<Type> {
+    pub fn future_output(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> {
         let future_output = LangItem::FutureOutput.resolve_type_alias(db, self.env.krate)?;
         self.normalize_trait_assoc_type(db, &[], future_output.into())
     }
 
     /// This does **not** resolve `IntoIterator`, only `Iterator`.
-    pub fn iterator_item(self, db: &dyn HirDatabase) -> Option<Type> {
+    pub fn iterator_item(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> {
         let iterator_trait = LangItem::Iterator.resolve_trait(db, self.env.krate)?;
         let iterator_item = db
             .trait_items(iterator_trait)
@@ -4954,7 +5019,7 @@ impl Type {
         self.normalize_trait_assoc_type(db, &[], iterator_item.into())
     }
 
-    pub fn impls_iterator(self, db: &dyn HirDatabase) -> bool {
+    pub fn impls_iterator(self, db: &'db dyn HirDatabase) -> bool {
         let Some(iterator_trait) = LangItem::Iterator.resolve_trait(db, self.env.krate) else {
             return false;
         };
@@ -4964,7 +5029,7 @@ impl Type {
     }
 
     /// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type
-    pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> {
+    pub fn into_iterator_iter(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> {
         let trait_ = LangItem::IntoIterIntoIter.resolve_function(db, self.env.krate).and_then(
             |into_iter_fn| {
                 let assoc_item = as_assoc_item(db, AssocItem::Function, into_iter_fn)?;
@@ -4989,7 +5054,7 @@ impl Type {
     ///
     /// This function can be used to check if a particular type is callable, since FnOnce is a
     /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce.
-    pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool {
+    pub fn impls_fnonce(&self, db: &'db dyn HirDatabase) -> bool {
         let fnonce_trait = match FnTrait::FnOnce.get_id(db, self.env.krate) {
             Some(it) => it,
             None => return false,
@@ -5001,7 +5066,7 @@ impl Type {
     }
 
     // FIXME: Find better API that also handles const generics
-    pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
+    pub fn impls_trait(&self, db: &'db dyn HirDatabase, trait_: Trait, args: &[Type<'db>]) -> bool {
         let mut it = args.iter().map(|t| t.ty.clone());
         let trait_ref = TyBuilder::trait_ref(db, trait_.id)
             .push(self.ty.clone())
@@ -5029,10 +5094,10 @@ impl Type {
 
     pub fn normalize_trait_assoc_type(
         &self,
-        db: &dyn HirDatabase,
-        args: &[Type],
+        db: &'db dyn HirDatabase,
+        args: &[Type<'db>],
         alias: TypeAlias,
-    ) -> Option<Type> {
+    ) -> Option<Type<'db>> {
         let mut args = args.iter();
         let trait_id = match alias.id.lookup(db).container {
             ItemContainerId::TraitId(id) => id,
@@ -5056,14 +5121,14 @@ impl Type {
         if ty.is_unknown() { None } else { Some(self.derived(ty)) }
     }
 
-    pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
+    pub fn is_copy(&self, db: &'db dyn HirDatabase) -> bool {
         let Some(copy_trait) = LangItem::Copy.resolve_trait(db, self.env.krate) else {
             return false;
         };
         self.impls_trait(db, copy_trait.into(), &[])
     }
 
-    pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
+    pub fn as_callable(&self, db: &'db dyn HirDatabase) -> Option<Callable<'db>> {
         let callee = match self.ty.kind(Interner) {
             TyKind::Closure(id, subst) => Callee::Closure(*id, subst.clone()),
             TyKind::Function(_) => Callee::FnPtr,
@@ -5117,7 +5182,7 @@ impl Type {
         matches!(self.ty.kind(Interner), TyKind::Array(..))
     }
 
-    pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
+    pub fn is_packed(&self, db: &'db dyn HirDatabase) -> bool {
         let adt_id = match *self.ty.kind(Interner) {
             TyKind::Adt(hir_ty::AdtId(adt_id), ..) => adt_id,
             _ => return false,
@@ -5134,7 +5199,7 @@ impl Type {
         matches!(self.ty.kind(Interner), TyKind::Raw(..))
     }
 
-    pub fn remove_raw_ptr(&self) -> Option<Type> {
+    pub fn remove_raw_ptr(&self) -> Option<Type<'db>> {
         if let TyKind::Raw(_, ty) = self.ty.kind(Interner) {
             Some(self.derived(ty.clone()))
         } else {
@@ -5182,7 +5247,7 @@ impl Type {
         }
     }
 
-    pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
+    pub fn fields(&self, db: &'db dyn HirDatabase) -> Vec<(Field, Self)> {
         let (variant_id, substs) = match self.ty.kind(Interner) {
             TyKind::Adt(hir_ty::AdtId(AdtId::StructId(s)), substs) => ((*s).into(), substs),
             TyKind::Adt(hir_ty::AdtId(AdtId::UnionId(u)), substs) => ((*u).into(), substs),
@@ -5199,7 +5264,7 @@ impl Type {
             .collect()
     }
 
-    pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> {
+    pub fn tuple_fields(&self, _db: &'db dyn HirDatabase) -> Vec<Self> {
         if let TyKind::Tuple(_, substs) = &self.ty.kind(Interner) {
             substs
                 .iter(Interner)
@@ -5210,7 +5275,7 @@ impl Type {
         }
     }
 
-    pub fn as_array(&self, db: &dyn HirDatabase) -> Option<(Type, usize)> {
+    pub fn as_array(&self, db: &'db dyn HirDatabase) -> Option<(Self, usize)> {
         if let TyKind::Array(ty, len) = &self.ty.kind(Interner) {
             try_const_usize(db, len).map(|it| (self.derived(ty.clone()), it as usize))
         } else {
@@ -5228,14 +5293,14 @@ impl Type {
 
     /// Returns types that this type dereferences to (including this type itself). The returned
     /// iterator won't yield the same type more than once even if the deref chain contains a cycle.
-    pub fn autoderef<'db>(
+    pub fn autoderef(
         &self,
         db: &'db dyn HirDatabase,
-    ) -> impl Iterator<Item = Type> + use<'_, 'db> {
+    ) -> impl Iterator<Item = Type<'db>> + use<'_, 'db> {
         self.autoderef_(db).map(move |ty| self.derived(ty))
     }
 
-    fn autoderef_(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Ty> {
+    fn autoderef_(&self, db: &'db dyn HirDatabase) -> impl Iterator<Item = Ty> {
         // There should be no inference vars in types passed here
         let canonical = hir_ty::replace_errors_with_variables(&self.ty);
         autoderef(db, self.env.clone(), canonical)
@@ -5245,7 +5310,7 @@ impl Type {
     // lifetime problems, because we need to borrow temp `CrateImplDefs`.
     pub fn iterate_assoc_items<T>(
         &self,
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         krate: Crate,
         mut callback: impl FnMut(AssocItem) -> Option<T>,
     ) -> Option<T> {
@@ -5259,7 +5324,7 @@ impl Type {
 
     fn iterate_assoc_items_dyn(
         &self,
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         krate: Crate,
         callback: &mut dyn FnMut(AssocItemId) -> bool,
     ) {
@@ -5298,7 +5363,7 @@ impl Type {
     /// - "String"
     /// - "U"
     /// ```
-    pub fn type_arguments(&self) -> impl Iterator<Item = Type> + '_ {
+    pub fn type_arguments(&self) -> impl Iterator<Item = Type<'db>> + '_ {
         self.ty
             .strip_references()
             .as_adt()
@@ -5368,7 +5433,7 @@ impl Type {
 
     pub fn iterate_method_candidates_with_traits<T>(
         &self,
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
         traits_in_scope: &FxHashSet<TraitId>,
         with_local_impls: Option<Module>,
@@ -5396,7 +5461,7 @@ impl Type {
 
     pub fn iterate_method_candidates<T>(
         &self,
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
         with_local_impls: Option<Module>,
         name: Option<&Name>,
@@ -5418,7 +5483,7 @@ impl Type {
     /// are considered inherent methods.
     pub fn iterate_method_candidates_split_inherent(
         &self,
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
         traits_in_scope: &FxHashSet<TraitId>,
         with_local_impls: Option<Module>,
@@ -5486,7 +5551,7 @@ impl Type {
     #[tracing::instrument(skip_all, fields(name = ?name))]
     pub fn iterate_path_candidates<T>(
         &self,
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
         traits_in_scope: &FxHashSet<TraitId>,
         with_local_impls: Option<Module>,
@@ -5521,7 +5586,7 @@ impl Type {
     #[tracing::instrument(skip_all, fields(name = ?name))]
     pub fn iterate_path_candidates_split_inherent(
         &self,
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         scope: &SemanticsScope<'_>,
         traits_in_scope: &FxHashSet<TraitId>,
         with_local_impls: Option<Module>,
@@ -5584,10 +5649,10 @@ impl Type {
 
     /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type,
     /// or an empty iterator otherwise.
-    pub fn applicable_inherent_traits<'a>(
-        &'a self,
-        db: &'a dyn HirDatabase,
-    ) -> impl Iterator<Item = Trait> + 'a {
+    pub fn applicable_inherent_traits(
+        &self,
+        db: &'db dyn HirDatabase,
+    ) -> impl Iterator<Item = Trait> {
         let _p = tracing::info_span!("applicable_inherent_traits").entered();
         self.autoderef_(db)
             .filter_map(|ty| ty.dyn_trait())
@@ -5595,7 +5660,7 @@ impl Type {
             .map(Trait::from)
     }
 
-    pub fn env_traits<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Trait> + 'a {
+    pub fn env_traits(&self, db: &'db dyn HirDatabase) -> impl Iterator<Item = Trait> {
         let _p = tracing::info_span!("env_traits").entered();
         self.autoderef_(db)
             .filter(|ty| matches!(ty.kind(Interner), TyKind::Placeholder(_)))
@@ -5607,10 +5672,7 @@ impl Type {
             .map(Trait::from)
     }
 
-    pub fn as_impl_traits(
-        &self,
-        db: &dyn HirDatabase,
-    ) -> Option<impl Iterator<Item = Trait> + use<>> {
+    pub fn as_impl_traits(&self, db: &'db dyn HirDatabase) -> Option<impl Iterator<Item = Trait>> {
         self.ty.impl_trait_bounds(db).map(|it| {
             it.into_iter().filter_map(|pred| match pred.skip_binders() {
                 hir_ty::WhereClause::Implemented(trait_ref) => {
@@ -5621,33 +5683,33 @@ impl Type {
         })
     }
 
-    pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> {
+    pub fn as_associated_type_parent_trait(&self, db: &'db dyn HirDatabase) -> Option<Trait> {
         self.ty.associated_type_parent_trait(db).map(Into::into)
     }
 
-    fn derived(&self, ty: Ty) -> Type {
-        Type { env: self.env.clone(), ty }
+    fn derived(&self, ty: Ty) -> Self {
+        Type { env: self.env.clone(), ty, _pd: PhantomCovariantLifetime::new() }
     }
 
     /// Visits every type, including generic arguments, in this type. `cb` is called with type
     /// itself first, and then with its generic arguments.
-    pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
-        fn walk_substs(
-            db: &dyn HirDatabase,
-            type_: &Type,
+    pub fn walk(&self, db: &'db dyn HirDatabase, mut cb: impl FnMut(Type<'db>)) {
+        fn walk_substs<'db>(
+            db: &'db dyn HirDatabase,
+            type_: &Type<'db>,
             substs: &Substitution,
-            cb: &mut impl FnMut(Type),
+            cb: &mut impl FnMut(Type<'db>),
         ) {
             for ty in substs.iter(Interner).filter_map(|a| a.ty(Interner)) {
                 walk_type(db, &type_.derived(ty.clone()), cb);
             }
         }
 
-        fn walk_bounds(
-            db: &dyn HirDatabase,
-            type_: &Type,
+        fn walk_bounds<'db>(
+            db: &'db dyn HirDatabase,
+            type_: &Type<'db>,
             bounds: &[QuantifiedWhereClause],
-            cb: &mut impl FnMut(Type),
+            cb: &mut impl FnMut(Type<'db>),
         ) {
             for pred in bounds {
                 if let WhereClause::Implemented(trait_ref) = pred.skip_binders() {
@@ -5664,7 +5726,11 @@ impl Type {
             }
         }
 
-        fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
+        fn walk_type<'db>(
+            db: &'db dyn HirDatabase,
+            type_: &Type<'db>,
+            cb: &mut impl FnMut(Type<'db>),
+        ) {
             let ty = type_.ty.strip_references();
             match ty.kind(Interner) {
                 TyKind::Adt(_, substs) => {
@@ -5732,7 +5798,7 @@ impl Type {
     ///
     /// Note that we consider placeholder types to unify with everything.
     /// For example `Option<T>` and `Option<U>` unify although there is unresolved goal `T = U`.
-    pub fn could_unify_with(&self, db: &dyn HirDatabase, other: &Type) -> bool {
+    pub fn could_unify_with(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool {
         let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
         hir_ty::could_unify(db, self.env.clone(), &tys)
     }
@@ -5741,17 +5807,17 @@ impl Type {
     ///
     /// This means that placeholder types are not considered to unify if there are any bounds set on
     /// them. For example `Option<T>` and `Option<U>` do not unify as we cannot show that `T = U`
-    pub fn could_unify_with_deeply(&self, db: &dyn HirDatabase, other: &Type) -> bool {
+    pub fn could_unify_with_deeply(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool {
         let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), other.ty.clone()));
         hir_ty::could_unify_deeply(db, self.env.clone(), &tys)
     }
 
-    pub fn could_coerce_to(&self, db: &dyn HirDatabase, to: &Type) -> bool {
+    pub fn could_coerce_to(&self, db: &'db dyn HirDatabase, to: &Type<'db>) -> bool {
         let tys = hir_ty::replace_errors_with_variables(&(self.ty.clone(), to.ty.clone()));
         hir_ty::could_coerce(db, self.env.clone(), &tys)
     }
 
-    pub fn as_type_param(&self, db: &dyn HirDatabase) -> Option<TypeParam> {
+    pub fn as_type_param(&self, db: &'db dyn HirDatabase) -> Option<TypeParam> {
         match self.ty.kind(Interner) {
             TyKind::Placeholder(p) => Some(TypeParam {
                 id: TypeParamId::from_unchecked(hir_ty::from_placeholder_idx(db, *p)),
@@ -5761,19 +5827,19 @@ impl Type {
     }
 
     /// Returns unique `GenericParam`s contained in this type.
-    pub fn generic_params(&self, db: &dyn HirDatabase) -> FxHashSet<GenericParam> {
+    pub fn generic_params(&self, db: &'db dyn HirDatabase) -> FxHashSet<GenericParam> {
         hir_ty::collect_placeholders(&self.ty, db)
             .into_iter()
             .map(|id| TypeOrConstParam { id }.split(db).either_into())
             .collect()
     }
 
-    pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+    pub fn layout(&self, db: &'db dyn HirDatabase) -> Result<Layout, LayoutError> {
         db.layout_of_ty(self.ty.clone(), self.env.clone())
             .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap()))
     }
 
-    pub fn drop_glue(&self, db: &dyn HirDatabase) -> DropGlue {
+    pub fn drop_glue(&self, db: &'db dyn HirDatabase) -> DropGlue {
         db.has_drop_glue(self.ty.clone(), self.env.clone())
     }
 }
@@ -5800,8 +5866,8 @@ impl InlineAsmOperand {
 
 // FIXME: Document this
 #[derive(Debug)]
-pub struct Callable {
-    ty: Type,
+pub struct Callable<'db> {
+    ty: Type<'db>,
     sig: CallableSig,
     callee: Callee,
     /// Whether this is a method that was called with method call syntax.
@@ -5825,7 +5891,7 @@ pub enum CallableKind {
     FnImpl(FnTrait),
 }
 
-impl Callable {
+impl<'db> Callable<'db> {
     pub fn kind(&self) -> CallableKind {
         match self.callee {
             Callee::Def(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()),
@@ -5840,7 +5906,7 @@ impl Callable {
             Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_),
         }
     }
-    pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(SelfParam, Type)> {
+    pub fn receiver_param(&self, db: &'db dyn HirDatabase) -> Option<(SelfParam, Type<'db>)> {
         let func = match self.callee {
             Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
             _ => return None,
@@ -5851,7 +5917,7 @@ impl Callable {
     pub fn n_params(&self) -> usize {
         self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
     }
-    pub fn params(&self) -> Vec<Param> {
+    pub fn params(&self) -> Vec<Param<'db>> {
         self.sig
             .params()
             .iter()
@@ -5861,14 +5927,14 @@ impl Callable {
             .map(|(idx, ty)| Param { func: self.callee.clone(), idx, ty })
             .collect()
     }
-    pub fn return_type(&self) -> Type {
+    pub fn return_type(&self) -> Type<'db> {
         self.ty.derived(self.sig.ret().clone())
     }
     pub fn sig(&self) -> &CallableSig {
         &self.sig
     }
 
-    pub fn ty(&self) -> &Type {
+    pub fn ty(&self) -> &Type<'db> {
         &self.ty
     }
 }
@@ -6070,9 +6136,9 @@ impl From<ItemInNs> for ScopeDef {
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub struct Adjustment {
-    pub source: Type,
-    pub target: Type,
+pub struct Adjustment<'db> {
+    pub source: Type<'db>,
+    pub target: Type<'db>,
     pub kind: Adjust,
 }
 
@@ -6171,7 +6237,7 @@ impl HasCrate for TypeAlias {
     }
 }
 
-impl HasCrate for Type {
+impl HasCrate for Type<'_> {
     fn krate(&self, _db: &dyn HirDatabase) -> Crate {
         self.env.krate.into()
     }
@@ -6325,9 +6391,9 @@ pub enum DocLinkDef {
     SelfType(Trait),
 }
 
-fn push_ty_diagnostics(
-    db: &dyn HirDatabase,
-    acc: &mut Vec<AnyDiagnostic>,
+fn push_ty_diagnostics<'db>(
+    db: &'db dyn HirDatabase,
+    acc: &mut Vec<AnyDiagnostic<'db>>,
     diagnostics: Option<ThinArc<(), TyLoweringDiagnostic>>,
     source_map: &ExpressionStoreSourceMap,
 ) {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 10498958242..d96975831e0 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -123,15 +123,15 @@ impl PathResolutionPerNs {
 }
 
 #[derive(Debug)]
-pub struct TypeInfo {
+pub struct TypeInfo<'db> {
     /// The original type of the expression or pattern.
-    pub original: Type,
+    pub original: Type<'db>,
     /// The adjusted type, if an adjustment happened.
-    pub adjusted: Option<Type>,
+    pub adjusted: Option<Type<'db>>,
 }
 
-impl TypeInfo {
-    pub fn original(self) -> Type {
+impl<'db> TypeInfo<'db> {
+    pub fn original(self) -> Type<'db> {
         self.original
     }
 
@@ -140,7 +140,7 @@ impl TypeInfo {
     }
 
     /// The adjusted type, or the original in case no adjustments occurred.
-    pub fn adjusted(self) -> Type {
+    pub fn adjusted(self) -> Type<'db> {
         self.adjusted.unwrap_or(self.original)
     }
 }
@@ -1534,7 +1534,7 @@ impl<'db> SemanticsImpl<'db> {
         Some(Label { parent, label_id })
     }
 
-    pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
+    pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type<'db>> {
         let analyze = self.analyze(ty.syntax())?;
         analyze.type_of_type(self.db, ty)
     }
@@ -1553,7 +1553,7 @@ impl<'db> SemanticsImpl<'db> {
         }
     }
 
-    pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
+    pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment<'db>>> {
         let mutability = |m| match m {
             hir_ty::Mutability::Not => Mutability::Shared,
             hir_ty::Mutability::Mut => Mutability::Mut,
@@ -1596,13 +1596,13 @@ impl<'db> SemanticsImpl<'db> {
         })
     }
 
-    pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
+    pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo<'db>> {
         self.analyze(expr.syntax())?
             .type_of_expr(self.db, expr)
             .map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
     }
 
-    pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
+    pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo<'db>> {
         self.analyze(pat.syntax())?
             .type_of_pat(self.db, pat)
             .map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
@@ -1611,15 +1611,15 @@ impl<'db> SemanticsImpl<'db> {
     /// It also includes the changes that binding mode makes in the type. For example in
     /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result
     /// of this function is `&mut Option<T>`
-    pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
+    pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type<'db>> {
         self.analyze(pat.syntax())?.type_of_binding_in_pat(self.db, pat)
     }
 
-    pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
+    pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type<'db>> {
         self.analyze(param.syntax())?.type_of_self(self.db, param)
     }
 
-    pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
+    pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type<'db>; 1]> {
         self.analyze(pat.syntax())
             .and_then(|it| it.pattern_adjustments(self.db, pat))
             .unwrap_or_default()
@@ -1629,7 +1629,7 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(pat.syntax())?.binding_mode_of_pat(self.db, pat)
     }
 
-    pub fn resolve_expr_as_callable(&self, call: &ast::Expr) -> Option<Callable> {
+    pub fn resolve_expr_as_callable(&self, call: &ast::Expr) -> Option<Callable<'db>> {
         self.analyze(call.syntax())?.resolve_expr_as_callable(self.db, call)
     }
 
@@ -1641,7 +1641,7 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_method_call_fallback(
         &self,
         call: &ast::MethodCallExpr,
-    ) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> {
+    ) -> Option<(Either<Function, Field>, Option<GenericSubstitution<'db>>)> {
         self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
     }
 
@@ -1649,10 +1649,10 @@ impl<'db> SemanticsImpl<'db> {
     // FIXME: better api for the trait environment
     pub fn resolve_trait_impl_method(
         &self,
-        env: Type,
+        env: Type<'db>,
         trait_: Trait,
         func: Function,
-        subst: impl IntoIterator<Item = Type>,
+        subst: impl IntoIterator<Item = Type<'db>>,
     ) -> Option<Function> {
         let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None);
         for s in subst {
@@ -1691,7 +1691,10 @@ impl<'db> SemanticsImpl<'db> {
 
     // This does not resolve the method call to the correct trait impl!
     // We should probably fix that.
-    pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
+    pub fn resolve_method_call_as_callable(
+        &self,
+        call: &ast::MethodCallExpr,
+    ) -> Option<Callable<'db>> {
         self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
     }
 
@@ -1702,14 +1705,15 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_field_fallback(
         &self,
         field: &ast::FieldExpr,
-    ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> {
+    ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution<'db>>)>
+    {
         self.analyze(field.syntax())?.resolve_field_fallback(self.db, field)
     }
 
     pub fn resolve_record_field(
         &self,
         field: &ast::RecordExprField,
-    ) -> Option<(Field, Option<Local>, Type)> {
+    ) -> Option<(Field, Option<Local>, Type<'db>)> {
         self.resolve_record_field_with_substitution(field)
             .map(|(field, local, ty, _)| (field, local, ty))
     }
@@ -1717,18 +1721,21 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_record_field_with_substitution(
         &self,
         field: &ast::RecordExprField,
-    ) -> Option<(Field, Option<Local>, Type, GenericSubstitution)> {
+    ) -> Option<(Field, Option<Local>, Type<'db>, GenericSubstitution<'db>)> {
         self.analyze(field.syntax())?.resolve_record_field(self.db, field)
     }
 
-    pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
+    pub fn resolve_record_pat_field(
+        &self,
+        field: &ast::RecordPatField,
+    ) -> Option<(Field, Type<'db>)> {
         self.resolve_record_pat_field_with_subst(field).map(|(field, ty, _)| (field, ty))
     }
 
     pub fn resolve_record_pat_field_with_subst(
         &self,
         field: &ast::RecordPatField,
-    ) -> Option<(Field, Type, GenericSubstitution)> {
+    ) -> Option<(Field, Type<'db>, GenericSubstitution<'db>)> {
         self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field)
     }
 
@@ -1801,7 +1808,7 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_path_with_subst(
         &self,
         path: &ast::Path,
-    ) -> Option<(PathResolution, Option<GenericSubstitution>)> {
+    ) -> Option<(PathResolution, Option<GenericSubstitution<'db>>)> {
         self.analyze(path.syntax())?.resolve_path(self.db, path)
     }
 
@@ -1812,7 +1819,7 @@ impl<'db> SemanticsImpl<'db> {
     pub fn resolve_offset_of_field(
         &self,
         name_ref: &ast::NameRef,
-    ) -> Option<(Either<Variant, Field>, GenericSubstitution)> {
+    ) -> Option<(Either<Variant, Field>, GenericSubstitution<'db>)> {
         self.analyze_no_infer(name_ref.syntax())?.resolve_offset_of_field(self.db, name_ref)
     }
 
@@ -1834,13 +1841,19 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(pat.syntax())?.resolve_bind_pat_to_const(self.db, pat)
     }
 
-    pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
+    pub fn record_literal_missing_fields(
+        &self,
+        literal: &ast::RecordExpr,
+    ) -> Vec<(Field, Type<'db>)> {
         self.analyze(literal.syntax())
             .and_then(|it| it.record_literal_missing_fields(self.db, literal))
             .unwrap_or_default()
     }
 
-    pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
+    pub fn record_pattern_missing_fields(
+        &self,
+        pattern: &ast::RecordPat,
+    ) -> Vec<(Field, Type<'db>)> {
         self.analyze(pattern.syntax())
             .and_then(|it| it.record_pattern_missing_fields(self.db, pattern))
             .unwrap_or_default()
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 3273358b78e..48543ca581f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -257,7 +257,11 @@ impl<'db> SourceAnalyzer<'db> {
         infer.expr_adjustments.get(&expr_id).map(|v| &**v)
     }
 
-    pub(crate) fn type_of_type(&self, db: &'db dyn HirDatabase, ty: &ast::Type) -> Option<Type> {
+    pub(crate) fn type_of_type(
+        &self,
+        db: &'db dyn HirDatabase,
+        ty: &ast::Type,
+    ) -> Option<Type<'db>> {
         let type_ref = self.type_id(ty)?;
         let ty = TyLoweringContext::new(
             db,
@@ -277,7 +281,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         expr: &ast::Expr,
-    ) -> Option<(Type, Option<Type>)> {
+    ) -> Option<(Type<'db>, Option<Type<'db>>)> {
         let expr_id = self.expr_id(expr.clone())?;
         let infer = self.infer()?;
         let coerced = expr_id
@@ -293,7 +297,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         pat: &ast::Pat,
-    ) -> Option<(Type, Option<Type>)> {
+    ) -> Option<(Type<'db>, Option<Type<'db>>)> {
         let expr_or_pat_id = self.pat_id(pat)?;
         let infer = self.infer()?;
         let coerced = match expr_or_pat_id {
@@ -316,7 +320,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         pat: &ast::IdentPat,
-    ) -> Option<Type> {
+    ) -> Option<Type<'db>> {
         let binding_id = self.binding_id_of_pat(pat)?;
         let infer = self.infer()?;
         let ty = infer[binding_id].clone();
@@ -328,7 +332,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         _param: &ast::SelfParam,
-    ) -> Option<Type> {
+    ) -> Option<Type<'db>> {
         let binding = self.body()?.self_param?;
         let ty = self.infer()?[binding].clone();
         Some(Type::new_with_resolver(db, &self.resolver, ty))
@@ -353,7 +357,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         pat: &ast::Pat,
-    ) -> Option<SmallVec<[Type; 1]>> {
+    ) -> Option<SmallVec<[Type<'db>; 1]>> {
         let pat_id = self.pat_id(pat)?;
         let infer = self.infer()?;
         Some(
@@ -370,7 +374,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         call: &ast::MethodCallExpr,
-    ) -> Option<Callable> {
+    ) -> Option<Callable<'db>> {
         let expr_id = self.expr_id(call.clone().into())?.as_expr()?;
         let (func, substs) = self.infer()?.method_resolution(expr_id)?;
         let ty = db.value_ty(func.into())?.substitute(Interner, &substs);
@@ -395,7 +399,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         call: &ast::MethodCallExpr,
-    ) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> {
+    ) -> Option<(Either<Function, Field>, Option<GenericSubstitution<'db>>)> {
         let expr_id = self.expr_id(call.clone().into())?.as_expr()?;
         let inference_result = self.infer()?;
         match inference_result.method_resolution(expr_id) {
@@ -419,7 +423,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         call: &ast::Expr,
-    ) -> Option<Callable> {
+    ) -> Option<Callable<'db>> {
         let (orig, adjusted) = self.type_of_expr(db, &call.clone())?;
         adjusted.unwrap_or(orig).as_callable(db)
     }
@@ -440,7 +444,7 @@ impl<'db> SourceAnalyzer<'db> {
         field_expr: ExprId,
         infer: &InferenceResult,
         db: &'db dyn HirDatabase,
-    ) -> Option<GenericSubstitution> {
+    ) -> Option<GenericSubstitution<'db>> {
         let body = self.store()?;
         if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] {
             let (adt, subst) = type_of_expr_including_adjust(infer, object_expr)?.as_adt()?;
@@ -457,7 +461,8 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         field: &ast::FieldExpr,
-    ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> {
+    ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution<'db>>)>
+    {
         let (def, ..) = self.body_()?;
         let expr_id = self.expr_id(field.clone().into())?.as_expr()?;
         let inference_result = self.infer()?;
@@ -680,7 +685,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         field: &ast::RecordExprField,
-    ) -> Option<(Field, Option<Local>, Type, GenericSubstitution)> {
+    ) -> Option<(Field, Option<Local>, Type<'db>, GenericSubstitution<'db>)> {
         let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
         let expr = ast::Expr::from(record_expr);
         let expr_id = self.store_sm()?.node_expr(InFile::new(self.file_id, &expr))?;
@@ -724,7 +729,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         field: &ast::RecordPatField,
-    ) -> Option<(Field, Type, GenericSubstitution)> {
+    ) -> Option<(Field, Type<'db>, GenericSubstitution<'db>)> {
         let field_name = field.field_name()?.as_name();
         let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
         let pat_id = self.pat_id(&record_pat.into())?;
@@ -779,7 +784,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         name_ref: &ast::NameRef,
-    ) -> Option<(Either<crate::Variant, crate::Field>, GenericSubstitution)> {
+    ) -> Option<(Either<crate::Variant, crate::Field>, GenericSubstitution<'db>)> {
         let offset_of_expr = ast::OffsetOfExpr::cast(name_ref.syntax().parent()?)?;
         let container = offset_of_expr.ty()?;
         let container = self.type_of_type(db, &container)?;
@@ -851,7 +856,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         path: &ast::Path,
-    ) -> Option<(PathResolution, Option<GenericSubstitution>)> {
+    ) -> Option<(PathResolution, Option<GenericSubstitution<'db>>)> {
         let parent = path.syntax().parent();
         let parent = || parent.clone();
 
@@ -1216,7 +1221,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         literal: &ast::RecordExpr,
-    ) -> Option<Vec<(Field, Type)>> {
+    ) -> Option<Vec<(Field, Type<'db>)>> {
         let body = self.store()?;
         let infer = self.infer()?;
 
@@ -1239,7 +1244,7 @@ impl<'db> SourceAnalyzer<'db> {
         &self,
         db: &'db dyn HirDatabase,
         pattern: &ast::RecordPat,
-    ) -> Option<Vec<(Field, Type)>> {
+    ) -> Option<Vec<(Field, Type<'db>)>> {
         let body = self.store()?;
         let infer = self.infer()?;
 
@@ -1258,7 +1263,7 @@ impl<'db> SourceAnalyzer<'db> {
         substs: &Substitution,
         variant: VariantId,
         missing_fields: Vec<LocalFieldId>,
-    ) -> Vec<(Field, Type)> {
+    ) -> Vec<(Field, Type<'db>)> {
         let field_types = db.field_types(variant);
 
         missing_fields
diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs
index af72179305c..4b354e64062 100644
--- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs
@@ -22,20 +22,20 @@ enum NewTypesKey {
 /// Helper enum to squash big number of alternative trees into `Many` variant as there is too many
 /// to take into account.
 #[derive(Debug)]
-enum AlternativeExprs {
+enum AlternativeExprs<'db> {
     /// There are few trees, so we keep track of them all
-    Few(FxHashSet<Expr>),
+    Few(FxHashSet<Expr<'db>>),
     /// There are too many trees to keep track of
     Many,
 }
 
-impl AlternativeExprs {
+impl<'db> AlternativeExprs<'db> {
     /// Construct alternative trees
     ///
     /// # Arguments
     /// `threshold` - threshold value for many trees (more than that is many)
     /// `exprs` - expressions iterator
-    fn new(threshold: usize, exprs: impl Iterator<Item = Expr>) -> AlternativeExprs {
+    fn new(threshold: usize, exprs: impl Iterator<Item = Expr<'db>>) -> AlternativeExprs<'db> {
         let mut it = AlternativeExprs::Few(Default::default());
         it.extend_with_threshold(threshold, exprs);
         it
@@ -45,7 +45,7 @@ impl AlternativeExprs {
     ///
     /// # Arguments
     /// `ty` - Type of expressions queried (this is used to give type to `Expr::Many`)
-    fn exprs(&self, ty: &Type) -> Vec<Expr> {
+    fn exprs(&self, ty: &Type<'db>) -> Vec<Expr<'db>> {
         match self {
             AlternativeExprs::Few(exprs) => exprs.iter().cloned().collect(),
             AlternativeExprs::Many => vec![Expr::Many(ty.clone())],
@@ -57,7 +57,7 @@ impl AlternativeExprs {
     /// # Arguments
     /// `threshold` - threshold value for many trees (more than that is many)
     /// `exprs` - expressions iterator
-    fn extend_with_threshold(&mut self, threshold: usize, exprs: impl Iterator<Item = Expr>) {
+    fn extend_with_threshold(&mut self, threshold: usize, exprs: impl Iterator<Item = Expr<'db>>) {
         match self {
             AlternativeExprs::Few(tts) => {
                 for it in exprs {
@@ -88,20 +88,20 @@ impl AlternativeExprs {
 /// Both of them are to speed up the term search by leaving out types / ScopeDefs that likely do
 /// not produce any new results.
 #[derive(Default, Debug)]
-struct LookupTable {
+struct LookupTable<'db> {
     /// All the `Expr`s in "value" produce the type of "key"
-    data: FxHashMap<Type, AlternativeExprs>,
+    data: FxHashMap<Type<'db>, AlternativeExprs<'db>>,
     /// New types reached since last query by the `NewTypesKey`
-    new_types: FxHashMap<NewTypesKey, Vec<Type>>,
+    new_types: FxHashMap<NewTypesKey, Vec<Type<'db>>>,
     /// Types queried but not present
-    types_wishlist: FxHashSet<Type>,
+    types_wishlist: FxHashSet<Type<'db>>,
     /// Threshold to squash trees to `Many`
     many_threshold: usize,
 }
 
-impl LookupTable {
+impl<'db> LookupTable<'db> {
     /// Initialize lookup table
-    fn new(many_threshold: usize, goal: Type) -> Self {
+    fn new(many_threshold: usize, goal: Type<'db>) -> Self {
         let mut res = Self { many_threshold, ..Default::default() };
         res.new_types.insert(NewTypesKey::ImplMethod, Vec::new());
         res.new_types.insert(NewTypesKey::StructProjection, Vec::new());
@@ -110,7 +110,7 @@ impl LookupTable {
     }
 
     /// Find all `Expr`s that unify with the `ty`
-    fn find(&mut self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
+    fn find(&mut self, db: &'db dyn HirDatabase, ty: &Type<'db>) -> Option<Vec<Expr<'db>>> {
         let res = self
             .data
             .iter()
@@ -135,7 +135,7 @@ impl LookupTable {
     ///
     /// For example if we have type `i32` in data and we query for `&i32` it map all the type
     /// trees we have for `i32` with `Expr::Reference` and returns them.
-    fn find_autoref(&mut self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
+    fn find_autoref(&mut self, db: &'db dyn HirDatabase, ty: &Type<'db>) -> Option<Vec<Expr<'db>>> {
         let res = self
             .data
             .iter()
@@ -174,7 +174,7 @@ impl LookupTable {
     /// Note that the types have to be the same, unification is not enough as unification is not
     /// transitive. For example Vec<i32> and FxHashSet<i32> both unify with Iterator<Item = i32>,
     /// but they clearly do not unify themselves.
-    fn insert(&mut self, ty: Type, exprs: impl Iterator<Item = Expr>) {
+    fn insert(&mut self, ty: Type<'db>, exprs: impl Iterator<Item = Expr<'db>>) {
         match self.data.get_mut(&ty) {
             Some(it) => {
                 it.extend_with_threshold(self.many_threshold, exprs);
@@ -192,14 +192,14 @@ impl LookupTable {
     }
 
     /// Iterate all the reachable types
-    fn iter_types(&self) -> impl Iterator<Item = Type> + '_ {
+    fn iter_types(&self) -> impl Iterator<Item = Type<'db>> + '_ {
         self.data.keys().cloned()
     }
 
     /// Query new types reached since last query by key
     ///
     /// Create new key if you wish to query it to avoid conflicting with existing queries.
-    fn new_types(&mut self, key: NewTypesKey) -> Vec<Type> {
+    fn new_types(&mut self, key: NewTypesKey) -> Vec<Type<'db>> {
         match self.new_types.get_mut(&key) {
             Some(it) => std::mem::take(it),
             None => Vec::new(),
@@ -207,20 +207,20 @@ impl LookupTable {
     }
 
     /// Types queried but not found
-    fn types_wishlist(&mut self) -> &FxHashSet<Type> {
+    fn types_wishlist(&mut self) -> &FxHashSet<Type<'db>> {
         &self.types_wishlist
     }
 }
 
 /// Context for the `term_search` function
 #[derive(Debug)]
-pub struct TermSearchCtx<'a, DB: HirDatabase> {
+pub struct TermSearchCtx<'db, DB: HirDatabase> {
     /// Semantics for the program
-    pub sema: &'a Semantics<'a, DB>,
+    pub sema: &'db Semantics<'db, DB>,
     /// Semantic scope, captures context for the term search
-    pub scope: &'a SemanticsScope<'a>,
+    pub scope: &'db SemanticsScope<'db>,
     /// Target / expected output type
-    pub goal: Type,
+    pub goal: Type<'db>,
     /// Configuration for term search
     pub config: TermSearchConfig,
 }
@@ -263,7 +263,7 @@ impl Default for TermSearchConfig {
 /// Note that there are usually more ways we can get to the `goal` type but some are discarded to
 /// reduce the memory consumption. It is also unlikely anyone is willing ti browse through
 /// thousands of possible responses so we currently take first 10 from every tactic.
-pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
+pub fn term_search<'db, DB: HirDatabase>(ctx: &'db TermSearchCtx<'db, DB>) -> Vec<Expr<'db>> {
     let module = ctx.scope.module();
     let mut defs = FxHashSet::default();
     defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module)));
@@ -285,7 +285,7 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
     };
 
     // Try trivial tactic first, also populates lookup table
-    let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect();
+    let mut solutions: Vec<Expr<'db>> = tactics::trivial(ctx, &defs, &mut lookup).collect();
     // Use well known types tactic before iterations as it does not depend on other tactics
     solutions.extend(tactics::famous_types(ctx, &defs, &mut lookup));
     solutions.extend(tactics::assoc_const(ctx, &defs, &mut lookup));
diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs
index 78ee3b5aa68..843831948ad 100644
--- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs
@@ -59,7 +59,7 @@ fn mod_item_path_str(
 /// So in short it pretty much gives us a way to get type `Option<i32>` using the items we have in
 /// scope.
 #[derive(Debug, Clone, Eq, Hash, PartialEq)]
-pub enum Expr {
+pub enum Expr<'db> {
     /// Constant
     Const(Const),
     /// Static variable
@@ -69,26 +69,31 @@ pub enum Expr {
     /// Constant generic parameter
     ConstParam(ConstParam),
     /// Well known type (such as `true` for bool)
-    FamousType { ty: Type, value: &'static str },
+    FamousType { ty: Type<'db>, value: &'static str },
     /// Function call (does not take self param)
-    Function { func: Function, generics: Vec<Type>, params: Vec<Expr> },
+    Function { func: Function, generics: Vec<Type<'db>>, params: Vec<Expr<'db>> },
     /// Method call (has self param)
-    Method { func: Function, generics: Vec<Type>, target: Box<Expr>, params: Vec<Expr> },
+    Method {
+        func: Function,
+        generics: Vec<Type<'db>>,
+        target: Box<Expr<'db>>,
+        params: Vec<Expr<'db>>,
+    },
     /// Enum variant construction
-    Variant { variant: Variant, generics: Vec<Type>, params: Vec<Expr> },
+    Variant { variant: Variant, generics: Vec<Type<'db>>, params: Vec<Expr<'db>> },
     /// Struct construction
-    Struct { strukt: Struct, generics: Vec<Type>, params: Vec<Expr> },
+    Struct { strukt: Struct, generics: Vec<Type<'db>>, params: Vec<Expr<'db>> },
     /// Tuple construction
-    Tuple { ty: Type, params: Vec<Expr> },
+    Tuple { ty: Type<'db>, params: Vec<Expr<'db>> },
     /// Struct field access
-    Field { expr: Box<Expr>, field: Field },
+    Field { expr: Box<Expr<'db>>, field: Field },
     /// Passing type as reference (with `&`)
-    Reference(Box<Expr>),
+    Reference(Box<Expr<'db>>),
     /// Indicates possibility of many different options that all evaluate to `ty`
-    Many(Type),
+    Many(Type<'db>),
 }
 
-impl Expr {
+impl<'db> Expr<'db> {
     /// Generate source code for type tree.
     ///
     /// Note that trait imports are not added to generated code.
@@ -96,8 +101,8 @@ impl Expr {
     /// by `traits_used` method are also imported.
     pub fn gen_source_code(
         &self,
-        sema_scope: &SemanticsScope<'_>,
-        many_formatter: &mut dyn FnMut(&Type) -> String,
+        sema_scope: &SemanticsScope<'db>,
+        many_formatter: &mut dyn FnMut(&Type<'db>) -> String,
         cfg: ImportPathConfig,
         display_target: DisplayTarget,
     ) -> Result<String, DisplaySourceCodeError> {
@@ -298,7 +303,7 @@ impl Expr {
     /// Get type of the type tree.
     ///
     /// Same as getting the type of root node
-    pub fn ty(&self, db: &dyn HirDatabase) -> Type {
+    pub fn ty(&self, db: &'db dyn HirDatabase) -> Type<'db> {
         match self {
             Expr::Const(it) => it.ty(db),
             Expr::Static(it) => it.ty(db),
diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs
index bcff44fcd01..9df131f90e4 100644
--- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs
@@ -40,11 +40,11 @@ use super::{LookupTable, NewTypesKey, TermSearchCtx};
 ///
 /// _Note that there is no use of calling this tactic in every iteration as the output does not
 /// depend on the current state of `lookup`_
-pub(super) fn trivial<'a, DB: HirDatabase>(
-    ctx: &'a TermSearchCtx<'a, DB>,
+pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'db, DB>,
     defs: &'a FxHashSet<ScopeDef>,
-    lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = Expr> + 'a {
+    lookup: &'lt mut LookupTable<'db>,
+) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
     let db = ctx.sema.db;
     defs.iter().filter_map(|def| {
         let expr = match def {
@@ -104,11 +104,11 @@ pub(super) fn trivial<'a, DB: HirDatabase>(
 ///
 /// _Note that there is no use of calling this tactic in every iteration as the output does not
 /// depend on the current state of `lookup`_
-pub(super) fn assoc_const<'a, DB: HirDatabase>(
-    ctx: &'a TermSearchCtx<'a, DB>,
+pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'db, DB>,
     defs: &'a FxHashSet<ScopeDef>,
-    lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = Expr> + 'a {
+    lookup: &'lt mut LookupTable<'db>,
+) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
     let db = ctx.sema.db;
     let module = ctx.scope.module();
 
@@ -152,12 +152,12 @@ pub(super) fn assoc_const<'a, DB: HirDatabase>(
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
 /// * `should_continue` - Function that indicates when to stop iterating
-pub(super) fn data_constructor<'a, DB: HirDatabase>(
-    ctx: &'a TermSearchCtx<'a, DB>,
+pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'db, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
-    lookup: &'a mut LookupTable,
+    lookup: &'lt mut LookupTable<'db>,
     should_continue: &'a dyn std::ops::Fn() -> bool,
-) -> impl Iterator<Item = Expr> + 'a {
+) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
     let db = ctx.sema.db;
     let module = ctx.scope.module();
     lookup
@@ -199,14 +199,14 @@ pub(super) fn data_constructor<'a, DB: HirDatabase>(
                 let generics: Vec<_> = ty.type_arguments().collect();
 
                 // Early exit if some param cannot be filled from lookup
-                let param_exprs: Vec<Vec<Expr>> = fields
+                let param_exprs: Vec<Vec<Expr<'_>>> = fields
                     .into_iter()
                     .map(|field| lookup.find(db, &field.ty_with_args(db, generics.iter().cloned())))
                     .collect::<Option<_>>()?;
 
                 // Note that we need special case for 0 param constructors because of multi cartesian
                 // product
-                let exprs: Vec<Expr> = if param_exprs.is_empty() {
+                let exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
                     vec![Expr::Struct { strukt, generics, params: Vec::new() }]
                 } else {
                     param_exprs
@@ -247,7 +247,7 @@ pub(super) fn data_constructor<'a, DB: HirDatabase>(
                     .into_iter()
                     .filter_map(|variant| {
                         // Early exit if some param cannot be filled from lookup
-                        let param_exprs: Vec<Vec<Expr>> = variant
+                        let param_exprs: Vec<Vec<Expr<'_>>> = variant
                             .fields(db)
                             .into_iter()
                             .map(|field| {
@@ -257,7 +257,7 @@ pub(super) fn data_constructor<'a, DB: HirDatabase>(
 
                         // Note that we need special case for 0 param constructors because of multi cartesian
                         // product
-                        let variant_exprs: Vec<Expr> = if param_exprs.is_empty() {
+                        let variant_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
                             vec![Expr::Variant {
                                 variant,
                                 generics: generics.clone(),
@@ -301,12 +301,12 @@ pub(super) fn data_constructor<'a, DB: HirDatabase>(
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
 /// * `should_continue` - Function that indicates when to stop iterating
-pub(super) fn free_function<'a, DB: HirDatabase>(
-    ctx: &'a TermSearchCtx<'a, DB>,
+pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'db, DB>,
     defs: &'a FxHashSet<ScopeDef>,
-    lookup: &'a mut LookupTable,
+    lookup: &'lt mut LookupTable<'db>,
     should_continue: &'a dyn std::ops::Fn() -> bool,
-) -> impl Iterator<Item = Expr> + 'a {
+) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
     let db = ctx.sema.db;
     let module = ctx.scope.module();
     defs.iter()
@@ -375,7 +375,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
                         }
 
                         // Early exit if some param cannot be filled from lookup
-                        let param_exprs: Vec<Vec<Expr>> = it
+                        let param_exprs: Vec<Vec<Expr<'_>>> = it
                             .params_without_self_with_args(db, generics.iter().cloned())
                             .into_iter()
                             .map(|field| {
@@ -389,7 +389,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
 
                         // Note that we need special case for 0 param constructors because of multi cartesian
                         // product
-                        let fn_exprs: Vec<Expr> = if param_exprs.is_empty() {
+                        let fn_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
                             vec![Expr::Function { func: *it, generics, params: Vec::new() }]
                         } else {
                             param_exprs
@@ -432,12 +432,12 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
 /// * `should_continue` - Function that indicates when to stop iterating
-pub(super) fn impl_method<'a, DB: HirDatabase>(
-    ctx: &'a TermSearchCtx<'a, DB>,
+pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'db, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
-    lookup: &'a mut LookupTable,
+    lookup: &'lt mut LookupTable<'db>,
     should_continue: &'a dyn std::ops::Fn() -> bool,
-) -> impl Iterator<Item = Expr> + 'a {
+) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
     let db = ctx.sema.db;
     let module = ctx.scope.module();
     lookup
@@ -507,14 +507,14 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
             let target_type_exprs = lookup.find(db, &ty).expect("Type not in lookup");
 
             // Early exit if some param cannot be filled from lookup
-            let param_exprs: Vec<Vec<Expr>> = it
+            let param_exprs: Vec<Vec<Expr<'_>>> = it
                 .params_without_self_with_args(db, ty.type_arguments())
                 .into_iter()
                 .map(|field| lookup.find_autoref(db, field.ty()))
                 .collect::<Option<_>>()?;
 
             let generics: Vec<_> = ty.type_arguments().collect();
-            let fn_exprs: Vec<Expr> = std::iter::once(target_type_exprs)
+            let fn_exprs: Vec<Expr<'_>> = std::iter::once(target_type_exprs)
                 .chain(param_exprs)
                 .multi_cartesian_product()
                 .map(|params| {
@@ -547,12 +547,12 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
 /// * `should_continue` - Function that indicates when to stop iterating
-pub(super) fn struct_projection<'a, DB: HirDatabase>(
-    ctx: &'a TermSearchCtx<'a, DB>,
+pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'db, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
-    lookup: &'a mut LookupTable,
+    lookup: &'lt mut LookupTable<'db>,
     should_continue: &'a dyn std::ops::Fn() -> bool,
-) -> impl Iterator<Item = Expr> + 'a {
+) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
     let db = ctx.sema.db;
     let module = ctx.scope.module();
     lookup
@@ -589,11 +589,11 @@ pub(super) fn struct_projection<'a, DB: HirDatabase>(
 /// * `ctx` - Context for the term search
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
-pub(super) fn famous_types<'a, DB: HirDatabase>(
-    ctx: &'a TermSearchCtx<'a, DB>,
+pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'db, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
-    lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = Expr> + 'a {
+    lookup: &'lt mut LookupTable<'db>,
+) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
     let db = ctx.sema.db;
     let module = ctx.scope.module();
     [
@@ -620,12 +620,12 @@ pub(super) fn famous_types<'a, DB: HirDatabase>(
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
 /// * `should_continue` - Function that indicates when to stop iterating
-pub(super) fn impl_static_method<'a, DB: HirDatabase>(
-    ctx: &'a TermSearchCtx<'a, DB>,
+pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'db, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
-    lookup: &'a mut LookupTable,
+    lookup: &'lt mut LookupTable<'db>,
     should_continue: &'a dyn std::ops::Fn() -> bool,
-) -> impl Iterator<Item = Expr> + 'a {
+) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
     let db = ctx.sema.db;
     let module = ctx.scope.module();
     lookup
@@ -683,7 +683,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
             }
 
             // Early exit if some param cannot be filled from lookup
-            let param_exprs: Vec<Vec<Expr>> = it
+            let param_exprs: Vec<Vec<Expr<'_>>> = it
                 .params_without_self_with_args(db, ty.type_arguments())
                 .into_iter()
                 .map(|field| lookup.find_autoref(db, field.ty()))
@@ -692,7 +692,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
             // Note that we need special case for 0 param constructors because of multi cartesian
             // product
             let generics = ty.type_arguments().collect();
-            let fn_exprs: Vec<Expr> = if param_exprs.is_empty() {
+            let fn_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
                 vec![Expr::Function { func: it, generics, params: Vec::new() }]
             } else {
                 param_exprs
@@ -722,12 +722,12 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
 /// * `should_continue` - Function that indicates when to stop iterating
-pub(super) fn make_tuple<'a, DB: HirDatabase>(
-    ctx: &'a TermSearchCtx<'a, DB>,
+pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'db, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
-    lookup: &'a mut LookupTable,
+    lookup: &'lt mut LookupTable<'db>,
     should_continue: &'a dyn std::ops::Fn() -> bool,
-) -> impl Iterator<Item = Expr> + 'a {
+) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
     let db = ctx.sema.db;
     let module = ctx.scope.module();
 
@@ -749,15 +749,15 @@ pub(super) fn make_tuple<'a, DB: HirDatabase>(
             }
 
             // Early exit if some param cannot be filled from lookup
-            let param_exprs: Vec<Vec<Expr>> =
+            let param_exprs: Vec<Vec<Expr<'db>>> =
                 ty.type_arguments().map(|field| lookup.find(db, &field)).collect::<Option<_>>()?;
 
-            let exprs: Vec<Expr> = param_exprs
+            let exprs: Vec<Expr<'db>> = param_exprs
                 .into_iter()
                 .multi_cartesian_product()
                 .filter(|_| should_continue())
                 .map(|params| {
-                    let tys: Vec<Type> = params.iter().map(|it| it.ty(db)).collect();
+                    let tys: Vec<Type<'_>> = params.iter().map(|it| it.ty(db)).collect();
                     let tuple_ty = Type::new_tuple(module.krate().into(), &tys);
 
                     let expr = Expr::Tuple { ty: tuple_ty.clone(), params };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
index fb569f8cdae..57ced8d8534 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
@@ -22,6 +22,7 @@ pub struct AssistConfig {
     pub term_search_borrowck: bool,
     pub code_action_grouping: bool,
     pub expr_fill_default: ExprFillDefaultMode,
+    pub prefer_self_ty: bool,
 }
 
 impl AssistConfig {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
index 9eb9452a2b8..207a7548f49 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs
@@ -95,7 +95,7 @@ impl<'a> AssistContext<'a> {
         }
     }
 
-    pub(crate) fn db(&self) -> &RootDatabase {
+    pub(crate) fn db(&self) -> &'a RootDatabase {
         self.sema.db
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 6a55f39e693..9f9d21923ff 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -192,7 +192,7 @@ fn add_missing_impl_members_inner(
 fn try_gen_trait_body(
     ctx: &AssistContext<'_>,
     func: &ast::Fn,
-    trait_ref: hir::TraitRef,
+    trait_ref: hir::TraitRef<'_>,
     impl_def: &ast::Impl,
     edition: Edition,
 ) -> Option<()> {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 858d4369914..1ece7ddab10 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -1,12 +1,13 @@
 use std::iter::{self, Peekable};
 
 use either::Either;
-use hir::{Adt, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics, sym};
+use hir::{Adt, AsAssocItem, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics, sym};
 use ide_db::RootDatabase;
 use ide_db::assists::ExprFillDefaultMode;
 use ide_db::syntax_helpers::suggest_name;
 use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
 use itertools::Itertools;
+use syntax::ToSmolStr;
 use syntax::ast::edit::IndentLevel;
 use syntax::ast::edit_in_place::Indent;
 use syntax::ast::syntax_factory::SyntaxFactory;
@@ -79,12 +80,20 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
 
     let make = SyntaxFactory::with_mappings();
 
-    let module = ctx.sema.scope(expr.syntax())?.module();
+    let scope = ctx.sema.scope(expr.syntax())?;
+    let module = scope.module();
+    let self_ty = if ctx.config.prefer_self_ty {
+        scope
+            .containing_function()
+            .and_then(|function| function.as_assoc_item(ctx.db())?.implementing_ty(ctx.db()))
+    } else {
+        None
+    };
     let (mut missing_pats, is_non_exhaustive, has_hidden_variants): (
         Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>,
         bool,
         bool,
-    ) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
+    ) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr, self_ty.as_ref()) {
         let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate());
 
         let variants = enum_def.variants(ctx.db());
@@ -102,8 +111,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
             })
             .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
 
-        let option_enum = FamousDefs(&ctx.sema, module.krate()).core_option_Option().map(lift_enum);
-        let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def) == option_enum {
+        let option_enum = FamousDefs(&ctx.sema, module.krate()).core_option_Option();
+        let missing_pats: Box<dyn Iterator<Item = _>> = if matches!(enum_def, ExtendedEnum::Enum { enum_: e, .. } if Some(e) == option_enum)
+        {
             // Match `Some` variant first.
             cov_mark::hit!(option_order);
             Box::new(missing_pats.rev())
@@ -111,7 +121,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
             Box::new(missing_pats)
         };
         (missing_pats.peekable(), is_non_exhaustive, has_hidden_variants)
-    } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
+    } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr, self_ty.as_ref()) {
         let is_non_exhaustive =
             enum_defs.iter().any(|enum_def| enum_def.is_non_exhaustive(ctx.db(), module.krate()));
 
@@ -159,7 +169,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
             is_non_exhaustive,
             has_hidden_variants,
         )
-    } else if let Some((enum_def, len)) = resolve_array_of_enum_def(&ctx.sema, &expr) {
+    } else if let Some((enum_def, len)) =
+        resolve_array_of_enum_def(&ctx.sema, &expr, self_ty.as_ref())
+    {
         let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate());
         let variants = enum_def.variants(ctx.db());
 
@@ -373,23 +385,23 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
     }
 }
 
-#[derive(Eq, PartialEq, Clone, Copy)]
+#[derive(Eq, PartialEq, Clone)]
 enum ExtendedEnum {
     Bool,
-    Enum(hir::Enum),
+    Enum { enum_: hir::Enum, use_self: bool },
 }
 
 #[derive(Eq, PartialEq, Clone, Copy, Debug)]
 enum ExtendedVariant {
     True,
     False,
-    Variant(hir::Variant),
+    Variant { variant: hir::Variant, use_self: bool },
 }
 
 impl ExtendedVariant {
     fn should_be_hidden(self, db: &RootDatabase, krate: Crate) -> bool {
         match self {
-            ExtendedVariant::Variant(var) => {
+            ExtendedVariant::Variant { variant: var, .. } => {
                 var.attrs(db).has_doc_hidden() && var.module(db).krate() != krate
             }
             _ => false,
@@ -397,25 +409,35 @@ impl ExtendedVariant {
     }
 }
 
-fn lift_enum(e: hir::Enum) -> ExtendedEnum {
-    ExtendedEnum::Enum(e)
-}
-
 impl ExtendedEnum {
-    fn is_non_exhaustive(self, db: &RootDatabase, krate: Crate) -> bool {
+    fn enum_(
+        db: &RootDatabase,
+        enum_: hir::Enum,
+        enum_ty: &hir::Type<'_>,
+        self_ty: Option<&hir::Type<'_>>,
+    ) -> Self {
+        ExtendedEnum::Enum {
+            enum_,
+            use_self: self_ty.is_some_and(|self_ty| self_ty.could_unify_with_deeply(db, enum_ty)),
+        }
+    }
+
+    fn is_non_exhaustive(&self, db: &RootDatabase, krate: Crate) -> bool {
         match self {
-            ExtendedEnum::Enum(e) => {
+            ExtendedEnum::Enum { enum_: e, .. } => {
                 e.attrs(db).by_key(sym::non_exhaustive).exists() && e.module(db).krate() != krate
             }
             _ => false,
         }
     }
 
-    fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> {
-        match self {
-            ExtendedEnum::Enum(e) => {
-                e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>()
-            }
+    fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> {
+        match *self {
+            ExtendedEnum::Enum { enum_: e, use_self } => e
+                .variants(db)
+                .into_iter()
+                .map(|variant| ExtendedVariant::Variant { variant, use_self })
+                .collect::<Vec<_>>(),
             ExtendedEnum::Bool => {
                 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
             }
@@ -423,9 +445,13 @@ impl ExtendedEnum {
     }
 }
 
-fn resolve_enum_def(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
+fn resolve_enum_def(
+    sema: &Semantics<'_, RootDatabase>,
+    expr: &ast::Expr,
+    self_ty: Option<&hir::Type<'_>>,
+) -> Option<ExtendedEnum> {
     sema.type_of_expr(expr)?.adjusted().autoderef(sema.db).find_map(|ty| match ty.as_adt() {
-        Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
+        Some(Adt::Enum(e)) => Some(ExtendedEnum::enum_(sema.db, e, &ty, self_ty)),
         _ => ty.is_bool().then_some(ExtendedEnum::Bool),
     })
 }
@@ -433,6 +459,7 @@ fn resolve_enum_def(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> Opt
 fn resolve_tuple_of_enum_def(
     sema: &Semantics<'_, RootDatabase>,
     expr: &ast::Expr,
+    self_ty: Option<&hir::Type<'_>>,
 ) -> Option<Vec<ExtendedEnum>> {
     sema.type_of_expr(expr)?
         .adjusted()
@@ -441,7 +468,7 @@ fn resolve_tuple_of_enum_def(
         .map(|ty| {
             ty.autoderef(sema.db).find_map(|ty| {
                 match ty.as_adt() {
-                    Some(Adt::Enum(e)) => Some(lift_enum(e)),
+                    Some(Adt::Enum(e)) => Some(ExtendedEnum::enum_(sema.db, e, &ty, self_ty)),
                     // For now we only handle expansion for a tuple of enums. Here
                     // we map non-enum items to None and rely on `collect` to
                     // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
@@ -456,10 +483,11 @@ fn resolve_tuple_of_enum_def(
 fn resolve_array_of_enum_def(
     sema: &Semantics<'_, RootDatabase>,
     expr: &ast::Expr,
+    self_ty: Option<&hir::Type<'_>>,
 ) -> Option<(ExtendedEnum, usize)> {
     sema.type_of_expr(expr)?.adjusted().as_array(sema.db).and_then(|(ty, len)| {
         ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
-            Some(Adt::Enum(e)) => Some((lift_enum(e), len)),
+            Some(Adt::Enum(e)) => Some((ExtendedEnum::enum_(sema.db, e, &ty, self_ty), len)),
             _ => ty.is_bool().then_some((ExtendedEnum::Bool, len)),
         })
     })
@@ -474,9 +502,21 @@ fn build_pat(
 ) -> Option<ast::Pat> {
     let db = ctx.db();
     match var {
-        ExtendedVariant::Variant(var) => {
+        ExtendedVariant::Variant { variant: var, use_self } => {
             let edition = module.krate().edition(db);
-            let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition);
+            let path = if use_self {
+                make::path_from_segments(
+                    [
+                        make::path_segment(make::name_ref_self_ty()),
+                        make::path_segment(make::name_ref(
+                            &var.name(db).display(db, edition).to_smolstr(),
+                        )),
+                    ],
+                    false,
+                )
+            } else {
+                mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition)
+            };
             let fields = var.fields(db);
             let pat: ast::Pat = match var.kind(db) {
                 hir::StructKind::Tuple => {
@@ -509,8 +549,10 @@ fn build_pat(
 
 #[cfg(test)]
 mod tests {
+    use crate::AssistConfig;
     use crate::tests::{
-        check_assist, check_assist_not_applicable, check_assist_target, check_assist_unresolved,
+        TEST_CONFIG, check_assist, check_assist_not_applicable, check_assist_target,
+        check_assist_unresolved, check_assist_with_config,
     };
 
     use super::add_missing_match_arms;
@@ -2095,4 +2137,111 @@ fn f() {
 "#,
         );
     }
+
+    #[test]
+    fn prefer_self() {
+        check_assist_with_config(
+            add_missing_match_arms,
+            AssistConfig { prefer_self_ty: true, ..TEST_CONFIG },
+            r#"
+enum Foo {
+    Bar,
+    Baz,
+}
+
+impl Foo {
+    fn qux(&self) {
+        match self {
+            $0_ => {}
+        }
+    }
+}
+            "#,
+            r#"
+enum Foo {
+    Bar,
+    Baz,
+}
+
+impl Foo {
+    fn qux(&self) {
+        match self {
+            Self::Bar => ${1:todo!()},
+            Self::Baz => ${2:todo!()},$0
+        }
+    }
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn prefer_self_with_generics() {
+        check_assist_with_config(
+            add_missing_match_arms,
+            AssistConfig { prefer_self_ty: true, ..TEST_CONFIG },
+            r#"
+enum Foo<T> {
+    Bar(T),
+    Baz,
+}
+
+impl<T> Foo<T> {
+    fn qux(&self) {
+        match self {
+            $0_ => {}
+        }
+    }
+}
+            "#,
+            r#"
+enum Foo<T> {
+    Bar(T),
+    Baz,
+}
+
+impl<T> Foo<T> {
+    fn qux(&self) {
+        match self {
+            Self::Bar(${1:_}) => ${2:todo!()},
+            Self::Baz => ${3:todo!()},$0
+        }
+    }
+}
+            "#,
+        );
+        check_assist_with_config(
+            add_missing_match_arms,
+            AssistConfig { prefer_self_ty: true, ..TEST_CONFIG },
+            r#"
+enum Foo<T> {
+    Bar(T),
+    Baz,
+}
+
+impl<T> Foo<T> {
+    fn qux(v: Foo<i32>) {
+        match v {
+            $0_ => {}
+        }
+    }
+}
+            "#,
+            r#"
+enum Foo<T> {
+    Bar(T),
+    Baz,
+}
+
+impl<T> Foo<T> {
+    fn qux(v: Foo<i32>) {
+        match v {
+            Foo::Bar(${1:_}) => ${2:todo!()},
+            Foo::Baz => ${3:todo!()},$0
+        }
+    }
+}
+            "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
index f3243d369a0..bb6a10d40b7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
@@ -164,9 +164,9 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
     Some(())
 }
 
-pub(super) fn find_importable_node(
-    ctx: &AssistContext<'_>,
-) -> Option<(ImportAssets, SyntaxNode, Option<Type>)> {
+pub(super) fn find_importable_node<'a: 'db, 'db>(
+    ctx: &'a AssistContext<'db>,
+) -> Option<(ImportAssets<'db>, SyntaxNode, Option<Type<'db>>)> {
     // Deduplicate this with the `expected_type_and_name` logic for completions
     let expected = |expr_or_pat: Either<ast::Expr, ast::Pat>| match expr_or_pat {
         Either::Left(expr) => {
@@ -226,7 +226,7 @@ pub(super) fn find_importable_node(
     }
 }
 
-fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
+fn group_label(import_candidate: &ImportCandidate<'_>) -> GroupLabel {
     let name = match import_candidate {
         ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
         ImportCandidate::TraitAssocItem(candidate) => {
@@ -244,7 +244,7 @@ fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
 pub(crate) fn relevance_score(
     ctx: &AssistContext<'_>,
     import: &LocatedImport,
-    expected: Option<&Type>,
+    expected: Option<&Type<'_>>,
     current_module: Option<&Module>,
 ) -> i32 {
     let mut score = 0;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index cf45ea0a30d..00cbef1c01c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -309,23 +309,23 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu
 }
 
 #[derive(Debug)]
-struct Function {
+struct Function<'db> {
     name: ast::NameRef,
     self_param: Option<ast::SelfParam>,
-    params: Vec<Param>,
-    control_flow: ControlFlow,
-    ret_ty: RetType,
+    params: Vec<Param<'db>>,
+    control_flow: ControlFlow<'db>,
+    ret_ty: RetType<'db>,
     body: FunctionBody,
     outliving_locals: Vec<OutlivedLocal>,
     /// Whether at least one of the container's tail expr is contained in the range we're extracting.
     contains_tail_expr: bool,
-    mods: ContainerInfo,
+    mods: ContainerInfo<'db>,
 }
 
 #[derive(Debug)]
-struct Param {
+struct Param<'db> {
     var: Local,
-    ty: hir::Type,
+    ty: hir::Type<'db>,
     move_local: bool,
     requires_mut: bool,
     is_copy: bool,
@@ -340,10 +340,10 @@ enum ParamKind {
 }
 
 #[derive(Debug)]
-enum FunType {
+enum FunType<'db> {
     Unit,
-    Single(hir::Type),
-    Tuple(Vec<hir::Type>),
+    Single(hir::Type<'db>),
+    Tuple(Vec<hir::Type<'db>>),
 }
 
 /// Where to put extracted function definition
@@ -358,19 +358,19 @@ enum Anchor {
 // FIXME: ControlFlow and ContainerInfo both track some function modifiers, feels like these two should
 // probably be merged somehow.
 #[derive(Debug)]
-struct ControlFlow {
-    kind: Option<FlowKind>,
+struct ControlFlow<'db> {
+    kind: Option<FlowKind<'db>>,
     is_async: bool,
     is_unsafe: bool,
 }
 
 /// The thing whose expression we are extracting from. Can be a function, const, static, const arg, ...
 #[derive(Clone, Debug)]
-struct ContainerInfo {
+struct ContainerInfo<'db> {
     is_const: bool,
     parent_loop: Option<SyntaxNode>,
     /// The function's return type, const's type etc.
-    ret_type: Option<hir::Type>,
+    ret_type: Option<hir::Type<'db>>,
     generic_param_lists: Vec<ast::GenericParamList>,
     where_clauses: Vec<ast::WhereClause>,
     edition: Edition,
@@ -389,11 +389,11 @@ struct ContainerInfo {
 /// }
 /// ```
 #[derive(Debug, Clone)]
-enum FlowKind {
+enum FlowKind<'db> {
     /// Return with value (`return $expr;`)
     Return(Option<ast::Expr>),
     Try {
-        kind: TryKind,
+        kind: TryKind<'db>,
     },
     /// Break with label and value (`break 'label $expr;`)
     Break(Option<ast::Lifetime>, Option<ast::Expr>),
@@ -402,18 +402,18 @@ enum FlowKind {
 }
 
 #[derive(Debug, Clone)]
-enum TryKind {
+enum TryKind<'db> {
     Option,
-    Result { ty: hir::Type },
+    Result { ty: hir::Type<'db> },
 }
 
 #[derive(Debug)]
-enum RetType {
-    Expr(hir::Type),
+enum RetType<'db> {
+    Expr(hir::Type<'db>),
     Stmt,
 }
 
-impl RetType {
+impl RetType<'_> {
     fn is_unit(&self) -> bool {
         match self {
             RetType::Expr(ty) => ty.is_unit(),
@@ -456,8 +456,8 @@ impl LocalUsages {
     }
 }
 
-impl Function {
-    fn return_type(&self, ctx: &AssistContext<'_>) -> FunType {
+impl<'db> Function<'db> {
+    fn return_type(&self, ctx: &AssistContext<'db>) -> FunType<'db> {
         match &self.ret_ty {
             RetType::Expr(ty) if ty.is_unit() => FunType::Unit,
             RetType::Expr(ty) => FunType::Single(ty.clone()),
@@ -487,7 +487,7 @@ impl ParamKind {
     }
 }
 
-impl Param {
+impl<'db> Param<'db> {
     fn kind(&self) -> ParamKind {
         match (self.move_local, self.requires_mut, self.is_copy) {
             (false, true, _) => ParamKind::MutRef,
@@ -497,7 +497,7 @@ impl Param {
         }
     }
 
-    fn to_arg(&self, ctx: &AssistContext<'_>, edition: Edition) -> ast::Expr {
+    fn to_arg(&self, ctx: &AssistContext<'db>, edition: Edition) -> ast::Expr {
         let var = path_expr_from_local(ctx, self.var, edition);
         match self.kind() {
             ParamKind::Value | ParamKind::MutValue => var,
@@ -532,8 +532,12 @@ impl Param {
     }
 }
 
-impl TryKind {
-    fn of_ty(ty: hir::Type, ctx: &AssistContext<'_>, edition: Edition) -> Option<TryKind> {
+impl<'db> TryKind<'db> {
+    fn of_ty(
+        ty: hir::Type<'db>,
+        ctx: &AssistContext<'db>,
+        edition: Edition,
+    ) -> Option<TryKind<'db>> {
         if ty.is_unknown() {
             // We favour Result for `expr?`
             return Some(TryKind::Result { ty });
@@ -551,7 +555,7 @@ impl TryKind {
     }
 }
 
-impl FlowKind {
+impl<'db> FlowKind<'db> {
     fn make_result_handler(&self, expr: Option<ast::Expr>) -> ast::Expr {
         match self {
             FlowKind::Return(_) => make::expr_return(expr),
@@ -567,7 +571,7 @@ impl FlowKind {
         }
     }
 
-    fn expr_ty(&self, ctx: &AssistContext<'_>) -> Option<hir::Type> {
+    fn expr_ty(&self, ctx: &AssistContext<'db>) -> Option<hir::Type<'db>> {
         match self {
             FlowKind::Return(Some(expr)) | FlowKind::Break(_, Some(expr)) => {
                 ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted)
@@ -876,11 +880,11 @@ impl FunctionBody {
         (res, self_param)
     }
 
-    fn analyze_container(
+    fn analyze_container<'db>(
         &self,
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         edition: Edition,
-    ) -> Option<(ContainerInfo, bool)> {
+    ) -> Option<(ContainerInfo<'db>, bool)> {
         let mut ancestors = self.parent()?.ancestors();
         let infer_expr_opt = |expr| sema.type_of_expr(&expr?).map(TypeInfo::adjusted);
         let mut parent_loop = None;
@@ -985,7 +989,7 @@ impl FunctionBody {
         ))
     }
 
-    fn return_ty(&self, ctx: &AssistContext<'_>) -> Option<RetType> {
+    fn return_ty<'db>(&self, ctx: &AssistContext<'db>) -> Option<RetType<'db>> {
         match self.tail_expr() {
             Some(expr) => ctx.sema.type_of_expr(&expr).map(TypeInfo::original).map(RetType::Expr),
             None => Some(RetType::Stmt),
@@ -1006,11 +1010,11 @@ impl FunctionBody {
     }
 
     /// Analyses the function body for external control flow.
-    fn external_control_flow(
+    fn external_control_flow<'db>(
         &self,
-        ctx: &AssistContext<'_>,
-        container_info: &ContainerInfo,
-    ) -> Option<ControlFlow> {
+        ctx: &AssistContext<'db>,
+        container_info: &ContainerInfo<'db>,
+    ) -> Option<ControlFlow<'db>> {
         let mut ret_expr = None;
         let mut try_expr = None;
         let mut break_expr = None;
@@ -1096,12 +1100,12 @@ impl FunctionBody {
     /// find variables that should be extracted as params
     ///
     /// Computes additional info that affects param type and mutability
-    fn extracted_function_params(
+    fn extracted_function_params<'db>(
         &self,
-        ctx: &AssistContext<'_>,
-        container_info: &ContainerInfo,
+        ctx: &AssistContext<'db>,
+        container_info: &ContainerInfo<'db>,
         locals: FxIndexSet<Local>,
-    ) -> Vec<Param> {
+    ) -> Vec<Param<'db>> {
         locals
             .into_iter()
             .sorted()
@@ -1449,7 +1453,7 @@ fn fixup_call_site(builder: &mut SourceChangeBuilder, body: &FunctionBody) {
     }
 }
 
-fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> SyntaxNode {
+fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) -> SyntaxNode {
     let ret_ty = fun.return_type(ctx);
 
     let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx, fun.mods.edition)));
@@ -1508,17 +1512,17 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> Sy
     }
 }
 
-enum FlowHandler {
+enum FlowHandler<'db> {
     None,
-    Try { kind: TryKind },
-    If { action: FlowKind },
-    IfOption { action: FlowKind },
-    MatchOption { none: FlowKind },
-    MatchResult { err: FlowKind },
+    Try { kind: TryKind<'db> },
+    If { action: FlowKind<'db> },
+    IfOption { action: FlowKind<'db> },
+    MatchOption { none: FlowKind<'db> },
+    MatchResult { err: FlowKind<'db> },
 }
 
-impl FlowHandler {
-    fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler {
+impl<'db> FlowHandler<'db> {
+    fn from_ret_ty(fun: &Function<'db>, ret_ty: &FunType<'db>) -> FlowHandler<'db> {
         if fun.contains_tail_expr {
             return FlowHandler::None;
         }
@@ -1628,7 +1632,7 @@ fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local, edition: Edition) -
 fn format_function(
     ctx: &AssistContext<'_>,
     module: hir::Module,
-    fun: &Function,
+    fun: &Function<'_>,
     old_indent: IndentLevel,
 ) -> ast::Fn {
     let fun_name = make::name(&fun.name.text());
@@ -1654,7 +1658,7 @@ fn format_function(
 
 fn make_generic_params_and_where_clause(
     ctx: &AssistContext<'_>,
-    fun: &Function,
+    fun: &Function<'_>,
 ) -> (Option<ast::GenericParamList>, Option<ast::WhereClause>) {
     let used_type_params = fun.type_params(ctx);
 
@@ -1666,7 +1670,7 @@ fn make_generic_params_and_where_clause(
 
 fn make_generic_param_list(
     ctx: &AssistContext<'_>,
-    fun: &Function,
+    fun: &Function<'_>,
     used_type_params: &[TypeParam],
 ) -> Option<ast::GenericParamList> {
     let mut generic_params = fun
@@ -1703,7 +1707,7 @@ fn param_is_required(
 
 fn make_where_clause(
     ctx: &AssistContext<'_>,
-    fun: &Function,
+    fun: &Function<'_>,
     used_type_params: &[TypeParam],
 ) -> Option<ast::WhereClause> {
     let mut predicates = fun
@@ -1743,9 +1747,9 @@ fn resolved_type_param(ctx: &AssistContext<'_>, pred: &ast::WherePred) -> Option
     }
 }
 
-impl Function {
+impl<'db> Function<'db> {
     /// Collect all the `TypeParam`s used in the `body` and `params`.
-    fn type_params(&self, ctx: &AssistContext<'_>) -> Vec<TypeParam> {
+    fn type_params(&self, ctx: &AssistContext<'db>) -> Vec<TypeParam> {
         let type_params_in_descendant_paths =
             self.body.descendant_paths().filter_map(|it| match ctx.sema.resolve_path(&it) {
                 Some(PathResolution::TypeParam(type_param)) => Some(type_param),
@@ -1808,8 +1812,8 @@ impl Function {
     }
 }
 
-impl FunType {
-    fn make_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
+impl<'db> FunType<'db> {
+    fn make_ty(&self, ctx: &AssistContext<'db>, module: hir::Module) -> ast::Type {
         match self {
             FunType::Unit => make::ty_unit(),
             FunType::Single(ty) => make_ty(ty, ctx, module),
@@ -1831,7 +1835,11 @@ impl FunType {
     }
 }
 
-fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -> ast::BlockExpr {
+fn make_body(
+    ctx: &AssistContext<'_>,
+    old_indent: IndentLevel,
+    fun: &Function<'_>,
+) -> ast::BlockExpr {
     let ret_ty = fun.return_type(ctx);
     let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
 
@@ -2009,19 +2017,19 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr
     make::hacky_block_expr(elements, Some(tail_expr))
 }
 
-fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
+fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> String {
     ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_owned())
 }
 
-fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
+fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
     let ty_str = format_type(ty, ctx, module);
     make::ty(&ty_str)
 }
 
 fn rewrite_body_segment(
     ctx: &AssistContext<'_>,
-    params: &[Param],
-    handler: &FlowHandler,
+    params: &[Param<'_>],
+    handler: &FlowHandler<'_>,
     syntax: &SyntaxNode,
 ) -> SyntaxNode {
     let syntax = fix_param_usages(ctx, params, syntax);
@@ -2030,8 +2038,12 @@ fn rewrite_body_segment(
 }
 
 /// change all usages to account for added `&`/`&mut` for some params
-fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode {
-    let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new();
+fn fix_param_usages(
+    ctx: &AssistContext<'_>,
+    params: &[Param<'_>],
+    syntax: &SyntaxNode,
+) -> SyntaxNode {
+    let mut usages_for_param: Vec<(&Param<'_>, Vec<ast::Expr>)> = Vec::new();
 
     let tm = TreeMutator::new(syntax);
 
@@ -2085,7 +2097,7 @@ fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNo
     res
 }
 
-fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) {
+fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) {
     let mut nested_loop = None;
     let mut nested_scope = None;
     for event in syntax.preorder() {
@@ -2146,7 +2158,10 @@ fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) {
     }
 }
 
-fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> {
+fn make_rewritten_flow(
+    handler: &FlowHandler<'_>,
+    arg_expr: Option<ast::Expr>,
+) -> Option<ast::Expr> {
     let value = match handler {
         FlowHandler::None | FlowHandler::Try { .. } => return None,
         FlowHandler::If { .. } => make::expr_call(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index 30084d23d1f..78ae815dc87 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -307,7 +307,7 @@ impl FunctionBuilder {
         ctx: &AssistContext<'_>,
         call: &ast::MethodCallExpr,
         name: &ast::NameRef,
-        receiver_ty: Type,
+        receiver_ty: Type<'_>,
         target_module: Module,
         target: GeneratedFunctionTarget,
     ) -> Option<Self> {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index 6f028e58d0c..b7b8bc604a5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -283,11 +283,11 @@ impl CallInfo {
     }
 }
 
-fn get_fn_params(
-    db: &dyn HirDatabase,
+fn get_fn_params<'db>(
+    db: &'db dyn HirDatabase,
     function: hir::Function,
     param_list: &ast::ParamList,
-) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param)>> {
+) -> Option<Vec<(ast::Pat, Option<ast::Type>, hir::Param<'db>)>> {
     let mut assoc_fn_params = function.assoc_fn_params(db).into_iter();
 
     let mut params = Vec::new();
@@ -316,7 +316,7 @@ fn inline(
     function_def_file_id: EditionedFileId,
     function: hir::Function,
     fn_body: &ast::BlockExpr,
-    params: &[(ast::Pat, Option<ast::Type>, hir::Param)],
+    params: &[(ast::Pat, Option<ast::Type>, hir::Param<'_>)],
     CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo,
 ) -> ast::Expr {
     let file_id = sema.hir_file_for(fn_body.syntax());
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
index e5ed04fdc7c..b11d3792bc4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -58,7 +58,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>
 
 fn validate_type_recursively(
     ctx: &AssistContext<'_>,
-    ty_hir: Option<&hir::Type>,
+    ty_hir: Option<&hir::Type<'_>>,
     refed: bool,
     fuel: i32,
 ) -> Option<()> {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
index 42f35210b49..08170f81b28 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
@@ -105,7 +105,7 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool {
 }
 
 fn are_same_types(
-    current_arm_types: &FxHashMap<String, Option<Type>>,
+    current_arm_types: &FxHashMap<String, Option<Type<'_>>>,
     arm: &ast::MatchArm,
     ctx: &AssistContext<'_>,
 ) -> bool {
@@ -121,15 +121,15 @@ fn are_same_types(
     true
 }
 
-fn get_arm_types(
-    context: &AssistContext<'_>,
+fn get_arm_types<'db>(
+    context: &AssistContext<'db>,
     arm: &ast::MatchArm,
-) -> FxHashMap<String, Option<Type>> {
-    let mut mapping: FxHashMap<String, Option<Type>> = FxHashMap::default();
+) -> FxHashMap<String, Option<Type<'db>>> {
+    let mut mapping: FxHashMap<String, Option<Type<'db>>> = FxHashMap::default();
 
-    fn recurse(
-        map: &mut FxHashMap<String, Option<Type>>,
-        ctx: &AssistContext<'_>,
+    fn recurse<'db>(
+        map: &mut FxHashMap<String, Option<Type<'db>>>,
+        ctx: &AssistContext<'db>,
         pat: &Option<ast::Pat>,
     ) {
         if let Some(local_pat) = pat {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
index 07d2f52a34e..8834ad97652 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
@@ -217,7 +217,7 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
     }
 }
 
-fn group_label(candidate: &ImportCandidate) -> GroupLabel {
+fn group_label(candidate: &ImportCandidate<'_>) -> GroupLabel {
     let name = match candidate {
         ImportCandidate::Path(it) => &it.name,
         ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => {
@@ -230,7 +230,7 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
 
 fn label(
     db: &RootDatabase,
-    candidate: &ImportCandidate,
+    candidate: &ImportCandidate<'_>,
     import: &LocatedImport,
     edition: Edition,
 ) -> String {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
index 16debc4d728..c38bdfdccf5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -117,7 +117,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
     if unused.peek().is_some() {
         acc.add(
             AssistId::quick_fix("remove_unused_imports"),
-            "Remove all the unused imports",
+            "Remove all unused imports",
             selected_el.text_range(),
             |builder| {
                 let unused: Vec<ast::UseTree> = unused.map(|x| builder.make_mut(x)).collect();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
index 6af8e1482c2..019ddaf1441 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
@@ -46,7 +46,7 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
         return None;
     }
 
-    let mut formatter = |_: &hir::Type| String::from("todo!()");
+    let mut formatter = |_: &hir::Type<'_>| String::from("todo!()");
 
     let edition = scope.krate().edition(ctx.db());
     let paths = paths
@@ -100,7 +100,9 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!() }"#,
     fn test_complete_todo_with_msg() {
         check_assist(
             term_search,
-            r#"//- minicore: todo, unimplemented
+            // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure.
+            // Should implement super let and remove `fmt_before_1_89_0`
+            r#"//- minicore: todo, unimplemented, fmt_before_1_89_0
 fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
             r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
         )
@@ -110,7 +112,9 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
     fn test_complete_unimplemented_with_msg() {
         check_assist(
             term_search,
-            r#"//- minicore: todo, unimplemented
+            // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure.
+            // Should implement super let and remove `fmt_before_1_89_0`
+            r#"//- minicore: todo, unimplemented, fmt_before_1_89_0
 fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
             r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
         )
@@ -120,7 +124,9 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
     fn test_complete_unimplemented() {
         check_assist(
             term_search,
-            r#"//- minicore: todo, unimplemented
+            // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure.
+            // Should implement super let and remove `fmt_before_1_89_0`
+            r#"//- minicore: todo, unimplemented, fmt_before_1_89_0
 fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
             r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
         )
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
index 5e6889792db..cda2ad43278 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -37,6 +37,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
     term_search_borrowck: true,
     code_action_grouping: true,
     expr_fill_default: ExprFillDefaultMode::Todo,
+    prefer_self_ty: false,
 };
 
 pub(crate) const TEST_CONFIG_NO_GROUPING: AssistConfig = AssistConfig {
@@ -57,6 +58,7 @@ pub(crate) const TEST_CONFIG_NO_GROUPING: AssistConfig = AssistConfig {
     term_search_borrowck: true,
     code_action_grouping: false,
     expr_fill_default: ExprFillDefaultMode::Todo,
+    prefer_self_ty: false,
 };
 
 pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
@@ -77,6 +79,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
     term_search_borrowck: true,
     code_action_grouping: true,
     expr_fill_default: ExprFillDefaultMode::Todo,
+    prefer_self_ty: false,
 };
 
 pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
@@ -97,6 +100,7 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
     term_search_borrowck: true,
     code_action_grouping: true,
     expr_fill_default: ExprFillDefaultMode::Todo,
+    prefer_self_ty: false,
 };
 
 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, EditionedFileId) {
@@ -114,6 +118,23 @@ pub(crate) fn check_assist(
 }
 
 #[track_caller]
+pub(crate) fn check_assist_with_config(
+    assist: Handler,
+    config: AssistConfig,
+    #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
+    let ra_fixture_after = trim_indent(ra_fixture_after);
+    check_with_config(
+        config,
+        assist,
+        ra_fixture_before,
+        ExpectedResult::After(&ra_fixture_after),
+        None,
+    );
+}
+
+#[track_caller]
 pub(crate) fn check_assist_no_snippet_cap(
     assist: Handler,
     #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index ef6914fda1d..1a91053f93c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -405,7 +405,7 @@ pub(crate) fn does_pat_variant_nested_or_literal(ctx: &AssistContext<'_>, pat: &
 }
 
 fn check_pat_variant_from_enum(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool {
-    ctx.sema.type_of_pat(pat).is_none_or(|ty: hir::TypeInfo| {
+    ctx.sema.type_of_pat(pat).is_none_or(|ty: hir::TypeInfo<'_>| {
         ty.adjusted().as_adt().is_some_and(|adt| matches!(adt, hir::Adt::Enum(_)))
     })
 }
@@ -780,9 +780,9 @@ pub(crate) fn add_method_to_adt(
 }
 
 #[derive(Debug)]
-pub(crate) struct ReferenceConversion {
+pub(crate) struct ReferenceConversion<'db> {
     conversion: ReferenceConversionType,
-    ty: hir::Type,
+    ty: hir::Type<'db>,
     impls_deref: bool,
 }
 
@@ -802,10 +802,10 @@ enum ReferenceConversionType {
     Result,
 }
 
-impl ReferenceConversion {
+impl<'db> ReferenceConversion<'db> {
     pub(crate) fn convert_type(
         &self,
-        db: &dyn HirDatabase,
+        db: &'db dyn HirDatabase,
         display_target: DisplayTarget,
     ) -> ast::Type {
         let ty = match self.conversion {
@@ -878,11 +878,11 @@ impl ReferenceConversion {
 // FIXME: It should return a new hir::Type, but currently constructing new types is too cumbersome
 //        and all users of this function operate on string type names, so they can do the conversion
 //        itself themselves.
-pub(crate) fn convert_reference_type(
-    ty: hir::Type,
-    db: &RootDatabase,
-    famous_defs: &FamousDefs<'_, '_>,
-) -> Option<ReferenceConversion> {
+pub(crate) fn convert_reference_type<'db>(
+    ty: hir::Type<'db>,
+    db: &'db RootDatabase,
+    famous_defs: &FamousDefs<'_, 'db>,
+) -> Option<ReferenceConversion<'db>> {
     handle_copy(&ty, db)
         .or_else(|| handle_as_ref_str(&ty, db, famous_defs))
         .or_else(|| handle_as_ref_slice(&ty, db, famous_defs))
@@ -892,18 +892,21 @@ pub(crate) fn convert_reference_type(
         .map(|(conversion, impls_deref)| ReferenceConversion { ty, conversion, impls_deref })
 }
 
-fn could_deref_to_target(ty: &hir::Type, target: &hir::Type, db: &dyn HirDatabase) -> bool {
+fn could_deref_to_target(ty: &hir::Type<'_>, target: &hir::Type<'_>, db: &dyn HirDatabase) -> bool {
     let ty_ref = ty.add_reference(hir::Mutability::Shared);
     let target_ref = target.add_reference(hir::Mutability::Shared);
     ty_ref.could_coerce_to(db, &target_ref)
 }
 
-fn handle_copy(ty: &hir::Type, db: &dyn HirDatabase) -> Option<(ReferenceConversionType, bool)> {
+fn handle_copy(
+    ty: &hir::Type<'_>,
+    db: &dyn HirDatabase,
+) -> Option<(ReferenceConversionType, bool)> {
     ty.is_copy(db).then_some((ReferenceConversionType::Copy, true))
 }
 
 fn handle_as_ref_str(
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     db: &dyn HirDatabase,
     famous_defs: &FamousDefs<'_, '_>,
 ) -> Option<(ReferenceConversionType, bool)> {
@@ -914,7 +917,7 @@ fn handle_as_ref_str(
 }
 
 fn handle_as_ref_slice(
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     db: &dyn HirDatabase,
     famous_defs: &FamousDefs<'_, '_>,
 ) -> Option<(ReferenceConversionType, bool)> {
@@ -928,7 +931,7 @@ fn handle_as_ref_slice(
 }
 
 fn handle_dereferenced(
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     db: &dyn HirDatabase,
     famous_defs: &FamousDefs<'_, '_>,
 ) -> Option<(ReferenceConversionType, bool)> {
@@ -941,7 +944,7 @@ fn handle_dereferenced(
 }
 
 fn handle_option_as_ref(
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     db: &dyn HirDatabase,
     famous_defs: &FamousDefs<'_, '_>,
 ) -> Option<(ReferenceConversionType, bool)> {
@@ -953,7 +956,7 @@ fn handle_option_as_ref(
 }
 
 fn handle_result_as_ref(
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     db: &dyn HirDatabase,
     famous_defs: &FamousDefs<'_, '_>,
 ) -> Option<(ReferenceConversionType, bool)> {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
index 4ea56dc46aa..c58bdd9e8ed 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -17,7 +17,7 @@ pub(crate) fn gen_trait_fn_body(
     func: &ast::Fn,
     trait_path: &ast::Path,
     adt: &ast::Adt,
-    trait_ref: Option<TraitRef>,
+    trait_ref: Option<TraitRef<'_>>,
 ) -> Option<()> {
     match trait_path.segment()?.name_ref()?.text().as_str() {
         "Clone" => gen_clone_impl(adt, func),
@@ -405,7 +405,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
 }
 
 /// Generate a `PartialEq` impl based on the fields and members of the target type.
-fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -> Option<()> {
+fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef<'_>>) -> Option<()> {
     stdx::always!(func.name().is_some_and(|name| name.text() == "eq"));
     fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
         match expr {
@@ -599,7 +599,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -
     Some(())
 }
 
-fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -> Option<()> {
+fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef<'_>>) -> Option<()> {
     stdx::always!(func.name().is_some_and(|name| name.text() == "partial_cmp"));
     fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> {
         let mut arms = vec![];
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
index 5d68aca9e61..65072d936f6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -161,7 +161,11 @@ impl Completions {
         item.add_to(self, ctx.db);
     }
 
-    pub(crate) fn add_expr(&mut self, ctx: &CompletionContext<'_>, expr: &hir::term_search::Expr) {
+    pub(crate) fn add_expr(
+        &mut self,
+        ctx: &CompletionContext<'_>,
+        expr: &hir::term_search::Expr<'_>,
+    ) {
         if let Some(item) = render_expr(ctx, expr) {
             item.add_to(self, ctx.db)
         }
@@ -170,7 +174,7 @@ impl Completions {
     pub(crate) fn add_crate_roots(
         &mut self,
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
     ) {
         ctx.process_all_names(&mut |name, res, doc_aliases| match res {
             ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root() => {
@@ -183,7 +187,7 @@ impl Completions {
     pub(crate) fn add_path_resolution(
         &mut self,
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
         local_name: hir::Name,
         resolution: hir::ScopeDef,
         doc_aliases: Vec<syntax::SmolStr>,
@@ -232,7 +236,7 @@ impl Completions {
     pub(crate) fn add_enum_variants(
         &mut self,
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
         e: hir::Enum,
     ) {
         if !ctx.check_stability_and_hidden(e) {
@@ -246,7 +250,7 @@ impl Completions {
     pub(crate) fn add_module(
         &mut self,
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
         module: hir::Module,
         local_name: hir::Name,
         doc_aliases: Vec<syntax::SmolStr>,
@@ -263,7 +267,7 @@ impl Completions {
     pub(crate) fn add_macro(
         &mut self,
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
         mac: hir::Macro,
         local_name: hir::Name,
     ) {
@@ -286,7 +290,7 @@ impl Completions {
     pub(crate) fn add_function(
         &mut self,
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
         func: hir::Function,
         local_name: Option<hir::Name>,
     ) {
@@ -312,7 +316,7 @@ impl Completions {
     pub(crate) fn add_method(
         &mut self,
         ctx: &CompletionContext<'_>,
-        dot_access: &DotAccess,
+        dot_access: &DotAccess<'_>,
         func: hir::Function,
         receiver: Option<SmolStr>,
         local_name: Option<hir::Name>,
@@ -340,7 +344,7 @@ impl Completions {
     pub(crate) fn add_method_with_import(
         &mut self,
         ctx: &CompletionContext<'_>,
-        dot_access: &DotAccess,
+        dot_access: &DotAccess<'_>,
         func: hir::Function,
         import: LocatedImport,
     ) {
@@ -407,7 +411,7 @@ impl Completions {
     pub(crate) fn add_qualified_enum_variant(
         &mut self,
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
         variant: hir::Variant,
         path: hir::ModPath,
     ) {
@@ -424,7 +428,7 @@ impl Completions {
     pub(crate) fn add_enum_variant(
         &mut self,
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
@@ -447,10 +451,10 @@ impl Completions {
     pub(crate) fn add_field(
         &mut self,
         ctx: &CompletionContext<'_>,
-        dot_access: &DotAccess,
+        dot_access: &DotAccess<'_>,
         receiver: Option<SmolStr>,
         field: hir::Field,
-        ty: &hir::Type,
+        ty: &hir::Type<'_>,
     ) {
         let is_private_editable = match ctx.is_visible(&field) {
             Visible::Yes => false,
@@ -471,7 +475,7 @@ impl Completions {
     pub(crate) fn add_struct_literal(
         &mut self,
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
         strukt: hir::Struct,
         path: Option<hir::ModPath>,
         local_name: Option<hir::Name>,
@@ -518,7 +522,7 @@ impl Completions {
         ctx: &CompletionContext<'_>,
         receiver: Option<SmolStr>,
         field: usize,
-        ty: &hir::Type,
+        ty: &hir::Type<'_>,
     ) {
         // Only used for (unnamed) tuples, whose all fields *are* stable. No need to check
         // stability here.
@@ -550,7 +554,7 @@ impl Completions {
         &mut self,
         ctx: &CompletionContext<'_>,
         pattern_ctx: &PatternContext,
-        path_ctx: Option<&PathCompletionCtx>,
+        path_ctx: Option<&PathCompletionCtx<'_>>,
         variant: hir::Variant,
         local_name: Option<hir::Name>,
     ) {
@@ -704,7 +708,7 @@ pub(super) fn complete_name(
 pub(super) fn complete_name_ref(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    NameRefContext { nameref, kind }: &NameRefContext,
+    NameRefContext { nameref, kind }: &NameRefContext<'_>,
 ) {
     match kind {
         NameRefKind::Path(path_ctx) => {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
index 705402c785a..c542e140df5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
@@ -86,7 +86,7 @@ pub(crate) fn complete_known_attribute_input(
 pub(crate) fn complete_attribute_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
+    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
     &AttrCtx { kind, annotated_item_kind, ref derive_helpers }: &AttrCtx,
 ) {
     let is_inner = kind == AttrKind::Inner;
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
index 2fc07e01382..267d92b6c09 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
@@ -13,7 +13,7 @@ use crate::{
 pub(crate) fn complete_derive_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
+    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
     existing_derives: &ExistingDerives,
 ) {
     let core = ctx.famous_defs().core();
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index 4f21136d214..5340d65a142 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -18,7 +18,7 @@ use crate::{
 pub(crate) fn complete_dot(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    dot_access: &DotAccess,
+    dot_access: &DotAccess<'_>,
 ) {
     let receiver_ty = match dot_access {
         DotAccess { receiver_ty: Some(receiver_ty), .. } => &receiver_ty.original,
@@ -130,8 +130,8 @@ pub(crate) fn complete_dot(
 pub(crate) fn complete_undotted_self(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
-    expr_ctx: &PathExprCtx,
+    path_ctx: &PathCompletionCtx<'_>,
+    expr_ctx: &PathExprCtx<'_>,
 ) {
     if !ctx.config.enable_self_on_the_fly {
         return;
@@ -198,9 +198,9 @@ pub(crate) fn complete_undotted_self(
 fn complete_fields(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    receiver: &hir::Type,
-    mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type),
-    mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type),
+    receiver: &hir::Type<'_>,
+    mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type<'_>),
+    mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type<'_>),
     is_field_access: bool,
     is_method_access_with_parens: bool,
 ) {
@@ -230,7 +230,7 @@ fn complete_fields(
 
 fn complete_methods(
     ctx: &CompletionContext<'_>,
-    receiver: &hir::Type,
+    receiver: &hir::Type<'_>,
     traits_in_scope: &FxHashSet<hir::TraitId>,
     f: impl FnMut(hir::Function),
 ) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index 7fbd1fbc1af..2133291b1de 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -47,8 +47,8 @@ where
 pub(crate) fn complete_expr_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
-    expr_ctx: &PathExprCtx,
+    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
+    expr_ctx: &PathExprCtx<'_>,
 ) {
     let _p = tracing::info_span!("complete_expr_path").entered();
     if !ctx.qualifier_ctx.none() {
@@ -145,10 +145,16 @@ pub(crate) fn complete_expr_path(
             });
             match resolution {
                 hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
-                    // Set visible_from to None so private items are returned.
-                    // They will be possibly filtered out in add_path_resolution()
-                    // via def_is_visible().
-                    let module_scope = module.scope(ctx.db, None);
+                    let visible_from = if ctx.config.enable_private_editable {
+                        // Set visible_from to None so private items are returned.
+                        // They will be possibly filtered out in add_path_resolution()
+                        // via def_is_visible().
+                        None
+                    } else {
+                        Some(ctx.module)
+                    };
+
+                    let module_scope = module.scope(ctx.db, visible_from);
                     for (name, def) in module_scope {
                         if scope_def_applicable(def) {
                             acc.add_path_resolution(
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs
index 1441b0e3a01..26afa9c8ad9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs
@@ -8,7 +8,7 @@ use crate::{
 pub(crate) fn complete_field_list_tuple_variant(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
 ) {
     if ctx.qualifier_ctx.vis_node.is_some() {
     } else if let PathCompletionCtx {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index a7475613809..dad8a76de87 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -111,7 +111,7 @@ use crate::{
 pub(crate) fn import_on_the_fly_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
 ) -> Option<()> {
     if !ctx.config.enable_imports_on_the_fly {
         return None;
@@ -175,7 +175,7 @@ pub(crate) fn import_on_the_fly_pat(
 pub(crate) fn import_on_the_fly_dot(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    dot_access: &DotAccess,
+    dot_access: &DotAccess<'_>,
 ) -> Option<()> {
     if !ctx.config.enable_imports_on_the_fly {
         return None;
@@ -203,8 +203,8 @@ pub(crate) fn import_on_the_fly_dot(
 fn import_on_the_fly(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx,
-    import_assets: ImportAssets,
+    path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx<'_>,
+    import_assets: ImportAssets<'_>,
     position: SyntaxNode,
     potential_import_name: String,
 ) -> Option<()> {
@@ -290,7 +290,7 @@ fn import_on_the_fly_pat_(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
     pattern_ctx: &PatternContext,
-    import_assets: ImportAssets,
+    import_assets: ImportAssets<'_>,
     position: SyntaxNode,
     potential_import_name: String,
 ) -> Option<()> {
@@ -335,8 +335,8 @@ fn import_on_the_fly_pat_(
 fn import_on_the_fly_method(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    dot_access: &DotAccess,
-    import_assets: ImportAssets,
+    dot_access: &DotAccess<'_>,
+    import_assets: ImportAssets<'_>,
     position: SyntaxNode,
     potential_import_name: String,
 ) -> Option<()> {
@@ -400,11 +400,11 @@ fn import_name(ctx: &CompletionContext<'_>) -> String {
     if token_kind.is_any_identifier() { ctx.token.to_string() } else { String::new() }
 }
 
-fn import_assets_for_path(
-    ctx: &CompletionContext<'_>,
+fn import_assets_for_path<'db>(
+    ctx: &CompletionContext<'db>,
     potential_import_name: &str,
     qualifier: Option<ast::Path>,
-) -> Option<ImportAssets> {
+) -> Option<ImportAssets<'db>> {
     let _p =
         tracing::info_span!("import_assets_for_path", ?potential_import_name, ?qualifier).entered();
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
index 893997cee47..6c001bd16bf 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
@@ -10,8 +10,8 @@ pub(crate) mod trait_impl;
 pub(crate) fn complete_item_list_in_expr(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
-    expr_ctx: &PathExprCtx,
+    path_ctx: &PathCompletionCtx<'_>,
+    expr_ctx: &PathExprCtx<'_>,
 ) {
     if !expr_ctx.in_block_expr {
         return;
@@ -25,7 +25,7 @@ pub(crate) fn complete_item_list_in_expr(
 pub(crate) fn complete_item_list(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
+    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
     kind: &ItemListKind,
 ) {
     let _p = tracing::info_span!("complete_item_list").entered();
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 58aead73fd6..092219a058a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -122,7 +122,7 @@ fn complete_trait_impl_name(
 pub(crate) fn complete_trait_impl_item_by_name(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     name_ref: &Option<ast::NameRef>,
     impl_: &Option<ast::Impl>,
 ) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
index ea3511d31ca..62fae1cb237 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
@@ -124,7 +124,7 @@ pub(crate) fn complete_pattern(
 pub(crate) fn complete_pattern_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
+    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
 ) {
     match qualified {
         Qualified::With { resolution: Some(resolution), super_chain_len, .. } => {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 3cdf2112835..d0023852acf 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -11,6 +11,7 @@ use ide_db::{
     text_edit::TextEdit,
     ty_filter::TryEnum,
 };
+use itertools::Either;
 use stdx::never;
 use syntax::{
     SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
@@ -28,7 +29,7 @@ use crate::{
 pub(crate) fn complete_postfix(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    dot_access: &DotAccess,
+    dot_access: &DotAccess<'_>,
 ) {
     if !ctx.config.enable_postfix_completions {
         return;
@@ -86,98 +87,10 @@ pub(crate) fn complete_postfix(
         }
     }
 
-    let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references());
-    if let Some(try_enum) = &try_enum {
-        match try_enum {
-            TryEnum::Result => {
-                postfix_snippet(
-                    "ifl",
-                    "if let Ok {}",
-                    &format!("if let Ok($1) = {receiver_text} {{\n    $0\n}}"),
-                )
-                .add_to(acc, ctx.db);
-
-                postfix_snippet(
-                    "lete",
-                    "let Ok else {}",
-                    &format!("let Ok($1) = {receiver_text} else {{\n    $2\n}};\n$0"),
-                )
-                .add_to(acc, ctx.db);
-
-                postfix_snippet(
-                    "while",
-                    "while let Ok {}",
-                    &format!("while let Ok($1) = {receiver_text} {{\n    $0\n}}"),
-                )
-                .add_to(acc, ctx.db);
-            }
-            TryEnum::Option => {
-                postfix_snippet(
-                    "ifl",
-                    "if let Some {}",
-                    &format!("if let Some($1) = {receiver_text} {{\n    $0\n}}"),
-                )
-                .add_to(acc, ctx.db);
-
-                postfix_snippet(
-                    "lete",
-                    "let Some else {}",
-                    &format!("let Some($1) = {receiver_text} else {{\n    $2\n}};\n$0"),
-                )
-                .add_to(acc, ctx.db);
-
-                postfix_snippet(
-                    "while",
-                    "while let Some {}",
-                    &format!("while let Some($1) = {receiver_text} {{\n    $0\n}}"),
-                )
-                .add_to(acc, ctx.db);
-            }
-        }
-    } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
-        postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n    $0\n}}"))
-            .add_to(acc, ctx.db);
-        postfix_snippet("while", "while expr {}", &format!("while {receiver_text} {{\n    $0\n}}"))
-            .add_to(acc, ctx.db);
-        postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db);
-    } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() {
-        if receiver_ty.impls_trait(ctx.db, trait_, &[]) {
-            postfix_snippet(
-                "for",
-                "for ele in expr {}",
-                &format!("for ele in {receiver_text} {{\n    $0\n}}"),
-            )
-            .add_to(acc, ctx.db);
-        }
-    }
-
     postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db);
     postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db);
     postfix_snippet("deref", "*expr", &format!("*{receiver_text}")).add_to(acc, ctx.db);
 
-    let mut block_should_be_wrapped = true;
-    if dot_receiver.syntax().kind() == BLOCK_EXPR {
-        block_should_be_wrapped = false;
-        if let Some(parent) = dot_receiver.syntax().parent() {
-            if matches!(parent.kind(), IF_EXPR | WHILE_EXPR | LOOP_EXPR | FOR_EXPR) {
-                block_should_be_wrapped = true;
-            }
-        }
-    };
-    let unsafe_completion_string = if block_should_be_wrapped {
-        format!("unsafe {{ {receiver_text} }}")
-    } else {
-        format!("unsafe {receiver_text}")
-    };
-    postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db);
-
-    let const_completion_string = if block_should_be_wrapped {
-        format!("const {{ {receiver_text} }}")
-    } else {
-        format!("const {receiver_text}")
-    };
-    postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db);
-
     // The rest of the postfix completions create an expression that moves an argument,
     // so it's better to consider references now to avoid breaking the compilation
 
@@ -195,18 +108,81 @@ pub(crate) fn complete_postfix(
         add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
     }
 
-    match try_enum {
-        Some(try_enum) => match try_enum {
-            TryEnum::Result => {
-                postfix_snippet(
+    postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})"))
+        .add_to(acc, ctx.db);
+    postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme
+    postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db);
+    postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})"))
+        .add_to(acc, ctx.db);
+
+    let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references());
+    let mut is_in_cond = false;
+    if let Some(parent) = dot_receiver_including_refs.syntax().parent() {
+        if let Some(second_ancestor) = parent.parent() {
+            let sec_ancestor_kind = second_ancestor.kind();
+            if let Some(expr) = <Either<ast::IfExpr, ast::WhileExpr>>::cast(second_ancestor) {
+                is_in_cond = match expr {
+                    Either::Left(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent),
+                    Either::Right(it) => {
+                        it.condition().is_some_and(|cond| *cond.syntax() == parent)
+                    }
+                }
+            }
+            match &try_enum {
+                Some(try_enum) if is_in_cond => match try_enum {
+                    TryEnum::Result => {
+                        postfix_snippet(
+                            "let",
+                            "let Ok(_)",
+                            &format!("let Ok($0) = {receiver_text}"),
+                        )
+                        .add_to(acc, ctx.db);
+                        postfix_snippet(
+                            "letm",
+                            "let Ok(mut _)",
+                            &format!("let Ok(mut $0) = {receiver_text}"),
+                        )
+                        .add_to(acc, ctx.db);
+                    }
+                    TryEnum::Option => {
+                        postfix_snippet(
+                            "let",
+                            "let Some(_)",
+                            &format!("let Some($0) = {receiver_text}"),
+                        )
+                        .add_to(acc, ctx.db);
+                        postfix_snippet(
+                            "letm",
+                            "let Some(mut _)",
+                            &format!("let Some(mut $0) = {receiver_text}"),
+                        )
+                        .add_to(acc, ctx.db);
+                    }
+                },
+                _ if matches!(sec_ancestor_kind, STMT_LIST | EXPR_STMT) => {
+                    postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
+                        .add_to(acc, ctx.db);
+                    postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};"))
+                        .add_to(acc, ctx.db);
+                }
+                _ => (),
+            }
+        }
+    }
+
+    if !is_in_cond {
+        match try_enum {
+            Some(try_enum) => match try_enum {
+                TryEnum::Result => {
+                    postfix_snippet(
                     "match",
                     "match expr {}",
                     &format!("match {receiver_text} {{\n    Ok(${{1:_}}) => {{$2}},\n    Err(${{3:_}}) => {{$0}},\n}}"),
                 )
                 .add_to(acc, ctx.db);
-            }
-            TryEnum::Option => {
-                postfix_snippet(
+                }
+                TryEnum::Option => {
+                    postfix_snippet(
                     "match",
                     "match expr {}",
                     &format!(
@@ -214,32 +190,106 @@ pub(crate) fn complete_postfix(
                     ),
                 )
                 .add_to(acc, ctx.db);
+                }
+            },
+            None => {
+                postfix_snippet(
+                    "match",
+                    "match expr {}",
+                    &format!("match {receiver_text} {{\n    ${{1:_}} => {{$0}},\n}}"),
+                )
+                .add_to(acc, ctx.db);
             }
-        },
-        None => {
+        }
+        if let Some(try_enum) = &try_enum {
+            match try_enum {
+                TryEnum::Result => {
+                    postfix_snippet(
+                        "ifl",
+                        "if let Ok {}",
+                        &format!("if let Ok($1) = {receiver_text} {{\n    $0\n}}"),
+                    )
+                    .add_to(acc, ctx.db);
+
+                    postfix_snippet(
+                        "lete",
+                        "let Ok else {}",
+                        &format!("let Ok($1) = {receiver_text} else {{\n    $2\n}};\n$0"),
+                    )
+                    .add_to(acc, ctx.db);
+
+                    postfix_snippet(
+                        "while",
+                        "while let Ok {}",
+                        &format!("while let Ok($1) = {receiver_text} {{\n    $0\n}}"),
+                    )
+                    .add_to(acc, ctx.db);
+                }
+                TryEnum::Option => {
+                    postfix_snippet(
+                        "ifl",
+                        "if let Some {}",
+                        &format!("if let Some($1) = {receiver_text} {{\n    $0\n}}"),
+                    )
+                    .add_to(acc, ctx.db);
+
+                    postfix_snippet(
+                        "lete",
+                        "let Some else {}",
+                        &format!("let Some($1) = {receiver_text} else {{\n    $2\n}};\n$0"),
+                    )
+                    .add_to(acc, ctx.db);
+
+                    postfix_snippet(
+                        "while",
+                        "while let Some {}",
+                        &format!("while let Some($1) = {receiver_text} {{\n    $0\n}}"),
+                    )
+                    .add_to(acc, ctx.db);
+                }
+            }
+        } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
+            postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n    $0\n}}"))
+                .add_to(acc, ctx.db);
             postfix_snippet(
-                "match",
-                "match expr {}",
-                &format!("match {receiver_text} {{\n    ${{1:_}} => {{$0}},\n}}"),
+                "while",
+                "while expr {}",
+                &format!("while {receiver_text} {{\n    $0\n}}"),
             )
             .add_to(acc, ctx.db);
+            postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db);
+        } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() {
+            if receiver_ty.impls_trait(ctx.db, trait_, &[]) {
+                postfix_snippet(
+                    "for",
+                    "for ele in expr {}",
+                    &format!("for ele in {receiver_text} {{\n    $0\n}}"),
+                )
+                .add_to(acc, ctx.db);
+            }
         }
     }
 
-    postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})"))
-        .add_to(acc, ctx.db);
-    postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme
-    postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db);
-    postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})"))
-        .add_to(acc, ctx.db);
-
-    if let Some(parent) = dot_receiver_including_refs.syntax().parent().and_then(|p| p.parent()) {
-        if matches!(parent.kind(), STMT_LIST | EXPR_STMT) {
-            postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
-                .add_to(acc, ctx.db);
-            postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};"))
-                .add_to(acc, ctx.db);
+    let mut block_should_be_wrapped = true;
+    if dot_receiver.syntax().kind() == BLOCK_EXPR {
+        block_should_be_wrapped = false;
+        if let Some(parent) = dot_receiver.syntax().parent() {
+            if matches!(parent.kind(), IF_EXPR | WHILE_EXPR | LOOP_EXPR | FOR_EXPR) {
+                block_should_be_wrapped = true;
+            }
         }
+    };
+    {
+        let (open_brace, close_brace) =
+            if block_should_be_wrapped { ("{ ", " }") } else { ("", "") };
+        let (open_paren, close_paren) = if is_in_cond { ("(", ")") } else { ("", "") };
+        let unsafe_completion_string =
+            format!("{open_paren}unsafe {open_brace}{receiver_text}{close_brace}{close_paren}");
+        postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db);
+
+        let const_completion_string =
+            format!("{open_paren}const {open_brace}{receiver_text}{close_brace}{close_paren}");
+        postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db);
     }
 
     if let ast::Expr::Literal(literal) = dot_receiver_including_refs.clone() {
@@ -568,6 +618,54 @@ fn main() {
     }
 
     #[test]
+    fn option_iflet_cond() {
+        check(
+            r#"
+//- minicore: option
+fn main() {
+    let bar = Some(true);
+    if bar.$0
+}
+"#,
+            expect![[r#"
+                me and(…)    fn(self, Option<U>) -> Option<U>
+                me as_ref()     const fn(&self) -> Option<&T>
+                me ok_or(…) const fn(self, E) -> Result<T, E>
+                me unwrap()               const fn(self) -> T
+                me unwrap_or(…)              fn(self, T) -> T
+                sn box                         Box::new(expr)
+                sn call                        function(expr)
+                sn const                             const {}
+                sn dbg                             dbg!(expr)
+                sn dbgr                           dbg!(&expr)
+                sn deref                                *expr
+                sn let                            let Some(_)
+                sn letm                       let Some(mut _)
+                sn ref                                  &expr
+                sn refm                             &mut expr
+                sn return                         return expr
+                sn unsafe                           unsafe {}
+            "#]],
+        );
+        check_edit(
+            "let",
+            r#"
+//- minicore: option
+fn main() {
+    let bar = Some(true);
+    if bar.$0
+}
+"#,
+            r#"
+fn main() {
+    let bar = Some(true);
+    if let Some($0) = bar
+}
+"#,
+        );
+    }
+
+    #[test]
     fn option_letelse() {
         check_edit(
             "lete",
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
index c18aab007b2..36f38a70db6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
@@ -88,7 +88,7 @@ pub(crate) fn complete_record_expr_fields(
 pub(crate) fn add_default_update(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    ty: Option<hir::TypeInfo>,
+    ty: Option<hir::TypeInfo<'_>>,
 ) {
     let default_trait = ctx.famous_defs().core_default_Default();
     let impls_default_trait = default_trait
@@ -117,7 +117,7 @@ pub(crate) fn add_default_update(
 fn complete_fields(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    missing_fields: Vec<(hir::Field, hir::Type)>,
+    missing_fields: Vec<(hir::Field, hir::Type<'_>)>,
 ) {
     for (field, ty) in missing_fields {
         // This should call something else, we shouldn't be synthesizing a DotAccess here
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
index 31aae116762..ead9852eff5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
@@ -11,8 +11,8 @@ use crate::{
 pub(crate) fn complete_expr_snippet(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
-    &PathExprCtx { in_block_expr, .. }: &PathExprCtx,
+    path_ctx: &PathCompletionCtx<'_>,
+    &PathExprCtx { in_block_expr, .. }: &PathExprCtx<'_>,
 ) {
     if !matches!(path_ctx.qualified, Qualified::No) {
         return;
@@ -51,7 +51,7 @@ macro_rules! $1 {
 pub(crate) fn complete_item_snippet(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     kind: &ItemListKind,
 ) {
     if !matches!(path_ctx.qualified, Qualified::No) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
index 79db705af49..7c38c7d8ce4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
@@ -12,7 +12,7 @@ use crate::{
 pub(crate) fn complete_type_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
+    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
     location: &TypeLocation,
 ) {
     let _p = tracing::info_span!("complete_type_path").entered();
@@ -220,7 +220,7 @@ pub(crate) fn complete_type_path(
 pub(crate) fn complete_ascribed_type(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     ascription: &TypeAscriptionTarget,
 ) -> Option<()> {
     if !path_ctx.is_trivial_path() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
index 4d6d0b758a3..d2ab193ec3d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
@@ -13,7 +13,7 @@ use crate::{
 pub(crate) fn complete_use_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx,
+    path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx<'_>,
     name_ref: &Option<ast::NameRef>,
 ) {
     match qualified {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
index d15c35ac849..38761f77a2c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
@@ -8,7 +8,7 @@ use crate::{
 pub(crate) fn complete_vis_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
+    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
     &has_in_token: &bool,
 ) {
     match qualified {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 5287627790a..cfd7f80d40b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -65,13 +65,13 @@ impl QualifierCtx {
 
 /// The state of the path we are currently completing.
 #[derive(Debug)]
-pub(crate) struct PathCompletionCtx {
+pub(crate) struct PathCompletionCtx<'db> {
     /// If this is a call with () already there (or {} in case of record patterns)
     pub(crate) has_call_parens: bool,
     /// If this has a macro call bang !
     pub(crate) has_macro_bang: bool,
     /// The qualifier of the current path.
-    pub(crate) qualified: Qualified,
+    pub(crate) qualified: Qualified<'db>,
     /// The parent of the path we are completing.
     pub(crate) parent: Option<ast::Path>,
     #[allow(dead_code)]
@@ -79,14 +79,14 @@ pub(crate) struct PathCompletionCtx {
     pub(crate) path: ast::Path,
     /// The path of which we are completing the segment in the original file
     pub(crate) original_path: Option<ast::Path>,
-    pub(crate) kind: PathKind,
+    pub(crate) kind: PathKind<'db>,
     /// Whether the path segment has type args or not.
     pub(crate) has_type_args: bool,
     /// Whether the qualifier comes from a use tree parent or not
     pub(crate) use_tree_parent: bool,
 }
 
-impl PathCompletionCtx {
+impl PathCompletionCtx<'_> {
     pub(crate) fn is_trivial_path(&self) -> bool {
         matches!(
             self,
@@ -104,9 +104,9 @@ impl PathCompletionCtx {
 
 /// The kind of path we are completing right now.
 #[derive(Debug, PartialEq, Eq)]
-pub(crate) enum PathKind {
+pub(crate) enum PathKind<'db> {
     Expr {
-        expr_ctx: PathExprCtx,
+        expr_ctx: PathExprCtx<'db>,
     },
     Type {
         location: TypeLocation,
@@ -140,7 +140,7 @@ pub(crate) struct AttrCtx {
 }
 
 #[derive(Debug, PartialEq, Eq)]
-pub(crate) struct PathExprCtx {
+pub(crate) struct PathExprCtx<'db> {
     pub(crate) in_block_expr: bool,
     pub(crate) in_breakable: BreakableKind,
     pub(crate) after_if_expr: bool,
@@ -152,7 +152,7 @@ pub(crate) struct PathExprCtx {
     /// The surrounding RecordExpression we are completing a functional update
     pub(crate) is_func_update: Option<ast::RecordExpr>,
     pub(crate) self_param: Option<hir::SelfParam>,
-    pub(crate) innermost_ret_ty: Option<hir::Type>,
+    pub(crate) innermost_ret_ty: Option<hir::Type<'db>>,
     pub(crate) impl_: Option<ast::Impl>,
     /// Whether this expression occurs in match arm guard position: before the
     /// fat arrow token
@@ -241,7 +241,7 @@ pub(crate) enum ItemListKind {
 }
 
 #[derive(Debug)]
-pub(crate) enum Qualified {
+pub(crate) enum Qualified<'db> {
     No,
     With {
         path: ast::Path,
@@ -260,7 +260,7 @@ pub(crate) enum Qualified {
     },
     /// <_>::
     TypeAnchor {
-        ty: Option<hir::Type>,
+        ty: Option<hir::Type<'db>>,
         trait_: Option<hir::Trait>,
     },
     /// Whether the path is an absolute path
@@ -341,17 +341,17 @@ pub(crate) enum NameKind {
 
 /// The state of the NameRef we are completing.
 #[derive(Debug)]
-pub(crate) struct NameRefContext {
+pub(crate) struct NameRefContext<'db> {
     /// NameRef syntax in the original file
     pub(crate) nameref: Option<ast::NameRef>,
-    pub(crate) kind: NameRefKind,
+    pub(crate) kind: NameRefKind<'db>,
 }
 
 /// The kind of the NameRef we are completing.
 #[derive(Debug)]
-pub(crate) enum NameRefKind {
-    Path(PathCompletionCtx),
-    DotAccess(DotAccess),
+pub(crate) enum NameRefKind<'db> {
+    Path(PathCompletionCtx<'db>),
+    DotAccess(DotAccess<'db>),
     /// Position where we are only interested in keyword completions
     Keyword(ast::Item),
     /// The record expression this nameref is a field of and whether a dot precedes the completion identifier.
@@ -365,9 +365,9 @@ pub(crate) enum NameRefKind {
 
 /// The identifier we are currently completing.
 #[derive(Debug)]
-pub(crate) enum CompletionAnalysis {
+pub(crate) enum CompletionAnalysis<'db> {
     Name(NameContext),
-    NameRef(NameRefContext),
+    NameRef(NameRefContext<'db>),
     Lifetime(LifetimeContext),
     /// The string the cursor is currently inside
     String {
@@ -386,9 +386,9 @@ pub(crate) enum CompletionAnalysis {
 
 /// Information about the field or method access we are completing.
 #[derive(Debug)]
-pub(crate) struct DotAccess {
+pub(crate) struct DotAccess<'db> {
     pub(crate) receiver: Option<ast::Expr>,
-    pub(crate) receiver_ty: Option<TypeInfo>,
+    pub(crate) receiver_ty: Option<TypeInfo<'db>>,
     pub(crate) kind: DotAccessKind,
     pub(crate) ctx: DotAccessExprCtx,
 }
@@ -457,7 +457,7 @@ pub(crate) struct CompletionContext<'a> {
     /// This is usually the parameter name of the function argument we are completing.
     pub(crate) expected_name: Option<NameOrNameRef>,
     /// The expected type of what we are completing.
-    pub(crate) expected_type: Option<Type>,
+    pub(crate) expected_type: Option<Type<'a>>,
 
     pub(crate) qualifier_ctx: QualifierCtx,
 
@@ -608,7 +608,7 @@ impl CompletionContext<'_> {
 
     pub(crate) fn iterate_path_candidates(
         &self,
-        ty: &hir::Type,
+        ty: &hir::Type<'_>,
         mut cb: impl FnMut(hir::AssocItem),
     ) {
         let mut seen = FxHashSet::default();
@@ -695,12 +695,12 @@ impl CompletionContext<'_> {
 }
 
 // CompletionContext construction
-impl<'a> CompletionContext<'a> {
+impl<'db> CompletionContext<'db> {
     pub(crate) fn new(
-        db: &'a RootDatabase,
+        db: &'db RootDatabase,
         position @ FilePosition { file_id, offset }: FilePosition,
-        config: &'a CompletionConfig<'a>,
-    ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> {
+        config: &'db CompletionConfig<'db>,
+    ) -> Option<(CompletionContext<'db>, CompletionAnalysis<'db>)> {
         let _p = tracing::info_span!("CompletionContext::new").entered();
         let sema = Semantics::new(db);
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 7a2230b3e36..6e3a76f346a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -38,9 +38,9 @@ struct ExpansionResult {
     derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
 }
 
-pub(super) struct AnalysisResult {
-    pub(super) analysis: CompletionAnalysis,
-    pub(super) expected: (Option<Type>, Option<ast::NameOrNameRef>),
+pub(super) struct AnalysisResult<'db> {
+    pub(super) analysis: CompletionAnalysis<'db>,
+    pub(super) expected: (Option<Type<'db>>, Option<ast::NameOrNameRef>),
     pub(super) qualifier_ctx: QualifierCtx,
     /// the original token of the expanded file
     pub(super) token: SyntaxToken,
@@ -48,13 +48,13 @@ pub(super) struct AnalysisResult {
     pub(super) original_offset: TextSize,
 }
 
-pub(super) fn expand_and_analyze(
-    sema: &Semantics<'_, RootDatabase>,
+pub(super) fn expand_and_analyze<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     original_file: InFile<SyntaxNode>,
     speculative_file: SyntaxNode,
     offset: TextSize,
     original_token: &SyntaxToken,
-) -> Option<AnalysisResult> {
+) -> Option<AnalysisResult<'db>> {
     // as we insert after the offset, right biased will *always* pick the identifier no matter
     // if there is an ident already typed or not
     let fake_ident_token = speculative_file.token_at_offset(offset).right_biased()?;
@@ -432,12 +432,13 @@ fn expand(
 
 /// Fill the completion context, this is what does semantic reasoning about the surrounding context
 /// of the completion location.
-fn analyze(
-    sema: &Semantics<'_, RootDatabase>,
+fn analyze<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     expansion_result: ExpansionResult,
     original_token: &SyntaxToken,
     self_token: &SyntaxToken,
-) -> Option<(CompletionAnalysis, (Option<Type>, Option<ast::NameOrNameRef>), QualifierCtx)> {
+) -> Option<(CompletionAnalysis<'db>, (Option<Type<'db>>, Option<ast::NameOrNameRef>), QualifierCtx)>
+{
     let _p = tracing::info_span!("CompletionContext::analyze").entered();
     let ExpansionResult {
         original_file,
@@ -555,17 +556,17 @@ fn analyze(
 }
 
 /// Calculate the expected type and name of the cursor position.
-fn expected_type_and_name(
-    sema: &Semantics<'_, RootDatabase>,
+fn expected_type_and_name<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     token: &SyntaxToken,
     name_like: &ast::NameLike,
-) -> (Option<Type>, Option<NameOrNameRef>) {
+) -> (Option<Type<'db>>, Option<NameOrNameRef>) {
     let mut node = match token.parent() {
         Some(it) => it,
         None => return (None, None),
     };
 
-    let strip_refs = |mut ty: Type| match name_like {
+    let strip_refs = |mut ty: Type<'db>| match name_like {
         ast::NameLike::NameRef(n) => {
             let p = match n.syntax().parent() {
                 Some(it) => it,
@@ -805,13 +806,13 @@ fn classify_name(
     Some(NameContext { name, kind })
 }
 
-fn classify_name_ref(
-    sema: &Semantics<'_, RootDatabase>,
+fn classify_name_ref<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     original_file: &SyntaxNode,
     name_ref: ast::NameRef,
     original_offset: TextSize,
     parent: SyntaxNode,
-) -> Option<(NameRefContext, QualifierCtx)> {
+) -> Option<(NameRefContext<'db>, QualifierCtx)> {
     let nameref = find_node_at_offset(original_file, original_offset);
 
     let make_res = |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index 19cdef30bd9..dcaac3997b2 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -502,7 +502,7 @@ pub(crate) struct Builder {
 impl Builder {
     pub(crate) fn from_resolution(
         ctx: &CompletionContext<'_>,
-        path_ctx: &PathCompletionCtx,
+        path_ctx: &PathCompletionCtx<'_>,
         local_name: hir::Name,
         resolution: hir::ScopeDef,
     ) -> Self {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index 00c0b470f98..c6b8af3c79a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -122,10 +122,10 @@ impl<'a> RenderContext<'a> {
 
 pub(crate) fn render_field(
     ctx: RenderContext<'_>,
-    dot_access: &DotAccess,
+    dot_access: &DotAccess<'_>,
     receiver: Option<SmolStr>,
     field: hir::Field,
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
 ) -> CompletionItem {
     let db = ctx.db();
     let is_deprecated = ctx.is_deprecated(field);
@@ -204,7 +204,7 @@ pub(crate) fn render_tuple_field(
     ctx: RenderContext<'_>,
     receiver: Option<SmolStr>,
     field: usize,
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
 ) -> CompletionItem {
     let mut item = CompletionItem::new(
         SymbolKind::Field,
@@ -241,7 +241,7 @@ pub(crate) fn render_type_inference(
 
 pub(crate) fn render_path_resolution(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     local_name: hir::Name,
     resolution: ScopeDef,
 ) -> Builder {
@@ -259,7 +259,7 @@ pub(crate) fn render_pattern_resolution(
 
 pub(crate) fn render_resolution_with_import(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     import_edit: LocatedImport,
 ) -> Option<Builder> {
     let resolution = ScopeDef::from(import_edit.original_item);
@@ -282,10 +282,10 @@ pub(crate) fn render_resolution_with_import_pat(
 
 pub(crate) fn render_expr(
     ctx: &CompletionContext<'_>,
-    expr: &hir::term_search::Expr,
+    expr: &hir::term_search::Expr<'_>,
 ) -> Option<Builder> {
     let mut i = 1;
-    let mut snippet_formatter = |ty: &hir::Type| {
+    let mut snippet_formatter = |ty: &hir::Type<'_>| {
         let arg_name = ty
             .as_adt()
             .map(|adt| stdx::to_lower_snake_case(adt.name(ctx.db).as_str()))
@@ -295,7 +295,7 @@ pub(crate) fn render_expr(
         res
     };
 
-    let mut label_formatter = |ty: &hir::Type| {
+    let mut label_formatter = |ty: &hir::Type<'_>| {
         ty.as_adt()
             .map(|adt| stdx::to_lower_snake_case(adt.name(ctx.db).as_str()))
             .unwrap_or_else(|| String::from("..."))
@@ -391,7 +391,7 @@ fn render_resolution_pat(
 
 fn render_resolution_path(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     local_name: hir::Name,
     import_to_add: Option<LocatedImport>,
     resolution: ScopeDef,
@@ -460,7 +460,7 @@ fn render_resolution_path(
         }
     }
 
-    let mut set_item_relevance = |ty: Type| {
+    let mut set_item_relevance = |ty: Type<'_>| {
         if !ty.is_unknown() {
             item.detail(ty.display(db, krate).to_string());
         }
@@ -593,8 +593,8 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo
 // FIXME: This checks types without possible coercions which some completions might want to do
 fn match_types(
     ctx: &CompletionContext<'_>,
-    ty1: &hir::Type,
-    ty2: &hir::Type,
+    ty1: &hir::Type<'_>,
+    ty2: &hir::Type<'_>,
 ) -> Option<CompletionRelevanceTypeMatch> {
     if ty1 == ty2 {
         Some(CompletionRelevanceTypeMatch::Exact)
@@ -607,7 +607,7 @@ fn match_types(
 
 fn compute_type_match(
     ctx: &CompletionContext<'_>,
-    completion_ty: &hir::Type,
+    completion_ty: &hir::Type<'_>,
 ) -> Option<CompletionRelevanceTypeMatch> {
     let expected_type = ctx.expected_type.as_ref()?;
 
@@ -626,7 +626,7 @@ fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str)
 
 fn compute_ref_match(
     ctx: &CompletionContext<'_>,
-    completion_ty: &hir::Type,
+    completion_ty: &hir::Type<'_>,
 ) -> Option<CompletionItemRefMode> {
     let expected_type = ctx.expected_type.as_ref()?;
     let expected_without_ref = expected_type.remove_ref();
@@ -658,8 +658,8 @@ fn compute_ref_match(
 
 fn path_ref_match(
     completion: &CompletionContext<'_>,
-    path_ctx: &PathCompletionCtx,
-    ty: &hir::Type,
+    path_ctx: &PathCompletionCtx<'_>,
+    ty: &hir::Type<'_>,
     item: &mut Builder,
 ) {
     if let Some(original_path) = &path_ctx.original_path {
@@ -733,7 +733,7 @@ mod tests {
     ) {
         let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
         actual.retain(|it| kinds.contains(&it.kind));
-        actual.sort_by_key(|it| cmp::Reverse(it.relevance.score()));
+        actual.sort_by_key(|it| (cmp::Reverse(it.relevance.score()), it.label.primary.clone()));
         check_relevance_(actual, expect);
     }
 
@@ -743,7 +743,7 @@ mod tests {
         actual.retain(|it| it.kind != CompletionItemKind::Snippet);
         actual.retain(|it| it.kind != CompletionItemKind::Keyword);
         actual.retain(|it| it.kind != CompletionItemKind::BuiltinType);
-        actual.sort_by_key(|it| cmp::Reverse(it.relevance.score()));
+        actual.sort_by_key(|it| (cmp::Reverse(it.relevance.score()), it.label.primary.clone()));
         check_relevance_(actual, expect);
     }
 
@@ -824,9 +824,9 @@ fn main() {
                 st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct {  } [type_could_unify]
                 ex dep::test_mod_b::Struct {  }  [type_could_unify]
                 st Struct Struct [type_could_unify+requires_import]
+                md dep  []
                 fn main() fn() []
                 fn test(…) fn(Struct) []
-                md dep  []
                 st Struct Struct [requires_import]
             "#]],
         );
@@ -862,9 +862,9 @@ fn main() {
 "#,
             expect![[r#"
                 un Union Union [type_could_unify+requires_import]
+                md dep  []
                 fn main() fn() []
                 fn test(…) fn(Union) []
-                md dep  []
                 en Union Union [requires_import]
             "#]],
         );
@@ -900,9 +900,9 @@ fn main() {
                 ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify]
                 ex dep::test_mod_b::Enum::variant  [type_could_unify]
                 en Enum Enum [type_could_unify+requires_import]
+                md dep  []
                 fn main() fn() []
                 fn test(…) fn(Enum) []
-                md dep  []
                 en Enum Enum [requires_import]
             "#]],
         );
@@ -937,9 +937,9 @@ fn main() {
             expect![[r#"
                 ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify]
                 ex dep::test_mod_b::Enum::Variant  [type_could_unify]
+                md dep  []
                 fn main() fn() []
                 fn test(…) fn(Enum) []
-                md dep  []
             "#]],
         );
     }
@@ -967,9 +967,9 @@ fn main() {
 }
 "#,
             expect![[r#"
+                md dep  []
                 fn main() fn() []
                 fn test(…) fn(fn(usize) -> i32) []
-                md dep  []
                 fn function fn(usize) -> i32 [requires_import]
                 fn function(…) fn(isize) -> i32 [requires_import]
             "#]],
@@ -1000,9 +1000,9 @@ fn main() {
 "#,
             expect![[r#"
                 ct CONST i32 [type_could_unify+requires_import]
+                md dep  []
                 fn main() fn() []
                 fn test(…) fn(i32) []
-                md dep  []
                 ct CONST i64 [requires_import]
             "#]],
         );
@@ -1032,9 +1032,9 @@ fn main() {
 "#,
             expect![[r#"
                 sc STATIC i32 [type_could_unify+requires_import]
+                md dep  []
                 fn main() fn() []
                 fn test(…) fn(i32) []
-                md dep  []
                 sc STATIC i64 [requires_import]
             "#]],
         );
@@ -1090,8 +1090,8 @@ fn func(input: Struct) { }
 
 "#,
             expect![[r#"
-                st Struct Struct [type]
                 st Self Self [type]
+                st Struct Struct [type]
                 sp Self Struct [type]
                 st Struct Struct [type]
                 ex Struct  [type]
@@ -1119,9 +1119,9 @@ fn main() {
 "#,
             expect![[r#"
                 lc input bool [type+name+local]
+                ex false  [type]
                 ex input  [type]
                 ex true  [type]
-                ex false  [type]
                 lc inputbad i32 [local]
                 fn main() fn() []
                 fn test(…) fn(bool) []
@@ -2088,9 +2088,9 @@ fn f() { A { bar: b$0 }; }
 "#,
             expect![[r#"
                 fn bar() fn() -> u8 [type+name]
+                ex bar()  [type]
                 fn baz() fn() -> u8 [type]
                 ex baz()  [type]
-                ex bar()  [type]
                 st A A []
                 fn f() fn() []
             "#]],
@@ -2199,8 +2199,8 @@ fn main() {
                 lc s S [type+name+local]
                 st S S [type]
                 st S S [type]
-                ex s  [type]
                 ex S  [type]
+                ex s  [type]
                 fn foo(…) fn(&mut S) []
                 fn main() fn() []
             "#]],
@@ -2218,8 +2218,8 @@ fn main() {
                 st S S [type]
                 lc ssss S [type+local]
                 st S S [type]
-                ex ssss  [type]
                 ex S  [type]
+                ex ssss  [type]
                 fn foo(…) fn(&mut S) []
                 fn main() fn() []
             "#]],
@@ -2252,11 +2252,11 @@ fn main() {
                 ex Foo  [type]
                 lc foo &Foo [local]
                 lc *foo [type+local]
-                fn bar(…) fn(Foo) []
-                fn main() fn() []
-                md core  []
                 tt Clone  []
                 tt Copy  []
+                fn bar(…) fn(Foo) []
+                md core  []
+                fn main() fn() []
             "#]],
         );
     }
@@ -2297,9 +2297,9 @@ fn main() {
                 st &S [type]
                 st T T []
                 st &T [type]
+                md core  []
                 fn foo(…) fn(&S) []
                 fn main() fn() []
-                md core  []
             "#]],
         )
     }
@@ -2346,9 +2346,9 @@ fn main() {
                 st &mut S [type]
                 st T T []
                 st &mut T [type]
+                md core  []
                 fn foo(…) fn(&mut S) []
                 fn main() fn() []
-                md core  []
             "#]],
         )
     }
@@ -2364,8 +2364,8 @@ fn foo(bar: u32) {
 }
 "#,
             expect![[r#"
-                lc baz i32 [local]
                 lc bar u32 [local]
+                lc baz i32 [local]
                 fn foo(…) fn(u32) []
             "#]],
         );
@@ -2449,9 +2449,9 @@ fn main() {
                 st &T [type]
                 fn bar() fn() -> T []
                 fn &bar() [type]
+                md core  []
                 fn foo(…) fn(&S) []
                 fn main() fn() []
-                md core  []
             "#]],
         )
     }
@@ -2702,8 +2702,8 @@ fn test() {
                 fn fn_builder() fn() -> FooBuilder [type_could_unify]
                 fn fn_ctr_wrapped() fn() -> Option<Foo<T>> [type_could_unify]
                 fn fn_ctr_wrapped_2() fn() -> Result<Foo<T>, u32> [type_could_unify]
-                me fn_returns_unit(…) fn(&self) [type_could_unify]
                 fn fn_other() fn() -> Option<u32> [type_could_unify]
+                me fn_returns_unit(…) fn(&self) [type_could_unify]
             "#]],
         );
     }
@@ -2965,12 +2965,12 @@ fn foo() {
                 ev Foo::B Foo::B [type_could_unify]
                 ev Foo::A(…) Foo::A(T) [type_could_unify]
                 lc foo Foo<u32> [type+local]
-                ex foo  [type]
                 ex Foo::B  [type]
+                ex foo  [type]
                 en Foo Foo<{unknown}> [type_could_unify]
-                fn foo() fn() []
                 fn bar() fn() -> Foo<u8> []
                 fn baz() fn() -> Foo<T> []
+                fn foo() fn() []
             "#]],
         );
     }
@@ -3000,19 +3000,19 @@ fn main() {
             expect![[r#"
                 sn not !expr [snippet]
                 me not() fn(self) -> <Self as Not>::Output [type_could_unify+requires_import]
-                sn if if expr {} []
-                sn while while expr {} []
-                sn ref &expr []
-                sn refm &mut expr []
-                sn deref *expr []
-                sn unsafe unsafe {} []
-                sn const const {} []
-                sn match match expr {} []
                 sn box Box::new(expr) []
+                sn call function(expr) []
+                sn const const {} []
                 sn dbg dbg!(expr) []
                 sn dbgr dbg!(&expr) []
-                sn call function(expr) []
+                sn deref *expr []
+                sn if if expr {} []
+                sn match match expr {} []
+                sn ref &expr []
+                sn refm &mut expr []
                 sn return return expr []
+                sn unsafe unsafe {} []
+                sn while while expr {} []
             "#]],
         );
     }
@@ -3033,19 +3033,19 @@ fn main() {
             &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)],
             expect![[r#"
                 me f() fn(&self) []
-                sn ref &expr []
-                sn refm &mut expr []
-                sn deref *expr []
-                sn unsafe unsafe {} []
-                sn const const {} []
-                sn match match expr {} []
                 sn box Box::new(expr) []
+                sn call function(expr) []
+                sn const const {} []
                 sn dbg dbg!(expr) []
                 sn dbgr dbg!(&expr) []
-                sn call function(expr) []
+                sn deref *expr []
                 sn let let []
                 sn letm let mut []
+                sn match match expr {} []
+                sn ref &expr []
+                sn refm &mut expr []
                 sn return return expr []
+                sn unsafe unsafe {} []
             "#]],
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
index 2fe517fa8cd..7669aec8f53 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
@@ -22,13 +22,13 @@ use crate::{
 
 #[derive(Debug)]
 enum FuncKind<'ctx> {
-    Function(&'ctx PathCompletionCtx),
-    Method(&'ctx DotAccess, Option<SmolStr>),
+    Function(&'ctx PathCompletionCtx<'ctx>),
+    Method(&'ctx DotAccess<'ctx>, Option<SmolStr>),
 }
 
 pub(crate) fn render_fn(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     local_name: Option<hir::Name>,
     func: hir::Function,
 ) -> Builder {
@@ -38,7 +38,7 @@ pub(crate) fn render_fn(
 
 pub(crate) fn render_method(
     ctx: RenderContext<'_>,
-    dot_access: &DotAccess,
+    dot_access: &DotAccess<'_>,
     receiver: Option<SmolStr>,
     local_name: Option<hir::Name>,
     func: hir::Function,
@@ -186,8 +186,8 @@ fn render(
 fn compute_return_type_match(
     db: &dyn HirDatabase,
     ctx: &RenderContext<'_>,
-    self_type: hir::Type,
-    ret_type: &hir::Type,
+    self_type: hir::Type<'_>,
+    ret_type: &hir::Type<'_>,
 ) -> CompletionRelevanceReturnType {
     if match_types(ctx.completion, &self_type, ret_type).is_some() {
         // fn([..]) -> Self
@@ -217,8 +217,8 @@ pub(super) fn add_call_parens<'b>(
     name: SmolStr,
     escaped_name: SmolStr,
     self_param: Option<hir::SelfParam>,
-    params: Vec<hir::Param>,
-    ret_type: &hir::Type,
+    params: Vec<hir::Param<'_>>,
+    ret_type: &hir::Type<'_>,
 ) -> &'b mut Builder {
     cov_mark::hit!(inserts_parens_for_function_calls);
 
@@ -288,7 +288,7 @@ pub(super) fn add_call_parens<'b>(
     builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet)
 }
 
-fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'static str {
+fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type<'_>) -> &'static str {
     if let Some(derefed_ty) = ty.remove_ref() {
         for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) {
             if name.as_str() == arg {
@@ -369,12 +369,12 @@ fn params_display(ctx: &CompletionContext<'_>, detail: &mut String, func: hir::F
     }
 }
 
-fn params(
-    ctx: &CompletionContext<'_>,
+fn params<'db>(
+    ctx: &CompletionContext<'db>,
     func: hir::Function,
     func_kind: &FuncKind<'_>,
     has_dot_receiver: bool,
-) -> Option<(Option<hir::SelfParam>, Vec<hir::Param>)> {
+) -> Option<(Option<hir::SelfParam>, Vec<hir::Param<'db>>)> {
     ctx.config.callable.as_ref()?;
 
     // Don't add parentheses if the expected type is a function reference with the same signature.
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
index 5a9e35a7290..6c89e49f94e 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
@@ -21,7 +21,7 @@ use crate::{
 
 pub(crate) fn render_variant_lit(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     local_name: Option<hir::Name>,
     variant: hir::Variant,
     path: Option<hir::ModPath>,
@@ -35,7 +35,7 @@ pub(crate) fn render_variant_lit(
 
 pub(crate) fn render_struct_literal(
     ctx: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     strukt: hir::Struct,
     path: Option<hir::ModPath>,
     local_name: Option<hir::Name>,
@@ -49,7 +49,7 @@ pub(crate) fn render_struct_literal(
 
 fn render(
     ctx @ RenderContext { completion, .. }: RenderContext<'_>,
-    path_ctx: &PathCompletionCtx,
+    path_ctx: &PathCompletionCtx<'_>,
     thing: Variant,
     name: hir::Name,
     path: Option<hir::ModPath>,
@@ -194,7 +194,7 @@ impl Variant {
         }
     }
 
-    fn ty(self, db: &dyn HirDatabase) -> hir::Type {
+    fn ty(self, db: &dyn HirDatabase) -> hir::Type<'_> {
         match self {
             Variant::Struct(it) => it.ty(db),
             Variant::EnumVariant(it) => it.parent_enum(db).ty(db),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index 4674dae0314..35fe407b2e6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -12,7 +12,7 @@ use crate::{
 
 pub(crate) fn render_macro(
     ctx: RenderContext<'_>,
-    PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx,
+    PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx<'_>,
 
     name: hir::Name,
     macro_: hir::Macro,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
index dcc51a86a8e..60ec1128233 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
@@ -46,7 +46,7 @@ pub(crate) fn render_struct_pat(
 pub(crate) fn render_variant_pat(
     ctx: RenderContext<'_>,
     pattern_ctx: &PatternContext,
-    path_ctx: Option<&PathCompletionCtx>,
+    path_ctx: Option<&PathCompletionCtx<'_>>,
     variant: hir::Variant,
     local_name: Option<Name>,
     path: Option<&hir::ModPath>,
@@ -109,7 +109,7 @@ fn build_completion(
     lookup: SmolStr,
     pat: String,
     def: impl HasDocs + Copy,
-    adt_ty: hir::Type,
+    adt_ty: hir::Type<'_>,
     // Missing in context of match statement completions
     is_variant_missing: bool,
 ) -> CompletionItem {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index b46e4c32061..b2d18b796f1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -1474,20 +1474,18 @@ fn main() {
 }
 "#,
         expect![[r#"
+            me foo()     fn(&self)
             sn box  Box::new(expr)
             sn call function(expr)
             sn const      const {}
             sn dbg      dbg!(expr)
             sn dbgr    dbg!(&expr)
             sn deref         *expr
-            sn if       if expr {}
             sn match match expr {}
-            sn not           !expr
             sn ref           &expr
             sn refm      &mut expr
             sn return  return expr
             sn unsafe    unsafe {}
-            sn while while expr {}
         "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs
index 4b5a0ac1c2b..b404011dfe6 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/visibility.rs
@@ -1,7 +1,7 @@
 //! Completion tests for visibility modifiers.
 use expect_test::expect;
 
-use crate::tests::{check, check_with_trigger_character};
+use crate::tests::{check, check_with_private_editable, check_with_trigger_character};
 
 #[test]
 fn empty_pub() {
@@ -78,3 +78,90 @@ mod bar {}
         "#]],
     );
 }
+
+#[test]
+fn use_inner_public_function() {
+    check(
+        r#"
+//- /inner.rs crate:inner
+pub fn inner_public() {}
+fn inner_private() {}
+//- /foo.rs crate:foo deps:inner
+use inner::inner_public;
+pub fn outer_public() {}
+//- /lib.rs crate:lib deps:foo
+fn x() {
+    foo::$0
+}
+        "#,
+        expect![[r#"
+            fn outer_public() fn()
+        "#]],
+    );
+}
+
+#[test]
+fn pub_use_inner_public_function() {
+    check(
+        r#"
+//- /inner.rs crate:inner
+pub fn inner_public() {}
+fn inner_private() {}
+//- /foo.rs crate:foo deps:inner
+pub use inner::inner_public;
+pub fn outer_public() {}
+//- /lib.rs crate:lib deps:foo
+fn x() {
+    foo::$0
+}
+        "#,
+        expect![[r#"
+            fn inner_public() fn()
+            fn outer_public() fn()
+        "#]],
+    );
+}
+
+#[test]
+fn use_inner_public_function_private_editable() {
+    check_with_private_editable(
+        r#"
+//- /inner.rs crate:inner
+pub fn inner_public() {}
+fn inner_private() {}
+//- /foo.rs crate:foo deps:inner
+use inner::inner_public;
+pub fn outer_public() {}
+//- /lib.rs crate:lib deps:foo
+fn x() {
+    foo::$0
+}
+        "#,
+        expect![[r#"
+            fn inner_public() fn()
+            fn outer_public() fn()
+        "#]],
+    );
+}
+
+#[test]
+fn pub_use_inner_public_function_private_editable() {
+    check_with_private_editable(
+        r#"
+//- /inner.rs crate:inner
+pub fn inner_public() {}
+fn inner_private() {}
+//- /foo.rs crate:foo deps:inner
+pub use inner::inner_public;
+pub fn outer_public() {}
+//- /lib.rs crate:lib deps:foo
+fn x() {
+    foo::$0
+}
+        "#,
+        expect![[r#"
+            fn inner_public() fn()
+            fn outer_public() fn()
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
index 7b5723f37f7..9edfc113f76 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs
@@ -13,21 +13,21 @@ use syntax::{
 use crate::RootDatabase;
 
 #[derive(Debug)]
-pub struct ActiveParameter {
-    pub ty: Type,
+pub struct ActiveParameter<'db> {
+    pub ty: Type<'db>,
     pub src: Option<InFile<Either<ast::SelfParam, ast::Param>>>,
 }
 
-impl ActiveParameter {
+impl<'db> ActiveParameter<'db> {
     /// Returns information about the call argument this token is part of.
-    pub fn at_token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Self> {
+    pub fn at_token(sema: &Semantics<'db, RootDatabase>, token: SyntaxToken) -> Option<Self> {
         let (signature, active_parameter) = callable_for_token(sema, token)?;
         Self::from_signature_and_active_parameter(sema, signature, active_parameter)
     }
 
     /// Returns information about the call argument this token is part of.
     pub fn at_arg(
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &'db Semantics<'db, RootDatabase>,
         list: ast::ArgList,
         at: TextSize,
     ) -> Option<Self> {
@@ -36,8 +36,8 @@ impl ActiveParameter {
     }
 
     fn from_signature_and_active_parameter(
-        sema: &Semantics<'_, RootDatabase>,
-        signature: hir::Callable,
+        sema: &Semantics<'db, RootDatabase>,
+        signature: hir::Callable<'db>,
         active_parameter: Option<usize>,
     ) -> Option<Self> {
         let idx = active_parameter?;
@@ -63,10 +63,10 @@ impl ActiveParameter {
 }
 
 /// Returns a [`hir::Callable`] this token is a part of and its argument index of said callable.
-pub fn callable_for_token(
-    sema: &Semantics<'_, RootDatabase>,
+pub fn callable_for_token<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     token: SyntaxToken,
-) -> Option<(hir::Callable, Option<usize>)> {
+) -> Option<(hir::Callable<'db>, Option<usize>)> {
     let offset = token.text_range().start();
     // Find the calling expression and its NameRef
     let parent = token.parent()?;
@@ -79,21 +79,21 @@ pub fn callable_for_token(
 }
 
 /// Returns a [`hir::Callable`] this token is a part of and its argument index of said callable.
-pub fn callable_for_arg_list(
-    sema: &Semantics<'_, RootDatabase>,
+pub fn callable_for_arg_list<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     arg_list: ast::ArgList,
     at: TextSize,
-) -> Option<(hir::Callable, Option<usize>)> {
+) -> Option<(hir::Callable<'db>, Option<usize>)> {
     debug_assert!(arg_list.syntax().text_range().contains(at));
     let callable = arg_list.syntax().parent().and_then(ast::CallableExpr::cast)?;
     callable_for_node(sema, &callable, at)
 }
 
-pub fn callable_for_node(
-    sema: &Semantics<'_, RootDatabase>,
+pub fn callable_for_node<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     calling_node: &ast::CallableExpr,
     offset: TextSize,
-) -> Option<(hir::Callable, Option<usize>)> {
+) -> Option<(hir::Callable<'db>, Option<usize>)> {
     let callable = match calling_node {
         ast::CallableExpr::Call(call) => sema.resolve_expr_as_callable(&call.expr()?),
         ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index d5db1c481b6..a4a140ec57a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -385,17 +385,17 @@ fn find_std_module(
 
 // FIXME: IdentClass as a name no longer fits
 #[derive(Debug)]
-pub enum IdentClass {
-    NameClass(NameClass),
-    NameRefClass(NameRefClass),
+pub enum IdentClass<'db> {
+    NameClass(NameClass<'db>),
+    NameRefClass(NameRefClass<'db>),
     Operator(OperatorClass),
 }
 
-impl IdentClass {
+impl<'db> IdentClass<'db> {
     pub fn classify_node(
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         node: &SyntaxNode,
-    ) -> Option<IdentClass> {
+    ) -> Option<IdentClass<'db>> {
         match_ast! {
             match node {
                 ast::Name(name) => NameClass::classify(sema, &name).map(IdentClass::NameClass),
@@ -418,23 +418,23 @@ impl IdentClass {
     }
 
     pub fn classify_token(
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         token: &SyntaxToken,
-    ) -> Option<IdentClass> {
+    ) -> Option<IdentClass<'db>> {
         let parent = token.parent()?;
         Self::classify_node(sema, &parent)
     }
 
     pub fn classify_lifetime(
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         lifetime: &ast::Lifetime,
-    ) -> Option<IdentClass> {
+    ) -> Option<IdentClass<'db>> {
         NameRefClass::classify_lifetime(sema, lifetime)
             .map(IdentClass::NameRefClass)
             .or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass))
     }
 
-    pub fn definitions(self) -> ArrayVec<(Definition, Option<GenericSubstitution>), 2> {
+    pub fn definitions(self) -> ArrayVec<(Definition, Option<GenericSubstitution<'db>>), 2> {
         let mut res = ArrayVec::new();
         match self {
             IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
@@ -518,7 +518,7 @@ impl IdentClass {
 ///
 /// A model special case is `None` constant in pattern.
 #[derive(Debug)]
-pub enum NameClass {
+pub enum NameClass<'db> {
     Definition(Definition),
     /// `None` in `if let None = Some(82) {}`.
     /// Syntactically, it is a name, but semantically it is a reference.
@@ -528,11 +528,11 @@ pub enum NameClass {
     PatFieldShorthand {
         local_def: Local,
         field_ref: Field,
-        adt_subst: GenericSubstitution,
+        adt_subst: GenericSubstitution<'db>,
     },
 }
 
-impl NameClass {
+impl<'db> NameClass<'db> {
     /// `Definition` defined by this name.
     pub fn defined(self) -> Option<Definition> {
         let res = match self {
@@ -545,7 +545,10 @@ impl NameClass {
         Some(res)
     }
 
-    pub fn classify(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<NameClass> {
+    pub fn classify(
+        sema: &Semantics<'db, RootDatabase>,
+        name: &ast::Name,
+    ) -> Option<NameClass<'db>> {
         let _p = tracing::info_span!("NameClass::classify").entered();
 
         let parent = name.syntax().parent()?;
@@ -597,10 +600,10 @@ impl NameClass {
             Some(definition)
         }
 
-        fn classify_ident_pat(
-            sema: &Semantics<'_, RootDatabase>,
+        fn classify_ident_pat<'db>(
+            sema: &Semantics<'db, RootDatabase>,
             ident_pat: ast::IdentPat,
-        ) -> Option<NameClass> {
+        ) -> Option<NameClass<'db>> {
             if let Some(def) = sema.resolve_bind_pat_to_const(&ident_pat) {
                 return Some(NameClass::ConstReference(Definition::from(def)));
             }
@@ -638,9 +641,9 @@ impl NameClass {
     }
 
     pub fn classify_lifetime(
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         lifetime: &ast::Lifetime,
-    ) -> Option<NameClass> {
+    ) -> Option<NameClass<'db>> {
         let _p = tracing::info_span!("NameClass::classify_lifetime", ?lifetime).entered();
         let parent = lifetime.syntax().parent()?;
 
@@ -723,12 +726,12 @@ impl OperatorClass {
 /// A model special case is field shorthand syntax, which uses a single
 /// reference to point to two different defs.
 #[derive(Debug)]
-pub enum NameRefClass {
-    Definition(Definition, Option<GenericSubstitution>),
+pub enum NameRefClass<'db> {
+    Definition(Definition, Option<GenericSubstitution<'db>>),
     FieldShorthand {
         local_ref: Local,
         field_ref: Field,
-        adt_subst: GenericSubstitution,
+        adt_subst: GenericSubstitution<'db>,
     },
     /// The specific situation where we have an extern crate decl without a rename
     /// Here we have both a declaration and a reference.
@@ -741,13 +744,13 @@ pub enum NameRefClass {
     },
 }
 
-impl NameRefClass {
+impl<'db> NameRefClass<'db> {
     // Note: we don't have unit-tests for this rather important function.
     // It is primarily exercised via goto definition tests in `ide`.
     pub fn classify(
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         name_ref: &ast::NameRef,
-    ) -> Option<NameRefClass> {
+    ) -> Option<NameRefClass<'db>> {
         let _p = tracing::info_span!("NameRefClass::classify", ?name_ref).entered();
 
         let parent = name_ref.syntax().parent()?;
@@ -866,9 +869,9 @@ impl NameRefClass {
     }
 
     pub fn classify_lifetime(
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         lifetime: &ast::Lifetime,
-    ) -> Option<NameRefClass> {
+    ) -> Option<NameRefClass<'db>> {
         let _p = tracing::info_span!("NameRefClass::classify_lifetime", ?lifetime).entered();
         if lifetime.text() == "'static" {
             return Some(NameRefClass::Definition(
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index ac592dfe93c..9f35988924b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -25,26 +25,26 @@ use crate::{
 /// * assists
 /// * etc.
 #[derive(Debug)]
-pub enum ImportCandidate {
+pub enum ImportCandidate<'db> {
     /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
     Path(PathImportCandidate),
     /// A trait associated function (with no self parameter) or an associated constant.
     /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
     /// and `name` is the `test_function`
-    TraitAssocItem(TraitImportCandidate),
+    TraitAssocItem(TraitImportCandidate<'db>),
     /// A trait method with self parameter.
     /// For 'test_enum.test_method()', `ty` is the `test_enum` expression type
     /// and `name` is the `test_method`
-    TraitMethod(TraitImportCandidate),
+    TraitMethod(TraitImportCandidate<'db>),
 }
 
 /// A trait import needed for a given associated item access.
 /// For `some::path::SomeStruct::ASSOC_`, contains the
 /// type of `some::path::SomeStruct` and `ASSOC_` as the item name.
 #[derive(Debug)]
-pub struct TraitImportCandidate {
+pub struct TraitImportCandidate<'db> {
     /// A type of the item that has the associated item accessed at.
-    pub receiver_ty: Type,
+    pub receiver_ty: Type<'db>,
     /// The associated item name that the trait to import should contain.
     pub assoc_item_name: NameToImport,
 }
@@ -100,16 +100,16 @@ impl NameToImport {
 
 /// A struct to find imports in the project, given a certain name (or its part) and the context.
 #[derive(Debug)]
-pub struct ImportAssets {
-    import_candidate: ImportCandidate,
+pub struct ImportAssets<'db> {
+    import_candidate: ImportCandidate<'db>,
     candidate_node: SyntaxNode,
     module_with_candidate: Module,
 }
 
-impl ImportAssets {
+impl<'db> ImportAssets<'db> {
     pub fn for_method_call(
         method_call: &ast::MethodCallExpr,
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
     ) -> Option<Self> {
         let candidate_node = method_call.syntax().clone();
         Some(Self {
@@ -121,7 +121,7 @@ impl ImportAssets {
 
     pub fn for_exact_path(
         fully_qualified_path: &ast::Path,
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
     ) -> Option<Self> {
         let candidate_node = fully_qualified_path.syntax().clone();
         if let Some(use_tree) = candidate_node.ancestors().find_map(ast::UseTree::cast) {
@@ -139,7 +139,7 @@ impl ImportAssets {
         })
     }
 
-    pub fn for_ident_pat(sema: &Semantics<'_, RootDatabase>, pat: &ast::IdentPat) -> Option<Self> {
+    pub fn for_ident_pat(sema: &Semantics<'db, RootDatabase>, pat: &ast::IdentPat) -> Option<Self> {
         if !pat.is_simple_ident() {
             return None;
         }
@@ -156,7 +156,7 @@ impl ImportAssets {
         module_with_candidate: Module,
         qualifier: Option<ast::Path>,
         fuzzy_name: String,
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         candidate_node: SyntaxNode,
     ) -> Option<Self> {
         Some(Self {
@@ -168,7 +168,7 @@ impl ImportAssets {
 
     pub fn for_fuzzy_method_call(
         module_with_method_call: Module,
-        receiver_ty: Type,
+        receiver_ty: Type<'db>,
         fuzzy_method_name: String,
         candidate_node: SyntaxNode,
     ) -> Option<Self> {
@@ -229,14 +229,14 @@ impl LocatedImport {
     }
 }
 
-impl ImportAssets {
-    pub fn import_candidate(&self) -> &ImportCandidate {
+impl<'db> ImportAssets<'db> {
+    pub fn import_candidate(&self) -> &ImportCandidate<'db> {
         &self.import_candidate
     }
 
     pub fn search_for_imports(
         &self,
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         cfg: ImportPathConfig,
         prefix_kind: PrefixKind,
     ) -> impl Iterator<Item = LocatedImport> {
@@ -247,7 +247,7 @@ impl ImportAssets {
     /// This may return non-absolute paths if a part of the returned path is already imported into scope.
     pub fn search_for_relative_paths(
         &self,
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         cfg: ImportPathConfig,
     ) -> impl Iterator<Item = LocatedImport> {
         let _p = tracing::info_span!("ImportAssets::search_for_relative_paths").entered();
@@ -286,7 +286,7 @@ impl ImportAssets {
 
     fn search_for(
         &self,
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         prefixed: Option<PrefixKind>,
         cfg: ImportPathConfig,
     ) -> impl Iterator<Item = LocatedImport> {
@@ -533,11 +533,11 @@ fn item_for_path_search_assoc(db: &RootDatabase, assoc_item: AssocItem) -> Optio
     })
 }
 
-fn trait_applicable_items(
-    db: &RootDatabase,
+fn trait_applicable_items<'db>(
+    db: &'db RootDatabase,
     current_crate: Crate,
-    scope: &SemanticsScope<'_>,
-    trait_candidate: &TraitImportCandidate,
+    scope: &SemanticsScope<'db>,
+    trait_candidate: &TraitImportCandidate<'db>,
     trait_assoc_item: bool,
     mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
     scope_filter: impl Fn(hir::Trait) -> bool,
@@ -709,9 +709,9 @@ fn get_mod_path(
     }
 }
 
-impl ImportCandidate {
+impl<'db> ImportCandidate<'db> {
     fn for_method_call(
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
         method_call: &ast::MethodCallExpr,
     ) -> Option<Self> {
         match sema.resolve_method_call(method_call) {
@@ -725,7 +725,7 @@ impl ImportCandidate {
         }
     }
 
-    fn for_regular_path(sema: &Semantics<'_, RootDatabase>, path: &ast::Path) -> Option<Self> {
+    fn for_regular_path(sema: &Semantics<'db, RootDatabase>, path: &ast::Path) -> Option<Self> {
         if sema.resolve_path(path).is_some() {
             return None;
         }
@@ -736,7 +736,7 @@ impl ImportCandidate {
         )
     }
 
-    fn for_name(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<Self> {
+    fn for_name(sema: &Semantics<'db, RootDatabase>, name: &ast::Name) -> Option<Self> {
         if sema
             .scope(name.syntax())?
             .speculative_resolve(&make::ext::ident_path(&name.text()))
@@ -753,17 +753,17 @@ impl ImportCandidate {
     fn for_fuzzy_path(
         qualifier: Option<ast::Path>,
         fuzzy_name: String,
-        sema: &Semantics<'_, RootDatabase>,
+        sema: &Semantics<'db, RootDatabase>,
     ) -> Option<Self> {
         path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name))
     }
 }
 
-fn path_import_candidate(
-    sema: &Semantics<'_, RootDatabase>,
+fn path_import_candidate<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     qualifier: Option<ast::Path>,
     name: NameToImport,
-) -> Option<ImportCandidate> {
+) -> Option<ImportCandidate<'db>> {
     Some(match qualifier {
         Some(qualifier) => match sema.resolve_path(&qualifier) {
             Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index c5ad64ed594..7d460f72492 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -429,7 +429,7 @@ pub struct FindUsages<'a> {
     /// The container of our definition should it be an assoc item
     assoc_item_container: Option<hir::AssocItemContainer>,
     /// whether to search for the `Self` type of the definition
-    include_self_kw_refs: Option<hir::Type>,
+    include_self_kw_refs: Option<hir::Type<'a>>,
     /// whether to search for the `self` module
     search_self_mod: bool,
 }
@@ -1087,12 +1087,12 @@ impl<'a> FindUsages<'a> {
 
     fn found_self_ty_name_ref(
         &self,
-        self_ty: &hir::Type,
+        self_ty: &hir::Type<'_>,
         name_ref: &ast::NameRef,
         sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
     ) -> bool {
         // See https://github.com/rust-lang/rust-analyzer/pull/15864/files/e0276dc5ddc38c65240edb408522bb869f15afb4#r1389848845
-        let ty_eq = |ty: hir::Type| match (ty.as_adt(), self_ty.as_adt()) {
+        let ty_eq = |ty: hir::Type<'_>| match (ty.as_adt(), self_ty.as_adt()) {
             (Some(ty), Some(self_ty)) => ty == self_ty,
             (None, None) => ty == *self_ty,
             _ => false,
@@ -1315,7 +1315,7 @@ impl<'a> FindUsages<'a> {
     }
 }
 
-fn def_to_ty(sema: &Semantics<'_, RootDatabase>, def: &Definition) -> Option<hir::Type> {
+fn def_to_ty<'db>(sema: &Semantics<'db, RootDatabase>, def: &Definition) -> Option<hir::Type<'db>> {
     match def {
         Definition::Adt(adt) => Some(adt.ty(sema.db)),
         Definition::TypeAlias(it) => Some(it.ty(sema.db)),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 9b9f450bc73..995bf72dca1 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -151,10 +151,10 @@ impl NameGenerator {
     /// - If `ty` is an `impl Trait`, it will suggest the name of the first trait.
     ///
     /// If the suggested name conflicts with reserved keywords, it will return `None`.
-    pub fn for_type(
+    pub fn for_type<'db>(
         &mut self,
-        ty: &hir::Type,
-        db: &RootDatabase,
+        ty: &hir::Type<'db>,
+        db: &'db RootDatabase,
         edition: Edition,
     ) -> Option<SmolStr> {
         let name = name_of_type(ty, db, edition)?;
@@ -373,7 +373,11 @@ fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<Smo
     name_of_type(&ty, sema.db, edition)
 }
 
-fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<SmolStr> {
+fn name_of_type<'db>(
+    ty: &hir::Type<'db>,
+    db: &'db RootDatabase,
+    edition: Edition,
+) -> Option<SmolStr> {
     let name = if let Some(adt) = ty.as_adt() {
         let name = adt.name(db).display(db, edition).to_string();
 
@@ -407,7 +411,11 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S
     normalize(&name)
 }
 
-fn sequence_name(inner_ty: Option<&hir::Type>, db: &RootDatabase, edition: Edition) -> SmolStr {
+fn sequence_name<'db>(
+    inner_ty: Option<&hir::Type<'db>>,
+    db: &'db RootDatabase,
+    edition: Edition,
+) -> SmolStr {
     let items_str = SmolStr::new_static("items");
     let Some(inner_ty) = inner_ty else {
         return items_str;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs
index 63ce0ddbb8f..095256d8294 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs
@@ -10,7 +10,7 @@ use syntax::ast::{self, Pat, make};
 use crate::RootDatabase;
 
 /// Enum types that implement `std::ops::Try` trait.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
 pub enum TryEnum {
     Result,
     Option,
@@ -20,7 +20,7 @@ impl TryEnum {
     const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result];
 
     /// Returns `Some(..)` if the provided type is an enum that implements `std::ops::Try`.
-    pub fn from_ty(sema: &Semantics<'_, RootDatabase>, ty: &hir::Type) -> Option<TryEnum> {
+    pub fn from_ty(sema: &Semantics<'_, RootDatabase>, ty: &hir::Type<'_>) -> Option<TryEnum> {
         let enum_ = match ty.as_adt() {
             Some(hir::Adt::Enum(it)) => it,
             _ => return None,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
index a4a93e36f0e..f63cd92694b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
@@ -11,7 +11,7 @@ use syntax::{
 pub fn use_trivial_constructor(
     db: &crate::RootDatabase,
     path: Path,
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     edition: Edition,
 ) -> Option<Expr> {
     match ty.as_adt() {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
index 7d2ac373dc0..afd1687ae07 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -7,7 +7,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
 // This diagnostic is triggered if a call is made on something that is not callable.
 pub(crate) fn expected_function(
     ctx: &DiagnosticsContext<'_>,
-    d: &hir::ExpectedFunction,
+    d: &hir::ExpectedFunction<'_>,
 ) -> Diagnostic {
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
index 7a6e98fe1b5..a59077b757b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -18,7 +18,7 @@ macro_rules! format_ty {
 // Diagnostic: invalid-cast
 //
 // This diagnostic is triggered if the code contains an illegal cast
-pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) -> Diagnostic {
+pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_>) -> Diagnostic {
     let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into()));
     let (code, message) = match d.error {
         CastError::CastToBool => (
@@ -106,7 +106,10 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) -
 // Diagnostic: cast-to-unsized
 //
 // This diagnostic is triggered when casting to an unsized type
-pub(crate) fn cast_to_unsized(ctx: &DiagnosticsContext<'_>, d: &hir::CastToUnsized) -> Diagnostic {
+pub(crate) fn cast_to_unsized(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::CastToUnsized<'_>,
+) -> Diagnostic {
     let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into()));
     Diagnostic::new(
         DiagnosticCode::RustcHardError("E0620"),
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 2b76efb1965..8a5d82b48c0 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -106,7 +106,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
                 }
             });
 
-            let generate_fill_expr = |ty: &Type| match ctx.config.expr_fill_default {
+            let generate_fill_expr = |ty: &Type<'_>| match ctx.config.expr_fill_default {
                 ExprFillDefaultMode::Todo => make::ext::expr_todo(),
                 ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
                 ExprFillDefaultMode::Default => {
@@ -180,7 +180,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
 }
 
 fn make_ty(
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     db: &dyn HirDatabase,
     module: hir::Module,
     edition: Edition,
@@ -198,7 +198,7 @@ fn make_ty(
 fn get_default_constructor(
     ctx: &DiagnosticsContext<'_>,
     d: &hir::MissingFields,
-    ty: &Type,
+    ty: &Type<'_>,
 ) -> Option<ast::Expr> {
     if let Some(builtin_ty) = ty.as_builtin() {
         if builtin_ty.is_int() || builtin_ty.is_uint() {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 6bd5417b25d..d8f6e813d80 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -630,6 +630,17 @@ fn main() {
         // Checks that we don't place orphan arguments for formatting under an unsafe block.
         check_diagnostics(
             r#"
+//- minicore: fmt_before_1_89_0
+fn foo() {
+    let p = 0xDEADBEEF as *const i32;
+    format_args!("", *p);
+                  // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block
+}
+        "#,
+        );
+
+        check_diagnostics(
+            r#"
 //- minicore: fmt
 fn foo() {
     let p = 0xDEADBEEF as *const i32;
@@ -958,4 +969,18 @@ impl FooTrait for S2 {
         "#,
         );
     }
+
+    #[test]
+    fn no_false_positive_on_format_args_since_1_89_0() {
+        check_diagnostics(
+            r#"
+//- minicore: fmt
+fn test() {
+    let foo = 10;
+    let bar = true;
+    let _x = format_args!("{} {0} {} {last}", foo, bar, last = "!");
+}
+            "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index 01cf5e8fa52..0928262d22f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -4,7 +4,10 @@ use hir::HirDisplay;
 // Diagnostic: moved-out-of-ref
 //
 // This diagnostic is triggered on moving non copy things out of references.
-pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOfRef) -> Diagnostic {
+pub(crate) fn moved_out_of_ref(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::MovedOutOfRef<'_>,
+) -> Diagnostic {
     Diagnostic::new_with_syntax_node_ptr(
         ctx,
         DiagnosticCode::RustcHardError("E0507"),
@@ -217,4 +220,23 @@ fn test() {
             "#,
         )
     }
+
+    #[test]
+    fn regression_18201() {
+        check_diagnostics(
+            r#"
+//- minicore: copy
+struct NotCopy;
+struct S(NotCopy);
+impl S {
+    fn f(&mut self) {
+        || {
+            if let ref mut _cb = self.0 {
+            }
+        };
+    }
+}
+"#,
+        )
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index ef42f2dc744..0edab5e0b3b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -1,5 +1,4 @@
 use either::Either;
-use hir::{Field, HasCrate};
 use hir::{HasSource, HirDisplay, Semantics, VariantId, db::ExpandDatabase};
 use ide_db::text_edit::TextEdit;
 use ide_db::{EditionedFileId, RootDatabase, source_change::SourceChange};
@@ -8,7 +7,10 @@ use syntax::{
     ast::{self, edit::IndentLevel, make},
 };
 
-use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
+use crate::{
+    Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix,
+    handlers::private_field::field_is_private_fixes,
+};
 
 // Diagnostic: no-such-field
 //
@@ -37,8 +39,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assis
                 field_is_private_fixes(
                     &ctx.sema,
                     d.field.file_id.original_file(ctx.sema.db),
-                    node,
                     private_field,
+                    ctx.sema.original_range(node.syntax()).range,
                 )
             } else {
                 missing_record_expr_field_fixes(
@@ -52,31 +54,6 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assis
     }
 }
 
-fn field_is_private_fixes(
-    sema: &Semantics<'_, RootDatabase>,
-    usage_file_id: EditionedFileId,
-    record_expr_field: &ast::RecordExprField,
-    private_field: Field,
-) -> Option<Vec<Assist>> {
-    let def_crate = private_field.krate(sema.db);
-    let usage_crate = sema.file_to_module_def(usage_file_id.file_id(sema.db))?.krate();
-    let visibility = if usage_crate == def_crate { "pub(crate) " } else { "pub " };
-
-    let source = private_field.source(sema.db)?;
-    let (range, _) = source.syntax().original_file_range_opt(sema.db)?;
-    let source_change = SourceChange::from_text_edit(
-        range.file_id.file_id(sema.db),
-        TextEdit::insert(range.range.start(), visibility.into()),
-    );
-
-    Some(vec![fix(
-        "increase_field_visibility",
-        "Increase field visibility",
-        source_change,
-        sema.original_range(record_expr_field.syntax()).range,
-    )])
-}
-
 fn missing_record_expr_field_fixes(
     sema: &Semantics<'_, RootDatabase>,
     usage_file_id: EditionedFileId,
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
index 5b4273a5a62..69cd0d27cb0 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
@@ -1,4 +1,8 @@
-use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+use hir::{EditionedFileId, FileRange, HasCrate, HasSource, Semantics};
+use ide_db::{RootDatabase, assists::Assist, source_change::SourceChange, text_edit::TextEdit};
+use syntax::{AstNode, TextRange, TextSize, ast::HasVisibility};
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
 
 // Diagnostic: private-field
 //
@@ -16,11 +20,59 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField)
         d.expr.map(|it| it.into()),
     )
     .stable()
+    .with_fixes(field_is_private_fixes(
+        &ctx.sema,
+        d.expr.file_id.original_file(ctx.sema.db),
+        d.field,
+        ctx.sema.original_range(d.expr.to_node(ctx.sema.db).syntax()).range,
+    ))
+}
+
+pub(crate) fn field_is_private_fixes(
+    sema: &Semantics<'_, RootDatabase>,
+    usage_file_id: EditionedFileId,
+    private_field: hir::Field,
+    fix_range: TextRange,
+) -> Option<Vec<Assist>> {
+    let def_crate = private_field.krate(sema.db);
+    let usage_crate = sema.file_to_module_def(usage_file_id.file_id(sema.db))?.krate();
+    let mut visibility_text = if usage_crate == def_crate { "pub(crate) " } else { "pub " };
+
+    let source = private_field.source(sema.db)?;
+    let existing_visibility = match &source.value {
+        hir::FieldSource::Named(it) => it.visibility(),
+        hir::FieldSource::Pos(it) => it.visibility(),
+    };
+    let range = match existing_visibility {
+        Some(visibility) => {
+            // If there is an existing visibility, don't insert whitespace after.
+            visibility_text = visibility_text.trim_end();
+            source.with_value(visibility.syntax()).original_file_range_opt(sema.db)?.0
+        }
+        None => {
+            let (range, _) = source.syntax().original_file_range_opt(sema.db)?;
+            FileRange {
+                file_id: range.file_id,
+                range: TextRange::at(range.range.start(), TextSize::new(0)),
+            }
+        }
+    };
+    let source_change = SourceChange::from_text_edit(
+        range.file_id.file_id(sema.db),
+        TextEdit::replace(range.range, visibility_text.into()),
+    );
+
+    Some(vec![fix(
+        "increase_field_visibility",
+        "Increase field visibility",
+        source_change,
+        fix_range,
+    )])
 }
 
 #[cfg(test)]
 mod tests {
-    use crate::tests::check_diagnostics;
+    use crate::tests::{check_diagnostics, check_fix};
 
     #[test]
     fn private_field() {
@@ -29,7 +81,7 @@ mod tests {
 mod module { pub struct Struct { field: u32 } }
 fn main(s: module::Struct) {
     s.field;
-  //^^^^^^^ error: field `field` of `Struct` is private
+  //^^^^^^^ 💡 error: field `field` of `Struct` is private
 }
 "#,
         );
@@ -42,7 +94,7 @@ fn main(s: module::Struct) {
 mod module { pub struct Struct(u32); }
 fn main(s: module::Struct) {
     s.0;
-  //^^^ error: field `0` of `Struct` is private
+  //^^^ 💡 error: field `0` of `Struct` is private
 }
 "#,
         );
@@ -113,4 +165,68 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn change_visibility_fix() {
+        check_fix(
+            r#"
+pub mod foo {
+    pub mod bar {
+        pub struct Struct {
+            field: i32,
+        }
+    }
+}
+
+fn foo(v: foo::bar::Struct) {
+    v.field$0;
+}
+            "#,
+            r#"
+pub mod foo {
+    pub mod bar {
+        pub struct Struct {
+            pub(crate) field: i32,
+        }
+    }
+}
+
+fn foo(v: foo::bar::Struct) {
+    v.field;
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn change_visibility_with_existing_visibility() {
+        check_fix(
+            r#"
+pub mod foo {
+    pub mod bar {
+        pub struct Struct {
+            pub(super) field: i32,
+        }
+    }
+}
+
+fn foo(v: foo::bar::Struct) {
+    v.field$0;
+}
+            "#,
+            r#"
+pub mod foo {
+    pub mod bar {
+        pub struct Struct {
+            pub(crate) field: i32,
+        }
+    }
+}
+
+fn foo(v: foo::bar::Struct) {
+    v.field;
+}
+            "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 076df1ab0f8..e2957fcaefb 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -20,7 +20,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_dis
 //
 // This diagnostic is triggered when the type of an expression or pattern does not match
 // the expected type.
-pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
+pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Diagnostic {
     let display_range = adjusted_display_range(ctx, d.expr_or_pat, &|node| {
         let Either::Left(expr) = node else { return None };
         let salient_token_range = match expr {
@@ -39,7 +39,7 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
         cov_mark::hit!(type_mismatch_range_adjustment);
         Some(salient_token_range)
     });
-    let mut diag = Diagnostic::new(
+    Diagnostic::new(
         DiagnosticCode::RustcHardError("E0308"),
         format!(
             "expected {}, found {}",
@@ -52,14 +52,10 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
         ),
         display_range,
     )
-    .with_fixes(fixes(ctx, d));
-    if diag.fixes.is_some() {
-        diag.experimental = false;
-    }
-    diag
+    .with_fixes(fixes(ctx, d))
 }
 
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> {
     let mut fixes = Vec::new();
 
     if let Some(expr_ptr) = d.expr_or_pat.value.cast::<ast::Expr>() {
@@ -76,7 +72,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assi
 
 fn add_reference(
     ctx: &DiagnosticsContext<'_>,
-    d: &hir::TypeMismatch,
+    d: &hir::TypeMismatch<'_>,
     expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
@@ -98,7 +94,7 @@ fn add_reference(
 
 fn add_missing_ok_or_some(
     ctx: &DiagnosticsContext<'_>,
-    d: &hir::TypeMismatch,
+    d: &hir::TypeMismatch<'_>,
     expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
@@ -188,7 +184,7 @@ fn add_missing_ok_or_some(
 
 fn remove_unnecessary_wrapper(
     ctx: &DiagnosticsContext<'_>,
-    d: &hir::TypeMismatch,
+    d: &hir::TypeMismatch<'_>,
     expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
@@ -271,7 +267,7 @@ fn remove_unnecessary_wrapper(
 
 fn remove_semicolon(
     ctx: &DiagnosticsContext<'_>,
-    d: &hir::TypeMismatch,
+    d: &hir::TypeMismatch<'_>,
     expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
@@ -301,7 +297,7 @@ fn remove_semicolon(
 
 fn str_ref_to_owned(
     ctx: &DiagnosticsContext<'_>,
-    d: &hir::TypeMismatch,
+    d: &hir::TypeMismatch<'_>,
     expr_ptr: &InFile<AstPtr<ast::Expr>>,
     acc: &mut Vec<Assist>,
 ) -> Option<()> {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
index 1915a88dd00..8d427702690 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -20,7 +20,7 @@ use syntax::AstNode;
 // Diagnostic: typed-hole
 //
 // This diagnostic is triggered when an underscore expression is used in an invalid position.
-pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Diagnostic {
+pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) -> Diagnostic {
     let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into()));
     let (message, fixes) = if d.expected.is_unknown() {
         ("`_` expressions may only appear on the left-hand side of an assignment".to_owned(), None)
@@ -41,7 +41,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di
         .with_fixes(fixes)
 }
 
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) -> Option<Vec<Assist>> {
     let db = ctx.sema.db;
     let root = db.parse_or_expand(d.expr.file_id);
     let (original_range, _) =
@@ -61,7 +61,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
     };
     let paths = term_search(&term_search_ctx);
 
-    let mut formatter = |_: &hir::Type| String::from("_");
+    let mut formatter = |_: &hir::Type<'_>| String::from("_");
 
     let assists: Vec<Assist> = d
         .expected
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 0649c97f820..69015898967 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -25,7 +25,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran
 // This diagnostic is triggered if a field does not exist on a given type.
 pub(crate) fn unresolved_field(
     ctx: &DiagnosticsContext<'_>,
-    d: &hir::UnresolvedField,
+    d: &hir::UnresolvedField<'_>,
 ) -> Diagnostic {
     let method_suffix = if d.method_with_same_name_exists {
         ", but a method with a similar name exists"
@@ -54,7 +54,7 @@ pub(crate) fn unresolved_field(
     .with_fixes(fixes(ctx, d))
 }
 
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option<Vec<Assist>> {
     let mut fixes = Vec::new();
     if d.method_with_same_name_exists {
         fixes.extend(method_fix(ctx, &d.expr));
@@ -64,7 +64,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<A
 }
 
 // FIXME: Add Snippet Support
-fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Assist> {
+fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option<Assist> {
     // Get the FileRange of the invalid field access
     let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
     let expr = d.expr.value.to_node(&root).left()?;
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index 00c2a8c4c46..1f2d671249d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -18,7 +18,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran
 // This diagnostic is triggered if a method does not exist on a given type.
 pub(crate) fn unresolved_method(
     ctx: &DiagnosticsContext<'_>,
-    d: &hir::UnresolvedMethodCall,
+    d: &hir::UnresolvedMethodCall<'_>,
 ) -> Diagnostic {
     let suffix = if d.field_with_same_name.is_some() {
         ", but a field with a similar name exists"
@@ -49,7 +49,7 @@ pub(crate) fn unresolved_method(
     .with_fixes(fixes(ctx, d))
 }
 
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall<'_>) -> Option<Vec<Assist>> {
     let field_fix = if let Some(ty) = &d.field_with_same_name {
         field_fix(ctx, d, ty)
     } else {
@@ -72,8 +72,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<
 
 fn field_fix(
     ctx: &DiagnosticsContext<'_>,
-    d: &hir::UnresolvedMethodCall,
-    ty: &hir::Type,
+    d: &hir::UnresolvedMethodCall<'_>,
+    ty: &hir::Type<'_>,
 ) -> Option<Assist> {
     if !ty.impls_fnonce(ctx.sema.db) {
         return None;
@@ -107,7 +107,10 @@ fn field_fix(
     })
 }
 
-fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<Assist> {
+fn assoc_func_fix(
+    ctx: &DiagnosticsContext<'_>,
+    d: &hir::UnresolvedMethodCall<'_>,
+) -> Option<Assist> {
     if let Some(f) = d.assoc_func_with_same_name {
         let db = ctx.sema.db;
 
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
index 43c56ac8bec..e4b20f3f1aa 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
@@ -110,7 +110,7 @@ pub struct SsrMatches {
 pub struct MatchFinder<'db> {
     /// Our source of information about the user's code.
     sema: Semantics<'db, ide_db::RootDatabase>,
-    rules: Vec<ResolvedRule>,
+    rules: Vec<ResolvedRule<'db>>,
     resolution_scope: resolving::ResolutionScope<'db>,
     restrict_ranges: Vec<ide_db::FileRange>,
 }
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
index cff4eede042..b350315ba54 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
@@ -84,12 +84,12 @@ pub(crate) struct MatchFailed {
 /// Checks if `code` matches the search pattern found in `search_scope`, returning information about
 /// the match, if it does. Since we only do matching in this module and searching is done by the
 /// parent module, we don't populate nested matches.
-pub(crate) fn get_match(
+pub(crate) fn get_match<'db>(
     debug_active: bool,
-    rule: &ResolvedRule,
+    rule: &ResolvedRule<'db>,
     code: &SyntaxNode,
     restrict_range: &Option<FileRange>,
-    sema: &Semantics<'_, ide_db::RootDatabase>,
+    sema: &Semantics<'db, ide_db::RootDatabase>,
 ) -> Result<Match, MatchFailed> {
     record_match_fails_reasons_scope(debug_active, || {
         Matcher::try_match(rule, code, restrict_range, sema)
@@ -102,7 +102,7 @@ struct Matcher<'db, 'sema> {
     /// If any placeholders come from anywhere outside of this range, then the match will be
     /// rejected.
     restrict_range: Option<FileRange>,
-    rule: &'sema ResolvedRule,
+    rule: &'sema ResolvedRule<'db>,
 }
 
 /// Which phase of matching we're currently performing. We do two phases because most attempted
@@ -117,7 +117,7 @@ enum Phase<'a> {
 
 impl<'db, 'sema> Matcher<'db, 'sema> {
     fn try_match(
-        rule: &ResolvedRule,
+        rule: &ResolvedRule<'db>,
         code: &SyntaxNode,
         restrict_range: &Option<FileRange>,
         sema: &'sema Semantics<'db, ide_db::RootDatabase>,
@@ -535,7 +535,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
     fn attempt_match_ufcs_to_method_call(
         &self,
         phase: &mut Phase<'_>,
-        pattern_ufcs: &UfcsCallInfo,
+        pattern_ufcs: &UfcsCallInfo<'db>,
         code: &ast::MethodCallExpr,
     ) -> Result<(), MatchFailed> {
         use ast::HasArgList;
@@ -597,7 +597,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
     fn attempt_match_ufcs_to_ufcs(
         &self,
         phase: &mut Phase<'_>,
-        pattern_ufcs: &UfcsCallInfo,
+        pattern_ufcs: &UfcsCallInfo<'db>,
         code: &ast::CallExpr,
     ) -> Result<(), MatchFailed> {
         use ast::HasArgList;
@@ -615,7 +615,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
     /// times. Returns the number of times it needed to be dereferenced.
     fn check_expr_type(
         &self,
-        pattern_type: &hir::Type,
+        pattern_type: &hir::Type<'db>,
         expr: &ast::Expr,
     ) -> Result<usize, MatchFailed> {
         use hir::HirDisplay;
@@ -656,10 +656,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
 }
 
 impl Match {
-    fn render_template_paths(
+    fn render_template_paths<'db>(
         &mut self,
-        template: &ResolvedPattern,
-        sema: &Semantics<'_, ide_db::RootDatabase>,
+        template: &ResolvedPattern<'db>,
+        sema: &Semantics<'db, ide_db::RootDatabase>,
     ) -> Result<(), MatchFailed> {
         let module = sema
             .scope(&self.matched_node)
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
index 3c92697926f..752edd6535a 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
@@ -14,21 +14,21 @@ use crate::{Match, SsrMatches, fragments, resolving::ResolvedRule};
 /// Returns a text edit that will replace each match in `matches` with its corresponding replacement
 /// template. Placeholders in the template will have been substituted with whatever they matched to
 /// in the original code.
-pub(crate) fn matches_to_edit(
-    db: &dyn hir::db::ExpandDatabase,
+pub(crate) fn matches_to_edit<'db>(
+    db: &'db dyn hir::db::ExpandDatabase,
     matches: &SsrMatches,
     file_src: &str,
-    rules: &[ResolvedRule],
+    rules: &[ResolvedRule<'db>],
 ) -> TextEdit {
     matches_to_edit_at_offset(db, matches, file_src, 0.into(), rules)
 }
 
-fn matches_to_edit_at_offset(
-    db: &dyn hir::db::ExpandDatabase,
+fn matches_to_edit_at_offset<'db>(
+    db: &'db dyn hir::db::ExpandDatabase,
     matches: &SsrMatches,
     file_src: &str,
     relative_start: TextSize,
-    rules: &[ResolvedRule],
+    rules: &[ResolvedRule<'db>],
 ) -> TextEdit {
     let mut edit_builder = TextEdit::builder();
     for m in &matches.matches {
@@ -40,12 +40,12 @@ fn matches_to_edit_at_offset(
     edit_builder.finish()
 }
 
-struct ReplacementRenderer<'a> {
-    db: &'a dyn hir::db::ExpandDatabase,
+struct ReplacementRenderer<'a, 'db> {
+    db: &'db dyn hir::db::ExpandDatabase,
     match_info: &'a Match,
     file_src: &'a str,
-    rules: &'a [ResolvedRule],
-    rule: &'a ResolvedRule,
+    rules: &'a [ResolvedRule<'db>],
+    rule: &'a ResolvedRule<'db>,
     out: String,
     // Map from a range within `out` to a token in `template` that represents a placeholder. This is
     // used to validate that the generated source code doesn't split any placeholder expansions (see
@@ -58,11 +58,11 @@ struct ReplacementRenderer<'a> {
     edition: Edition,
 }
 
-fn render_replace(
-    db: &dyn hir::db::ExpandDatabase,
+fn render_replace<'db>(
+    db: &'db dyn hir::db::ExpandDatabase,
     match_info: &Match,
     file_src: &str,
-    rules: &[ResolvedRule],
+    rules: &[ResolvedRule<'db>],
     edition: Edition,
 ) -> String {
     let rule = &rules[match_info.rule_index];
@@ -89,7 +89,7 @@ fn render_replace(
     renderer.out
 }
 
-impl ReplacementRenderer<'_> {
+impl<'db> ReplacementRenderer<'_, 'db> {
     fn render_node_children(&mut self, node: &SyntaxNode) {
         for node_or_token in node.children_with_tokens() {
             self.render_node_or_token(&node_or_token);
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs
index a687db4bf58..8f28a1cd3a6 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs
@@ -15,18 +15,18 @@ pub(crate) struct ResolutionScope<'db> {
     node: SyntaxNode,
 }
 
-pub(crate) struct ResolvedRule {
-    pub(crate) pattern: ResolvedPattern,
-    pub(crate) template: Option<ResolvedPattern>,
+pub(crate) struct ResolvedRule<'db> {
+    pub(crate) pattern: ResolvedPattern<'db>,
+    pub(crate) template: Option<ResolvedPattern<'db>>,
     pub(crate) index: usize,
 }
 
-pub(crate) struct ResolvedPattern {
+pub(crate) struct ResolvedPattern<'db> {
     pub(crate) placeholders_by_stand_in: FxHashMap<SmolStr, parsing::Placeholder>,
     pub(crate) node: SyntaxNode,
     // Paths in `node` that we've resolved.
     pub(crate) resolved_paths: FxHashMap<SyntaxNode, ResolvedPath>,
-    pub(crate) ufcs_function_calls: FxHashMap<SyntaxNode, UfcsCallInfo>,
+    pub(crate) ufcs_function_calls: FxHashMap<SyntaxNode, UfcsCallInfo<'db>>,
     pub(crate) contains_self: bool,
 }
 
@@ -36,18 +36,18 @@ pub(crate) struct ResolvedPath {
     pub(crate) depth: u32,
 }
 
-pub(crate) struct UfcsCallInfo {
+pub(crate) struct UfcsCallInfo<'db> {
     pub(crate) call_expr: ast::CallExpr,
     pub(crate) function: hir::Function,
-    pub(crate) qualifier_type: Option<hir::Type>,
+    pub(crate) qualifier_type: Option<hir::Type<'db>>,
 }
 
-impl ResolvedRule {
+impl<'db> ResolvedRule<'db> {
     pub(crate) fn new(
         rule: parsing::ParsedRule,
-        resolution_scope: &ResolutionScope<'_>,
+        resolution_scope: &ResolutionScope<'db>,
         index: usize,
-    ) -> Result<ResolvedRule, SsrError> {
+    ) -> Result<ResolvedRule<'db>, SsrError> {
         let resolver =
             Resolver { resolution_scope, placeholders_by_stand_in: rule.placeholders_by_stand_in };
         let resolved_template = match rule.template {
@@ -74,8 +74,8 @@ struct Resolver<'a, 'db> {
     placeholders_by_stand_in: FxHashMap<SmolStr, parsing::Placeholder>,
 }
 
-impl Resolver<'_, '_> {
-    fn resolve_pattern_tree(&self, pattern: SyntaxNode) -> Result<ResolvedPattern, SsrError> {
+impl<'db> Resolver<'_, 'db> {
+    fn resolve_pattern_tree(&self, pattern: SyntaxNode) -> Result<ResolvedPattern<'db>, SsrError> {
         use syntax::ast::AstNode;
         use syntax::{SyntaxElement, T};
         let mut resolved_paths = FxHashMap::default();
@@ -250,7 +250,7 @@ impl<'db> ResolutionScope<'db> {
         }
     }
 
-    fn qualifier_type(&self, path: &SyntaxNode) -> Option<hir::Type> {
+    fn qualifier_type(&self, path: &SyntaxNode) -> Option<hir::Type<'db>> {
         use syntax::ast::AstNode;
         if let Some(path) = ast::Path::cast(path.clone()) {
             if let Some(qualifier) = path.qualifier() {
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs
index 9afbedbb1ab..99a98fb2a71 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/search.rs
@@ -21,13 +21,13 @@ pub(crate) struct UsageCache {
     usages: Vec<(Definition, UsageSearchResult)>,
 }
 
-impl MatchFinder<'_> {
+impl<'db> MatchFinder<'db> {
     /// Adds all matches for `rule` to `matches_out`. Matches may overlap in ways that make
     /// replacement impossible, so further processing is required in order to properly nest matches
     /// and remove overlapping matches. This is done in the `nesting` module.
     pub(crate) fn find_matches_for_rule(
         &self,
-        rule: &ResolvedRule,
+        rule: &ResolvedRule<'db>,
         usage_cache: &mut UsageCache,
         matches_out: &mut Vec<Match>,
     ) {
@@ -49,8 +49,8 @@ impl MatchFinder<'_> {
 
     fn find_matches_for_pattern_tree(
         &self,
-        rule: &ResolvedRule,
-        pattern: &ResolvedPattern,
+        rule: &ResolvedRule<'db>,
+        pattern: &ResolvedPattern<'db>,
         usage_cache: &mut UsageCache,
         matches_out: &mut Vec<Match>,
     ) {
@@ -144,7 +144,7 @@ impl MatchFinder<'_> {
         SearchScope::files(&files)
     }
 
-    fn slow_scan(&self, rule: &ResolvedRule, matches_out: &mut Vec<Match>) {
+    fn slow_scan(&self, rule: &ResolvedRule<'db>, matches_out: &mut Vec<Match>) {
         self.search_files_do(|file_id| {
             let file = self.sema.parse_guess_edition(file_id);
             let code = file.syntax();
@@ -177,7 +177,7 @@ impl MatchFinder<'_> {
     fn slow_scan_node(
         &self,
         code: &SyntaxNode,
-        rule: &ResolvedRule,
+        rule: &ResolvedRule<'db>,
         restrict_range: &Option<FileRange>,
         matches_out: &mut Vec<Match>,
     ) {
@@ -206,7 +206,7 @@ impl MatchFinder<'_> {
 
     fn try_add_match(
         &self,
-        rule: &ResolvedRule,
+        rule: &ResolvedRule<'db>,
         code: &SyntaxNode,
         restrict_range: &Option<FileRange>,
         matches_out: &mut Vec<Match>,
@@ -274,7 +274,7 @@ impl UsageCache {
 /// Returns a path that's suitable for path resolution. We exclude builtin types, since they aren't
 /// something that we can find references to. We then somewhat arbitrarily pick the path that is the
 /// longest as this is hopefully more likely to be less common, making it faster to find.
-fn pick_path_for_usages(pattern: &ResolvedPattern) -> Option<&ResolvedPath> {
+fn pick_path_for_usages<'a>(pattern: &'a ResolvedPattern<'_>) -> Option<&'a ResolvedPath> {
     // FIXME: Take the scope of the resolved path into account. e.g. if there are any paths that are
     // private to the current module, then we definitely would want to pick them over say a path
     // from std. Possibly we should go further than this and intersect the search scopes for all
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
index 1bc28f28b6f..02d96a64732 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
@@ -83,7 +83,7 @@ pub(crate) fn goto_implementation(
     Some(RangeInfo { range, info: navs })
 }
 
-fn impls_for_ty(sema: &Semantics<'_, RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> {
+fn impls_for_ty(sema: &Semantics<'_, RootDatabase>, ty: hir::Type<'_>) -> Vec<NavigationTarget> {
     Impl::all_for_type(sema.db, ty)
         .into_iter()
         .filter_map(|imp| imp.try_to_nav(sema.db))
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
index 9781e7116de..86d72fefe05 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
@@ -38,7 +38,7 @@ pub(crate) fn goto_type_definition(
             }
         }
     };
-    let mut process_ty = |ty: hir::Type| {
+    let mut process_ty = |ty: hir::Type<'_>| {
         // collect from each `ty` into the `res` result vec
         let ty = ty.strip_references();
         ty.walk(db, |t| {
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 5404a9dc2ce..e4d6279759e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -426,7 +426,7 @@ pub(crate) fn hover_for_definition(
     sema: &Semantics<'_, RootDatabase>,
     file_id: FileId,
     def: Definition,
-    subst: Option<GenericSubstitution>,
+    subst: Option<GenericSubstitution<'_>>,
     scope_node: &SyntaxNode,
     macro_arm: Option<u32>,
     render_extras: bool,
@@ -483,10 +483,10 @@ pub(crate) fn hover_for_definition(
     }
 }
 
-fn notable_traits(
-    db: &RootDatabase,
-    ty: &hir::Type,
-) -> Vec<(hir::Trait, Vec<(Option<hir::Type>, hir::Name)>)> {
+fn notable_traits<'db>(
+    db: &'db RootDatabase,
+    ty: &hir::Type<'db>,
+) -> Vec<(hir::Trait, Vec<(Option<hir::Type<'db>>, hir::Name)>)> {
     db.notable_traits_in_deps(ty.krate(db).into())
         .iter()
         .flat_map(|it| &**it)
@@ -567,8 +567,8 @@ fn runnable_action(
 fn goto_type_action_for_def(
     db: &RootDatabase,
     def: Definition,
-    notable_traits: &[(hir::Trait, Vec<(Option<hir::Type>, hir::Name)>)],
-    subst_types: Option<Vec<(hir::Symbol, hir::Type)>>,
+    notable_traits: &[(hir::Trait, Vec<(Option<hir::Type<'_>>, hir::Name)>)],
+    subst_types: Option<Vec<(hir::Symbol, hir::Type<'_>)>>,
     edition: Edition,
 ) -> Option<HoverAction> {
     let mut targets: Vec<hir::ModuleDef> = Vec::new();
@@ -622,7 +622,7 @@ fn goto_type_action_for_def(
 
 fn walk_and_push_ty(
     db: &RootDatabase,
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     push_new_def: &mut dyn FnMut(hir::ModuleDef),
 ) {
     ty.walk(db, |t| {
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index c24864a18bd..670210d4998 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -476,10 +476,10 @@ pub(super) fn definition(
     db: &RootDatabase,
     def: Definition,
     famous_defs: Option<&FamousDefs<'_, '_>>,
-    notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
+    notable_traits: &[(Trait, Vec<(Option<Type<'_>>, Name)>)],
     macro_arm: Option<u32>,
     render_extras: bool,
-    subst_types: Option<&Vec<(Symbol, Type)>>,
+    subst_types: Option<&Vec<(Symbol, Type<'_>)>>,
     config: &HoverConfig,
     edition: Edition,
     display_target: DisplayTarget,
@@ -938,7 +938,7 @@ pub(super) fn literal(
 
 fn render_notable_trait(
     db: &RootDatabase,
-    notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
+    notable_traits: &[(Trait, Vec<(Option<Type<'_>>, Name)>)],
     edition: Edition,
     display_target: DisplayTarget,
 ) -> Option<String> {
@@ -979,7 +979,7 @@ fn render_notable_trait(
 fn type_info(
     sema: &Semantics<'_, RootDatabase>,
     config: &HoverConfig,
-    ty: TypeInfo,
+    ty: TypeInfo<'_>,
     edition: Edition,
     display_target: DisplayTarget,
 ) -> Option<HoverResult> {
@@ -1038,7 +1038,7 @@ fn type_info(
 fn closure_ty(
     sema: &Semantics<'_, RootDatabase>,
     config: &HoverConfig,
-    TypeInfo { original, adjusted }: &TypeInfo,
+    TypeInfo { original, adjusted }: &TypeInfo<'_>,
     edition: Edition,
     display_target: DisplayTarget,
 ) -> Option<HoverResult> {
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index b094b098462..19e5509681a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -722,14 +722,14 @@ impl InlayHintLabelBuilder<'_> {
 fn label_of_ty(
     famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     display_target: DisplayTarget,
 ) -> Option<InlayHintLabel> {
     fn rec(
         sema: &Semantics<'_, RootDatabase>,
         famous_defs: &FamousDefs<'_, '_>,
         mut max_length: Option<usize>,
-        ty: &hir::Type,
+        ty: &hir::Type<'_>,
         label_builder: &mut InlayHintLabelBuilder<'_>,
         config: &InlayHintsConfig,
         display_target: DisplayTarget,
@@ -788,11 +788,11 @@ fn label_of_ty(
 }
 
 /// Checks if the type is an Iterator from std::iter and returns the iterator trait and the item type of the concrete iterator.
-fn hint_iterator(
-    sema: &Semantics<'_, RootDatabase>,
-    famous_defs: &FamousDefs<'_, '_>,
-    ty: &hir::Type,
-) -> Option<(hir::Trait, hir::TypeAlias, hir::Type)> {
+fn hint_iterator<'db>(
+    sema: &Semantics<'db, RootDatabase>,
+    famous_defs: &FamousDefs<'_, 'db>,
+    ty: &hir::Type<'db>,
+) -> Option<(hir::Trait, hir::TypeAlias, hir::Type<'db>)> {
     let db = sema.db;
     let strukt = ty.strip_references().as_adt()?;
     let krate = strukt.module(db).krate();
@@ -826,7 +826,7 @@ fn ty_to_text_edit(
     sema: &Semantics<'_, RootDatabase>,
     config: &InlayHintsConfig,
     node_for_hint: &SyntaxNode,
-    ty: &hir::Type,
+    ty: &hir::Type<'_>,
     offset_to_insert_ty: TextSize,
     additional_edits: &dyn Fn(&mut TextEditBuilder),
     prefix: impl Into<String>,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
index 5ff9fee60ab..5174228466c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
@@ -87,10 +87,10 @@ pub(super) fn hints(
     Some(())
 }
 
-fn get_callable(
-    sema: &Semantics<'_, RootDatabase>,
+fn get_callable<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     expr: &ast::Expr,
-) -> Option<(hir::Callable, ast::ArgList)> {
+) -> Option<(hir::Callable<'db>, ast::ArgList)> {
     match expr {
         ast::Expr::CallExpr(expr) => {
             let descended = sema.descend_node_into_attributes(expr.clone()).pop();
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 82dbcde4c06..b3b8deb61fc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -409,7 +409,7 @@ impl Analysis {
         self.with_db(|db| typing::on_enter(db, position))
     }
 
-    pub const SUPPORTED_TRIGGER_CHARS: &'static str = typing::TRIGGER_CHARS;
+    pub const SUPPORTED_TRIGGER_CHARS: &[char] = typing::TRIGGER_CHARS;
 
     /// Returns an edit which should be applied after a character was typed.
     ///
@@ -421,7 +421,7 @@ impl Analysis {
         char_typed: char,
     ) -> Cancellable<Option<SourceChange>> {
         // Fast path to not even parse the file.
-        if !typing::TRIGGER_CHARS.contains(char_typed) {
+        if !typing::TRIGGER_CHARS.contains(&char_typed) {
             return Ok(None);
         }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index 0e17b355907..e30a3ebefb9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -278,7 +278,7 @@ fn signature_help_for_call(
     }
     res.signature.push(')');
 
-    let mut render = |ret_type: hir::Type| {
+    let mut render = |ret_type: hir::Type<'_>| {
         if !ret_type.is_unit() {
             format_to!(res.signature, " -> {}", ret_type.display(db, display_target));
         }
@@ -597,11 +597,11 @@ fn signature_help_for_tuple_expr(
     Some(res)
 }
 
-fn signature_help_for_record_(
-    sema: &Semantics<'_, RootDatabase>,
+fn signature_help_for_record_<'db>(
+    sema: &Semantics<'db, RootDatabase>,
     field_list_children: SyntaxElementChildren,
     path: &ast::Path,
-    fields2: impl Iterator<Item = (hir::Field, hir::Type)>,
+    fields2: impl Iterator<Item = (hir::Field, hir::Type<'db>)>,
     token: SyntaxToken,
     edition: Edition,
     display_target: DisplayTarget,
@@ -689,13 +689,13 @@ fn signature_help_for_record_(
     Some(res)
 }
 
-fn signature_help_for_tuple_pat_ish(
-    db: &RootDatabase,
+fn signature_help_for_tuple_pat_ish<'db>(
+    db: &'db RootDatabase,
     mut res: SignatureHelp,
     pat: &SyntaxNode,
     token: SyntaxToken,
     mut field_pats: AstChildren<ast::Pat>,
-    fields: impl ExactSizeIterator<Item = hir::Type>,
+    fields: impl ExactSizeIterator<Item = hir::Type<'db>>,
     display_target: DisplayTarget,
 ) -> SignatureHelp {
     let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs
index 4df7e25223d..ed55ac5bf04 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs
@@ -15,6 +15,7 @@
 
 mod on_enter;
 
+use either::Either;
 use hir::EditionedFileId;
 use ide_db::{FilePosition, RootDatabase, base_db::RootQueryDb};
 use span::Edition;
@@ -33,7 +34,7 @@ use crate::SourceChange;
 pub(crate) use on_enter::on_enter;
 
 // Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
-pub(crate) const TRIGGER_CHARS: &str = ".=<>{(|";
+pub(crate) const TRIGGER_CHARS: &[char] = &['.', '=', '<', '>', '{', '(', '|', '+'];
 
 struct ExtendedTextEdit {
     edit: TextEdit,
@@ -66,7 +67,7 @@ pub(crate) fn on_char_typed(
     position: FilePosition,
     char_typed: char,
 ) -> Option<SourceChange> {
-    if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
+    if !TRIGGER_CHARS.contains(&char_typed) {
         return None;
     }
     // FIXME: We need to figure out the edition of the file here, but that means hitting the
@@ -101,6 +102,7 @@ fn on_char_typed_(
         '>' => on_right_angle_typed(&file.tree(), offset),
         '{' | '(' | '<' => on_opening_delimiter_typed(file, offset, char_typed, edition),
         '|' => on_pipe_typed(&file.tree(), offset),
+        '+' => on_plus_typed(&file.tree(), offset),
         _ => None,
     }
     .map(conv)
@@ -402,6 +404,28 @@ fn on_pipe_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
     Some(TextEdit::insert(after_lpipe, "|".to_owned()))
 }
 
+fn on_plus_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
+    let plus_token = file.syntax().token_at_offset(offset).right_biased()?;
+    if plus_token.kind() != SyntaxKind::PLUS {
+        return None;
+    }
+    let mut ancestors = plus_token.parent_ancestors();
+    ancestors.next().and_then(ast::TypeBoundList::cast)?;
+    let trait_type =
+        ancestors.next().and_then(<Either<ast::DynTraitType, ast::ImplTraitType>>::cast)?;
+    let kind = ancestors.next()?.kind();
+
+    if ast::RefType::can_cast(kind) || ast::PtrType::can_cast(kind) || ast::RetType::can_cast(kind)
+    {
+        let mut builder = TextEdit::builder();
+        builder.insert(trait_type.syntax().text_range().start(), "(".to_owned());
+        builder.insert(trait_type.syntax().text_range().end(), ")".to_owned());
+        Some(builder.finish())
+    } else {
+        None
+    }
+}
+
 /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
 fn on_right_angle_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
     let file_text = file.syntax().text();
@@ -1597,4 +1621,64 @@ fn foo() {
 "#,
         );
     }
+
+    #[test]
+    fn adds_parentheses_around_trait_object_in_ref_type() {
+        type_char(
+            '+',
+            r#"
+fn foo(x: &dyn A$0) {}
+"#,
+            r#"
+fn foo(x: &(dyn A+)) {}
+"#,
+        );
+        type_char(
+            '+',
+            r#"
+fn foo(x: &'static dyn A$0B) {}
+"#,
+            r#"
+fn foo(x: &'static (dyn A+B)) {}
+"#,
+        );
+        type_char_noop(
+            '+',
+            r#"
+fn foo(x: &(dyn A$0)) {}
+"#,
+        );
+        type_char_noop(
+            '+',
+            r#"
+fn foo(x: Box<dyn A$0>) {}
+"#,
+        );
+    }
+
+    #[test]
+    fn adds_parentheses_around_trait_object_in_ptr_type() {
+        type_char(
+            '+',
+            r#"
+fn foo(x: *const dyn A$0) {}
+"#,
+            r#"
+fn foo(x: *const (dyn A+)) {}
+"#,
+        );
+    }
+
+    #[test]
+    fn adds_parentheses_around_trait_object_in_return_type() {
+        type_char(
+            '+',
+            r#"
+fn foo(x: fn() -> dyn A$0) {}
+"#,
+            r#"
+fn foo(x: fn() -> (dyn A+)) {}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
index 140ae4265be..63701a4d15e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
@@ -107,7 +107,7 @@ pub(crate) fn view_memory_layout(
     fn read_layout(
         nodes: &mut Vec<MemoryLayoutNode>,
         db: &RootDatabase,
-        ty: &Type,
+        ty: &Type<'_>,
         layout: &Layout,
         parent_idx: usize,
         display_target: DisplayTarget,
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index d5cbb7328c1..adc581309d1 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -496,6 +496,7 @@ define_symbols! {
     vectorcall,
     wasm,
     win64,
+    args,
     array,
     boxed_slice,
     completions,
diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index 89b8631cd25..52f59679b58 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -42,7 +42,7 @@ pub fn load_workspace_at(
     root: &Path,
     cargo_config: &CargoConfig,
     load_config: &LoadCargoConfig,
-    progress: &dyn Fn(String),
+    progress: &(dyn Fn(String) + Sync),
 ) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroClient>)> {
     let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root));
     let root = ProjectManifest::discover_single(&root)?;
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
index e0c38ccf333..4435376eab6 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
@@ -20,7 +20,9 @@ use toolchain::Tool;
 
 use crate::{
     CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot,
-    TargetKind, utf8_stdout,
+    TargetKind,
+    toolchain_info::{QueryConfig, version},
+    utf8_stdout,
 };
 
 /// Output of the build script and proc-macro building steps for a workspace.
@@ -446,10 +448,30 @@ impl WorkspaceBuildScripts {
             }
         };
 
-        if config.wrap_rustc_in_build_scripts {
+        // If [`--compile-time-deps` flag](https://github.com/rust-lang/cargo/issues/14434) is
+        // available in current toolchain's cargo, use it to build compile time deps only.
+        const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION: semver::Version = semver::Version {
+            major: 1,
+            minor: 90,
+            patch: 0,
+            pre: semver::Prerelease::EMPTY,
+            build: semver::BuildMetadata::EMPTY,
+        };
+
+        let query_config = QueryConfig::Cargo(sysroot, manifest_path);
+        let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
+        let cargo_comp_time_deps_available =
+            toolchain.is_some_and(|v| v >= COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION);
+
+        if cargo_comp_time_deps_available {
+            cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly");
+            cmd.arg("-Zunstable-options");
+            cmd.arg("--compile-time-deps");
+        } else if config.wrap_rustc_in_build_scripts {
             // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
             // that to compile only proc macros and build scripts during the initial
             // `cargo check`.
+            // We don't need this if we are using `--compile-time-deps` flag.
             let myself = std::env::current_exe()?;
             cmd.env("RUSTC_WRAPPER", myself);
             cmd.env("RA_RUSTC_WRAPPER", "1");
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index 1fade7b3323..58507418e4d 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -7,16 +7,25 @@ use anyhow::Context;
 use base_db::Env;
 use cargo_metadata::{CargoOpt, MetadataCommand};
 use la_arena::{Arena, Idx};
-use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
+use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
 use rustc_hash::{FxHashMap, FxHashSet};
 use serde_derive::Deserialize;
 use serde_json::from_value;
 use span::Edition;
+use stdx::process::spawn_with_streaming_output;
 use toolchain::Tool;
 
 use crate::{CfgOverrides, InvocationStrategy};
 use crate::{ManifestPath, Sysroot};
 
+const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH: semver::Version = semver::Version {
+    major: 1,
+    minor: 82,
+    patch: 0,
+    pre: semver::Prerelease::EMPTY,
+    build: semver::BuildMetadata::EMPTY,
+};
+
 /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
 /// workspace. It pretty closely mirrors `cargo metadata` output.
 ///
@@ -290,6 +299,13 @@ pub struct CargoMetadataConfig {
     pub extra_args: Vec<String>,
     /// Extra env vars to set when invoking the cargo command
     pub extra_env: FxHashMap<String, Option<String>>,
+    /// The target dir for this workspace load.
+    pub target_dir: Utf8PathBuf,
+    /// What kind of metadata are we fetching: workspace, rustc, or sysroot.
+    pub kind: &'static str,
+    /// The toolchain version, if known.
+    /// Used to conditionally enable unstable cargo features.
+    pub toolchain_version: Option<semver::Version>,
 }
 
 // Deserialize helper for the cargo metadata
@@ -382,28 +398,74 @@ impl CargoWorkspace {
                 config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
             );
         }
+        if no_deps {
+            other_options.push("--no-deps".to_owned());
+        }
+
+        let mut using_lockfile_copy = false;
         // The manifest is a rust file, so this means its a script manifest
         if cargo_toml.is_rust_manifest() {
-            // Deliberately don't set up RUSTC_BOOTSTRAP or a nightly override here, the user should
-            // opt into it themselves.
             other_options.push("-Zscript".to_owned());
+        } else if config
+            .toolchain_version
+            .as_ref()
+            .is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
+        {
+            let lockfile = <_ as AsRef<Utf8Path>>::as_ref(cargo_toml).with_extension("lock");
+            let target_lockfile = config
+                .target_dir
+                .join("rust-analyzer")
+                .join("metadata")
+                .join(config.kind)
+                .join("Cargo.lock");
+            match std::fs::copy(&lockfile, &target_lockfile) {
+                Ok(_) => {
+                    using_lockfile_copy = true;
+                    other_options.push("--lockfile-path".to_owned());
+                    other_options.push(target_lockfile.to_string());
+                }
+                Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
+                    // There exists no lockfile yet
+                    using_lockfile_copy = true;
+                    other_options.push("--lockfile-path".to_owned());
+                    other_options.push(target_lockfile.to_string());
+                }
+                Err(e) => {
+                    tracing::warn!(
+                        "Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}",
+                    );
+                }
+            }
         }
-        if locked {
-            other_options.push("--locked".to_owned());
+        if using_lockfile_copy {
+            other_options.push("-Zunstable-options".to_owned());
+            meta.env("RUSTC_BOOTSTRAP", "1");
         }
-        if no_deps {
-            other_options.push("--no-deps".to_owned());
+        // No need to lock it if we copied the lockfile, we won't modify the original after all/
+        // This way cargo cannot error out on us if the lockfile requires updating.
+        if !using_lockfile_copy && locked {
+            other_options.push("--locked".to_owned());
         }
         meta.other_options(other_options);
 
         // FIXME: Fetching metadata is a slow process, as it might require
         // calling crates.io. We should be reporting progress here, but it's
         // unclear whether cargo itself supports it.
-        progress("metadata".to_owned());
-
-        (|| -> anyhow::Result<(_, _)> {
-            let output = meta.cargo_command().output()?;
+        progress("cargo metadata: started".to_owned());
+
+        let res = (|| -> anyhow::Result<(_, _)> {
+            let mut errored = false;
+            let output =
+                spawn_with_streaming_output(meta.cargo_command(), &mut |_| (), &mut |line| {
+                    errored = errored || line.starts_with("error") || line.starts_with("warning");
+                    if errored {
+                        progress("cargo metadata: ?".to_owned());
+                        return;
+                    }
+                    progress(format!("cargo metadata: {line}"));
+                })?;
             if !output.status.success() {
+                progress(format!("cargo metadata: failed {}", output.status));
                 let error = cargo_metadata::Error::CargoMetadata {
                     stderr: String::from_utf8(output.stderr)?,
                 }
@@ -416,8 +478,8 @@ impl CargoWorkspace {
                         current_dir,
                         config,
                         sysroot,
-                        locked,
                         true,
+                        locked,
                         progress,
                     ) {
                         return Ok((metadata, Some(error)));
@@ -431,7 +493,9 @@ impl CargoWorkspace {
                 .ok_or(cargo_metadata::Error::NoJson)?;
             Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
         })()
-        .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))
+        .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()));
+        progress("cargo metadata: finished".to_owned());
+        res
     }
 
     pub fn new(
diff --git a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
index 4f43be2f38f..fba8cc9709d 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
@@ -1,7 +1,7 @@
 //! See [`ManifestPath`].
 use std::{borrow::Borrow, fmt, ops};
 
-use paths::{AbsPath, AbsPathBuf};
+use paths::{AbsPath, AbsPathBuf, Utf8Path};
 
 /// More or less [`AbsPathBuf`] with non-None parent.
 ///
@@ -78,6 +78,12 @@ impl AsRef<std::ffi::OsStr> for ManifestPath {
     }
 }
 
+impl AsRef<Utf8Path> for ManifestPath {
+    fn as_ref(&self) -> &Utf8Path {
+        self.file.as_ref()
+    }
+}
+
 impl Borrow<AbsPath> for ManifestPath {
     fn borrow(&self) -> &AbsPath {
         self.file.borrow()
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index ebd86e3dc48..4b34fc00711 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -4,6 +4,7 @@
 //! but we can't process `.rlib` and need source code instead. The source code
 //! is typically installed with `rustup component add rust-src` command.
 
+use core::fmt;
 use std::{env, fs, ops::Not, path::Path, process::Command};
 
 use anyhow::{Result, format_err};
@@ -34,6 +35,19 @@ pub enum RustLibSrcWorkspace {
     Empty,
 }
 
+impl fmt::Display for RustLibSrcWorkspace {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            RustLibSrcWorkspace::Workspace(ws) => write!(f, "workspace {}", ws.workspace_root()),
+            RustLibSrcWorkspace::Json(json) => write!(f, "json {}", json.manifest_or_root()),
+            RustLibSrcWorkspace::Stitched(stitched) => {
+                write!(f, "stitched with {} crates", stitched.crates.len())
+            }
+            RustLibSrcWorkspace::Empty => write!(f, "empty"),
+        }
+    }
+}
+
 impl Sysroot {
     pub const fn empty() -> Sysroot {
         Sysroot {
@@ -195,6 +209,8 @@ impl Sysroot {
     pub fn load_workspace(
         &self,
         sysroot_source_config: &RustSourceWorkspaceConfig,
+        current_dir: &AbsPath,
+        progress: &dyn Fn(String),
     ) -> Option<RustLibSrcWorkspace> {
         assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
         let Self { root: _, rust_lib_src_root: Some(src_root), workspace: _, error: _ } = self
@@ -204,10 +220,16 @@ impl Sysroot {
         if let RustSourceWorkspaceConfig::CargoMetadata(cargo_config) = sysroot_source_config {
             let library_manifest = ManifestPath::try_from(src_root.join("Cargo.toml")).unwrap();
             if fs::metadata(&library_manifest).is_ok() {
-                if let Some(loaded) =
-                    self.load_library_via_cargo(library_manifest, src_root, cargo_config)
-                {
-                    return Some(loaded);
+                match self.load_library_via_cargo(
+                    &library_manifest,
+                    current_dir,
+                    cargo_config,
+                    progress,
+                ) {
+                    Ok(loaded) => return Some(loaded),
+                    Err(e) => {
+                        tracing::error!("`cargo metadata` failed on `{library_manifest}` : {e}")
+                    }
                 }
             }
             tracing::debug!("Stitching sysroot library: {src_root}");
@@ -293,10 +315,11 @@ impl Sysroot {
 
     fn load_library_via_cargo(
         &self,
-        library_manifest: ManifestPath,
-        rust_lib_src_dir: &AbsPathBuf,
+        library_manifest: &ManifestPath,
+        current_dir: &AbsPath,
         cargo_config: &CargoMetadataConfig,
-    ) -> Option<RustLibSrcWorkspace> {
+        progress: &dyn Fn(String),
+    ) -> Result<RustLibSrcWorkspace> {
         tracing::debug!("Loading library metadata: {library_manifest}");
         let mut cargo_config = cargo_config.clone();
         // the sysroot uses `public-dependency`, so we make cargo think it's a nightly
@@ -305,22 +328,16 @@ impl Sysroot {
             Some("nightly".to_owned()),
         );
 
-        let (mut res, _) = match CargoWorkspace::fetch_metadata(
-            &library_manifest,
-            rust_lib_src_dir,
+        let (mut res, _) = CargoWorkspace::fetch_metadata(
+            library_manifest,
+            current_dir,
             &cargo_config,
             self,
             false,
             // Make sure we never attempt to write to the sysroot
             true,
-            &|_| (),
-        ) {
-            Ok(it) => it,
-            Err(e) => {
-                tracing::error!("`cargo metadata` failed on `{library_manifest}` : {e}");
-                return None;
-            }
-        };
+            progress,
+        )?;
 
         // Patch out `rustc-std-workspace-*` crates to point to the real crates.
         // This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing.
@@ -371,8 +388,9 @@ impl Sysroot {
             res.packages.remove(idx);
         });
 
-        let cargo_workspace = CargoWorkspace::new(res, library_manifest, Default::default(), true);
-        Some(RustLibSrcWorkspace::Workspace(cargo_workspace))
+        let cargo_workspace =
+            CargoWorkspace::new(res, library_manifest.clone(), Default::default(), true);
+        Ok(RustLibSrcWorkspace::Workspace(cargo_workspace))
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index c69891b7463..4f11af2d06c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -1,3 +1,5 @@
+use std::env::temp_dir;
+
 use base_db::{CrateGraphBuilder, ProcMacroPaths};
 use cargo_metadata::Metadata;
 use cfg::{CfgAtom, CfgDiff};
@@ -235,11 +237,18 @@ fn smoke_test_real_sysroot_cargo() {
         AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))),
         &Default::default(),
     );
-    let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo());
+    let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo"));
+    std::fs::create_dir_all(&cwd).unwrap();
+    let loaded_sysroot =
+        sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), &cwd, &|_| ());
     if let Some(loaded_sysroot) = loaded_sysroot {
         sysroot.set_workspace(loaded_sysroot);
     }
-    assert!(matches!(sysroot.workspace(), RustLibSrcWorkspace::Workspace(_)));
+    assert!(
+        matches!(sysroot.workspace(), RustLibSrcWorkspace::Workspace(_)),
+        "got {}",
+        sysroot.workspace()
+    );
     let project_workspace = ProjectWorkspace {
         kind: ProjectWorkspaceKind::Cargo {
             cargo: cargo_workspace,
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index eec0077ea6e..a6743a32b14 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -12,7 +12,7 @@ use base_db::{
 };
 use cfg::{CfgAtom, CfgDiff, CfgOptions};
 use intern::{Symbol, sym};
-use paths::{AbsPath, AbsPathBuf};
+use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
 use rustc_hash::{FxHashMap, FxHashSet};
 use semver::Version;
 use span::{Edition, FileId};
@@ -170,7 +170,7 @@ impl ProjectWorkspace {
     pub fn load(
         manifest: ProjectManifest,
         config: &CargoConfig,
-        progress: &dyn Fn(String),
+        progress: &(dyn Fn(String) + Sync),
     ) -> anyhow::Result<ProjectWorkspace> {
         ProjectWorkspace::load_inner(&manifest, config, progress)
             .with_context(|| format!("Failed to load the project at {manifest}"))
@@ -179,7 +179,7 @@ impl ProjectWorkspace {
     fn load_inner(
         manifest: &ProjectManifest,
         config: &CargoConfig,
-        progress: &dyn Fn(String),
+        progress: &(dyn Fn(String) + Sync),
     ) -> anyhow::Result<ProjectWorkspace> {
         let res = match manifest {
             ProjectManifest::ProjectJson(project_json) => {
@@ -206,9 +206,10 @@ impl ProjectWorkspace {
     fn load_cargo(
         cargo_toml: &ManifestPath,
         config: &CargoConfig,
-        progress: &dyn Fn(String),
+        progress: &(dyn Fn(String) + Sync),
     ) -> Result<ProjectWorkspace, anyhow::Error> {
         progress("Discovering sysroot".to_owned());
+        let workspace_dir = cargo_toml.parent();
         let CargoConfig {
             features,
             rustc_source,
@@ -224,15 +225,9 @@ impl ProjectWorkspace {
             ..
         } = config;
         let mut sysroot = match (sysroot, sysroot_src) {
-            (Some(RustLibSource::Discover), None) => {
-                Sysroot::discover(cargo_toml.parent(), extra_env)
-            }
+            (Some(RustLibSource::Discover), None) => Sysroot::discover(workspace_dir, extra_env),
             (Some(RustLibSource::Discover), Some(sysroot_src)) => {
-                Sysroot::discover_with_src_override(
-                    cargo_toml.parent(),
-                    extra_env,
-                    sysroot_src.clone(),
-                )
+                Sysroot::discover_with_src_override(workspace_dir, extra_env, sysroot_src.clone())
             }
             (Some(RustLibSource::Path(path)), None) => {
                 Sysroot::discover_rust_lib_src_dir(path.clone())
@@ -248,24 +243,23 @@ impl ProjectWorkspace {
         let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml);
         let targets =
             target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
+        let toolchain = version::get(toolchain_config, extra_env)
+            .inspect_err(|e| {
+                tracing::error!(%e,
+                    "failed fetching toolchain version for {cargo_toml:?} workspace"
+                )
+            })
+            .ok()
+            .flatten();
+
+        let target_dir =
+            config.target_dir.clone().unwrap_or_else(|| workspace_dir.join("target").into());
 
         // We spawn a bunch of processes to query various information about the workspace's
         // toolchain and sysroot
         // We can speed up loading a bit by spawning all of these processes in parallel (especially
         // on systems were process spawning is delayed)
         let join = thread::scope(|s| {
-            let workspace_dir = cargo_toml.parent();
-            let toolchain = s.spawn(|| {
-                version::get(toolchain_config, extra_env)
-                    .inspect_err(|e| {
-                        tracing::error!(%e,
-                            "failed fetching toolchain version for {cargo_toml:?} workspace"
-                        )
-                    })
-                    .ok()
-                    .flatten()
-            });
-
             let rustc_cfg = s.spawn(|| {
                 rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
             });
@@ -300,11 +294,14 @@ impl ProjectWorkspace {
                             targets: targets.clone(),
                             extra_args: extra_args.clone(),
                             extra_env: extra_env.clone(),
+                            target_dir: target_dir.clone(),
+                            toolchain_version: toolchain.clone(),
+                            kind: "rustc-dev"
                         },
                         &sysroot,
                         *no_deps,
-                        false,
-                        &|_| (),
+                        true,
+                        progress,
                     ) {
                         Ok((meta, _error)) => {
                             let workspace = CargoWorkspace::new(
@@ -343,22 +340,31 @@ impl ProjectWorkspace {
                         targets: targets.clone(),
                         extra_args: extra_args.clone(),
                         extra_env: extra_env.clone(),
+                        target_dir: target_dir.clone(),
+                        toolchain_version: toolchain.clone(),
+                        kind: "workspace",
                     },
                     &sysroot,
                     *no_deps,
                     false,
-                    &|_| (),
+                    progress,
                 )
             });
             let loaded_sysroot = s.spawn(|| {
-                sysroot.load_workspace(&RustSourceWorkspaceConfig::CargoMetadata(
-                    sysroot_metadata_config(extra_env, &targets),
-                ))
+                sysroot.load_workspace(
+                    &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
+                        config,
+                        &targets,
+                        toolchain.clone(),
+                        target_dir.clone(),
+                    )),
+                    workspace_dir,
+                    progress,
+                )
             });
             let cargo_config_extra_env =
                 s.spawn(|| cargo_config_env(cargo_toml, extra_env, &sysroot));
             thread::Result::Ok((
-                toolchain.join()?,
                 rustc_cfg.join()?,
                 data_layout.join()?,
                 rustc_dir.join()?,
@@ -368,18 +374,11 @@ impl ProjectWorkspace {
             ))
         });
 
-        let (
-            toolchain,
-            rustc_cfg,
-            data_layout,
-            rustc,
-            loaded_sysroot,
-            cargo_metadata,
-            cargo_config_extra_env,
-        ) = match join {
-            Ok(it) => it,
-            Err(e) => std::panic::resume_unwind(e),
-        };
+        let (rustc_cfg, data_layout, rustc, loaded_sysroot, cargo_metadata, cargo_config_extra_env) =
+            match join {
+                Ok(it) => it,
+                Err(e) => std::panic::resume_unwind(e),
+            };
 
         let (meta, error) = cargo_metadata.with_context(|| {
             format!(
@@ -388,6 +387,7 @@ impl ProjectWorkspace {
         })?;
         let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_config_extra_env, false);
         if let Some(loaded_sysroot) = loaded_sysroot {
+            tracing::info!(src_root = ?sysroot.rust_lib_src_root(), root = %loaded_sysroot, "Loaded sysroot");
             sysroot.set_workspace(loaded_sysroot);
         }
 
@@ -411,7 +411,7 @@ impl ProjectWorkspace {
     pub fn load_inline(
         mut project_json: ProjectJson,
         config: &CargoConfig,
-        progress: &dyn Fn(String),
+        progress: &(dyn Fn(String) + Sync),
     ) -> ProjectWorkspace {
         progress("Discovering sysroot".to_owned());
         let mut sysroot =
@@ -423,14 +423,13 @@ impl ProjectWorkspace {
         let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref());
         let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
             .unwrap_or_default();
+        let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
 
         // We spawn a bunch of processes to query various information about the workspace's
         // toolchain and sysroot
         // We can speed up loading a bit by spawning all of these processes in parallel (especially
         // on systems were process spawning is delayed)
         let join = thread::scope(|s| {
-            let toolchain =
-                s.spawn(|| version::get(query_config, &config.extra_env).ok().flatten());
             let rustc_cfg = s.spawn(|| {
                 rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
             });
@@ -442,24 +441,35 @@ impl ProjectWorkspace {
                 )
             });
             let loaded_sysroot = s.spawn(|| {
+                let project_root = project_json.project_root();
                 if let Some(sysroot_project) = sysroot_project {
-                    sysroot.load_workspace(&RustSourceWorkspaceConfig::Json(*sysroot_project))
+                    sysroot.load_workspace(
+                        &RustSourceWorkspaceConfig::Json(*sysroot_project),
+                        project_root,
+                        progress,
+                    )
                 } else {
-                    sysroot.load_workspace(&RustSourceWorkspaceConfig::CargoMetadata(
-                        sysroot_metadata_config(&config.extra_env, &targets),
-                    ))
+                    let target_dir = config
+                        .target_dir
+                        .clone()
+                        .unwrap_or_else(|| project_root.join("target").into());
+                    sysroot.load_workspace(
+                        &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
+                            config,
+                            &targets,
+                            toolchain.clone(),
+                            target_dir,
+                        )),
+                        project_root,
+                        progress,
+                    )
                 }
             });
 
-            thread::Result::Ok((
-                toolchain.join()?,
-                rustc_cfg.join()?,
-                data_layout.join()?,
-                loaded_sysroot.join()?,
-            ))
+            thread::Result::Ok((rustc_cfg.join()?, data_layout.join()?, loaded_sysroot.join()?))
         });
 
-        let (toolchain, rustc_cfg, target_layout, loaded_sysroot) = match join {
+        let (rustc_cfg, target_layout, loaded_sysroot) = match join {
             Ok(it) => it,
             Err(e) => std::panic::resume_unwind(e),
         };
@@ -497,9 +507,17 @@ impl ProjectWorkspace {
             .unwrap_or_default();
         let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
         let data_layout = target_data_layout::get(query_config, None, &config.extra_env);
-        let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::CargoMetadata(
-            sysroot_metadata_config(&config.extra_env, &targets),
-        ));
+        let target_dir = config.target_dir.clone().unwrap_or_else(|| dir.join("target").into());
+        let loaded_sysroot = sysroot.load_workspace(
+            &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
+                config,
+                &targets,
+                toolchain.clone(),
+                target_dir.clone(),
+            )),
+            dir,
+            &|_| (),
+        );
         if let Some(loaded_sysroot) = loaded_sysroot {
             sysroot.set_workspace(loaded_sysroot);
         }
@@ -512,6 +530,9 @@ impl ProjectWorkspace {
                 targets,
                 extra_args: config.extra_args.clone(),
                 extra_env: config.extra_env.clone(),
+                target_dir,
+                toolchain_version: toolchain.clone(),
+                kind: "detached-file",
             },
             &sysroot,
             config.no_deps,
@@ -1804,13 +1825,18 @@ fn add_dep_inner(graph: &mut CrateGraphBuilder, from: CrateBuilderId, dep: Depen
 }
 
 fn sysroot_metadata_config(
-    extra_env: &FxHashMap<String, Option<String>>,
+    config: &CargoConfig,
     targets: &[String],
+    toolchain_version: Option<Version>,
+    target_dir: Utf8PathBuf,
 ) -> CargoMetadataConfig {
     CargoMetadataConfig {
         features: Default::default(),
         targets: targets.to_vec(),
         extra_args: Default::default(),
-        extra_env: extra_env.clone(),
+        extra_env: config.extra_env.clone(),
+        target_dir,
+        toolchain_version,
+        kind: "sysroot",
     }
 }
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs
index baac3e8bbfe..c151cca0727 100644
--- a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs
+++ b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs
@@ -74,8 +74,8 @@ impl ToTokens for TrackedQuery {
                 quote! {
                     #sig {
                         #annotation
-                        fn #shim(
-                            db: &dyn #trait_name,
+                        fn #shim<'db>(
+                            db: &'db dyn #trait_name,
                             _input: #input_struct_name,
                             #(#pat_and_tys),*
                         ) #ret
@@ -88,8 +88,8 @@ impl ToTokens for TrackedQuery {
                 quote! {
                     #sig {
                         #annotation
-                        fn #shim(
-                            db: &dyn #trait_name,
+                        fn #shim<'db>(
+                            db: &'db dyn #trait_name,
                             #(#pat_and_tys),*
                         ) #ret
                             #invoke_block
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 12b393b80c0..0ee01982fea 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -532,7 +532,7 @@ impl flags::AnalysisStats {
                 }
 
                 let todo = syntax::ast::make::ext::expr_todo().to_string();
-                let mut formatter = |_: &hir::Type| todo.clone();
+                let mut formatter = |_: &hir::Type<'_>| todo.clone();
                 let mut syntax_hit_found = false;
                 for term in found_terms {
                     let generated = term
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index e3b372c9149..740fcd81ea9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -9,7 +9,6 @@ use hir::{ChangeWithProcMacros, Crate};
 use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
 use ide_db::base_db;
 use itertools::Either;
-use paths::Utf8PathBuf;
 use profile::StopWatch;
 use project_model::toolchain_info::{QueryConfig, target_data_layout};
 use project_model::{
@@ -64,9 +63,9 @@ fn detect_errors_from_rustc_stderr_file(p: PathBuf) -> FxHashMap<DiagnosticCode,
 
 impl Tester {
     fn new() -> Result<Self> {
-        let mut path = std::env::temp_dir();
-        path.push("ra-rustc-test.rs");
-        let tmp_file = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).unwrap()).unwrap();
+        let mut path = AbsPathBuf::assert_utf8(std::env::temp_dir());
+        path.push("ra-rustc-test");
+        let tmp_file = path.join("ra-rustc-test.rs");
         std::fs::write(&tmp_file, "")?;
         let cargo_config = CargoConfig {
             sysroot: Some(RustLibSource::Discover),
@@ -76,7 +75,8 @@ impl Tester {
         };
 
         let mut sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env);
-        let loaded_sysroot = sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo());
+        let loaded_sysroot =
+            sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), &path, &|_| ());
         if let Some(loaded_sysroot) = loaded_sysroot {
             sysroot.set_workspace(loaded_sysroot);
         }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 762b63f54b0..05e1b832cd1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -452,6 +452,8 @@ config_data! {
         assist_emitMustUse: bool               = false,
         /// Placeholder expression to use for missing expressions in assists.
         assist_expressionFillDefault: ExprFillDefaultDef              = ExprFillDefaultDef::Todo,
+        /// When inserting a type (e.g. in "fill match arms" assist), prefer to use `Self` over the type name where possible.
+        assist_preferSelf: bool = false,
         /// Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check.
         assist_termSearch_borrowcheck: bool = true,
         /// Term search fuel in "units of work" for assists (Defaults to 1800).
@@ -760,7 +762,11 @@ config_data! {
         /// though Cargo might be the eventual consumer.
         vfs_extraIncludes: Vec<String> = vec![],
 
-        /// Exclude imports from symbol search.
+        /// Exclude all imports from workspace symbol search.
+        ///
+        /// In addition to regular imports (which are always excluded),
+        /// this option removes public imports (better known as re-exports)
+        /// and removes imports that rename the imported symbol.
         workspace_symbol_search_excludeImports: bool = false,
         /// Workspace symbol search kind.
         workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes,
@@ -1505,6 +1511,7 @@ impl Config {
                 ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
                 ExprFillDefaultDef::Underscore => ExprFillDefaultMode::Underscore,
             },
+            prefer_self_ty: *self.assist_preferSelf(source_root),
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index b7373f274f0..200e972e428 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -239,7 +239,7 @@ pub(crate) fn handle_did_change_configuration(
                         let (config, e, _) = config.apply_change(change);
                         this.config_errors = e.is_empty().not().then_some(e);
 
-                        // Client config changes neccesitates .update_config method to be called.
+                        // Client config changes necessitates .update_config method to be called.
                         this.update_configuration(config);
                     }
                 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
index 418fe957590..04e31f37fd2 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
@@ -77,7 +77,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
             _ => Some(OneOf::Left(false)),
         },
         document_on_type_formatting_provider: Some({
-            let mut chars = ide::Analysis::SUPPORTED_TRIGGER_CHARS.chars();
+            let mut chars = ide::Analysis::SUPPORTED_TRIGGER_CHARS.iter();
             DocumentOnTypeFormattingOptions {
                 first_trigger_character: chars.next().unwrap().to_string(),
                 more_trigger_character: Some(chars.map(|c| c.to_string()).collect()),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 4677880daaf..189d95ec7ed 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -114,6 +114,16 @@ impl GlobalState {
                 Durability::HIGH,
             );
         }
+
+        if self.config.cargo(None) != old_config.cargo(None) {
+            let req = FetchWorkspaceRequest { path: None, force_crate_graph_reload: false };
+            self.fetch_workspaces_queue.request_op("cargo config changed".to_owned(), req)
+        }
+
+        if self.config.cfg_set_test(None) != old_config.cfg_set_test(None) {
+            let req = FetchWorkspaceRequest { path: None, force_crate_graph_reload: false };
+            self.fetch_workspaces_queue.request_op("cfg_set_test config changed".to_owned(), req)
+        }
     }
 
     pub(crate) fn current_status(&self) -> lsp_ext::ServerStatusParams {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
index 9c0bc33af64..e7528dbc939 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
@@ -103,6 +103,7 @@ impl CargoTestHandle {
     ) -> std::io::Result<Self> {
         let mut cmd = toolchain::command(Tool::Cargo.path(), root, &options.extra_env);
         cmd.env("RUSTC_BOOTSTRAP", "1");
+        cmd.arg("--color=always");
         cmd.arg("test");
 
         cmd.arg("--package");
diff --git a/src/tools/rust-analyzer/crates/stdx/src/lib.rs b/src/tools/rust-analyzer/crates/stdx/src/lib.rs
index 9a292eacd7f..978c50d807b 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/lib.rs
@@ -13,6 +13,7 @@ pub mod panic_context;
 pub mod process;
 pub mod rand;
 pub mod thread;
+pub mod variance;
 
 pub use itertools;
 
diff --git a/src/tools/rust-analyzer/crates/stdx/src/variance.rs b/src/tools/rust-analyzer/crates/stdx/src/variance.rs
new file mode 100644
index 00000000000..8465d72bf37
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/stdx/src/variance.rs
@@ -0,0 +1,270 @@
+//! This is a copy of [`std::marker::variance`].
+
+use std::any::type_name;
+use std::cmp::Ordering;
+use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::marker::PhantomData;
+
+macro_rules! first_token {
+    ($first:tt $($rest:tt)*) => {
+        $first
+    };
+}
+macro_rules! phantom_type {
+    ($(
+        $(#[$attr:meta])*
+        pub struct $name:ident <$t:ident> ($($inner:tt)*);
+    )*) => {$(
+        $(#[$attr])*
+        pub struct $name<$t>($($inner)*) where T: ?Sized;
+
+        impl<T> $name<T>
+            where T: ?Sized
+        {
+            /// Constructs a new instance of the variance marker.
+            pub const fn new() -> Self {
+                Self(PhantomData)
+            }
+        }
+
+        impl<T> self::sealed::Sealed for $name<T> where T: ?Sized {
+            const VALUE: Self = Self::new();
+        }
+
+        impl<T> Variance for $name<T> where T: ?Sized {}
+
+        impl<T> Default for $name<T>
+            where T: ?Sized
+        {
+            fn default() -> Self {
+                Self(PhantomData)
+            }
+        }
+
+        impl<T> fmt::Debug for $name<T>
+            where T: ?Sized
+        {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                write!(f, "{}<{}>", stringify!($name), type_name::<T>())
+            }
+        }
+
+        impl<T> Clone for $name<T>
+            where T: ?Sized
+        {
+            fn clone(&self) -> Self {
+                *self
+            }
+        }
+
+        impl<T> Copy for $name<T> where T: ?Sized {}
+
+        impl<T> PartialEq for $name<T>
+            where T: ?Sized
+        {
+            fn eq(&self, _: &Self) -> bool {
+                true
+            }
+        }
+
+        impl<T> Eq for $name<T> where T: ?Sized {}
+
+        #[allow(clippy::non_canonical_partial_ord_impl)]
+        impl<T> PartialOrd for $name<T>
+            where T: ?Sized
+        {
+            fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
+                Some(Ordering::Equal)
+            }
+        }
+
+        impl<T> Ord for $name<T>
+            where T: ?Sized
+        {
+            fn cmp(&self, _: &Self) -> Ordering {
+                Ordering::Equal
+            }
+        }
+
+        impl<T> Hash for $name<T>
+            where T: ?Sized
+        {
+            fn hash<H: Hasher>(&self, _: &mut H) {}
+        }
+    )*};
+}
+
+macro_rules! phantom_lifetime {
+    ($(
+        $(#[$attr:meta])*
+        pub struct $name:ident <$lt:lifetime> ($($inner:tt)*);
+    )*) => {$(
+        $(#[$attr])*
+
+        #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+        pub struct $name<$lt>($($inner)*);
+
+        impl $name<'_> {
+            /// Constructs a new instance of the variance marker.
+            pub const fn new() -> Self {
+                Self(first_token!($($inner)*)(PhantomData))
+            }
+        }
+
+        impl self::sealed::Sealed for $name<'_> {
+            const VALUE: Self = Self::new();
+        }
+
+        impl Variance for $name<'_> {}
+
+        impl fmt::Debug for $name<'_> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                write!(f, "{}", stringify!($name))
+            }
+        }
+    )*};
+}
+
+phantom_lifetime! {
+    /// Zero-sized type used to mark a lifetime as covariant.
+    ///
+    /// Covariant lifetimes must live at least as long as declared. See [the reference][1] for more
+    /// information.
+    ///
+    /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
+    ///
+    /// ## Layout
+    ///
+    /// For all `'a`, the following are guaranteed:
+    /// * `size_of::<PhantomCovariantLifetime<'a>>() == 0`
+    /// * `align_of::<PhantomCovariantLifetime<'a>>() == 1`
+
+    pub struct PhantomCovariantLifetime<'a>(PhantomCovariant<&'a ()>);
+    /// Zero-sized type used to mark a lifetime as contravariant.
+    ///
+    /// Contravariant lifetimes must live at most as long as declared. See [the reference][1] for
+    /// more information.
+    ///
+    /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
+    ///
+    /// ## Layout
+    ///
+    /// For all `'a`, the following are guaranteed:
+    /// * `size_of::<PhantomContravariantLifetime<'a>>() == 0`
+    /// * `align_of::<PhantomContravariantLifetime<'a>>() == 1`
+
+    pub struct PhantomContravariantLifetime<'a>(PhantomContravariant<&'a ()>);
+    /// Zero-sized type used to mark a lifetime as invariant.
+    ///
+    /// Invariant lifetimes must be live for the exact length declared, neither shorter nor longer.
+    /// See [the reference][1] for more information.
+    ///
+    /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
+    ///
+    /// ## Layout
+    ///
+    /// For all `'a`, the following are guaranteed:
+    /// * `size_of::<PhantomInvariantLifetime<'a>>() == 0`
+    /// * `align_of::<PhantomInvariantLifetime<'a>>() == 1`
+
+    pub struct PhantomInvariantLifetime<'a>(PhantomInvariant<&'a ()>);
+
+}
+
+phantom_type! {
+    /// Zero-sized type used to mark a type parameter as covariant.
+    ///
+    /// Types used as part of the return value from a function are covariant. If the type is _also_
+    /// passed as a parameter then it is [invariant][PhantomInvariant]. See [the reference][1] for
+    /// more information.
+    ///
+    /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
+    ///
+    /// ## Layout
+    ///
+    /// For all `T`, the following are guaranteed:
+    /// * `size_of::<PhantomCovariant<T>>() == 0`
+    /// * `align_of::<PhantomCovariant<T>>() == 1`
+
+    pub struct PhantomCovariant<T>(PhantomData<fn() -> T>);
+    /// Zero-sized type used to mark a type parameter as contravariant.
+    ///
+    /// Types passed as arguments to a function are contravariant. If the type is _also_ part of the
+    /// return value from a function then it is [invariant][PhantomInvariant]. See [the
+    /// reference][1] for more information.
+    ///
+    /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
+    ///
+    /// ## Layout
+    ///
+    /// For all `T`, the following are guaranteed:
+    /// * `size_of::<PhantomContravariant<T>>() == 0`
+    /// * `align_of::<PhantomContravariant<T>>() == 1`
+
+    pub struct PhantomContravariant<T>(PhantomData<fn(T)>);
+    /// Zero-sized type used to mark a type parameter as invariant.
+    ///
+    /// Types that are both passed as an argument _and_ used as part of the return value from a
+    /// function are invariant. See [the reference][1] for more information.
+    ///
+    /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance
+    ///
+    /// ## Layout
+    ///
+    /// For all `T`, the following are guaranteed:
+    /// * `size_of::<PhantomInvariant<T>>() == 0`
+    /// * `align_of::<PhantomInvariant<T>>() == 1`
+
+    pub struct PhantomInvariant<T>(PhantomData<fn(T) -> T>);
+
+}
+
+mod sealed {
+
+    pub trait Sealed {
+        const VALUE: Self;
+    }
+}
+/// A marker trait for phantom variance types.
+pub trait Variance: sealed::Sealed + Default {}
+/// Construct a variance marker; equivalent to [`Default::default`].
+///
+/// This type can be any of the following. You generally should not need to explicitly name the
+/// type, however.
+///
+/// - [`PhantomCovariant`]
+/// - [`PhantomContravariant`]
+/// - [`PhantomInvariant`]
+/// - [`PhantomCovariantLifetime`]
+/// - [`PhantomContravariantLifetime`]
+/// - [`PhantomInvariantLifetime`]
+///
+/// # Example
+///
+/// ```rust
+/// #![feature(phantom_variance_markers)]
+///
+/// use core::marker::{PhantomCovariant, variance};
+///
+/// struct BoundFn<F, P, R>
+/// where
+///     F: Fn(P) -> R,
+/// {
+///     function: F,
+///     parameter: P,
+///     return_value: PhantomCovariant<R>,
+/// }
+///
+/// let bound_fn = BoundFn {
+///     function: core::convert::identity,
+///     parameter: 5u8,
+///     return_value: variance(),
+/// };
+/// ```
+pub const fn variance<T>() -> T
+where
+    T: Variance,
+{
+    T::VALUE
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index fab4cb287c3..955aadaa25d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -134,6 +134,13 @@ pub fn name_ref(name_ref: &str) -> ast::NameRef {
         }
     }
 }
+pub fn name_ref_self_ty() -> ast::NameRef {
+    quote! {
+        NameRef {
+            [Self]
+        }
+    }
+}
 fn raw_ident_esc(ident: &str) -> &'static str {
     if is_raw_identifier(ident, Edition::CURRENT) { "r#" } else { "" }
 }
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index 7240069753e..1d821e96e55 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -412,22 +412,36 @@ impl MiniCore {
         }
 
         let mut active_regions = Vec::new();
+        let mut inactive_regions = Vec::new();
         let mut seen_regions = Vec::new();
         for line in lines {
             let trimmed = line.trim();
             if let Some(region) = trimmed.strip_prefix("// region:") {
-                active_regions.push(region);
-                continue;
+                if let Some(region) = region.strip_prefix('!') {
+                    inactive_regions.push(region);
+                    continue;
+                } else {
+                    active_regions.push(region);
+                    continue;
+                }
             }
             if let Some(region) = trimmed.strip_prefix("// endregion:") {
-                let prev = active_regions.pop().unwrap();
+                let (prev, region) = if let Some(region) = region.strip_prefix('!') {
+                    (inactive_regions.pop().unwrap(), region)
+                } else {
+                    (active_regions.pop().unwrap(), region)
+                };
                 assert_eq!(prev, region, "unbalanced region pairs");
                 continue;
             }
 
-            let mut line_region = false;
-            if let Some(idx) = trimmed.find("// :") {
-                line_region = true;
+            let mut active_line_region = false;
+            let mut inactive_line_region = false;
+            if let Some(idx) = trimmed.find("// :!") {
+                inactive_line_region = true;
+                inactive_regions.push(&trimmed[idx + "// :!".len()..]);
+            } else if let Some(idx) = trimmed.find("// :") {
+                active_line_region = true;
                 active_regions.push(&trimmed[idx + "// :".len()..]);
             }
 
@@ -438,18 +452,30 @@ impl MiniCore {
                 seen_regions.push(region);
                 keep &= self.has_flag(region);
             }
+            for &region in &inactive_regions {
+                assert!(!region.starts_with(' '), "region marker starts with a space: {region:?}");
+                self.assert_valid_flag(region);
+                seen_regions.push(region);
+                keep &= !self.has_flag(region);
+            }
 
             if keep {
                 buf.push_str(line);
             }
-            if line_region {
+            if active_line_region {
                 active_regions.pop().unwrap();
             }
+            if inactive_line_region {
+                inactive_regions.pop().unwrap();
+            }
         }
 
         if !active_regions.is_empty() {
             panic!("unclosed regions: {active_regions:?} Add an `endregion` comment");
         }
+        if !inactive_regions.is_empty() {
+            panic!("unclosed regions: {inactive_regions:?} Add an `endregion` comment");
+        }
 
         for flag in &self.valid_flags {
             if !seen_regions.iter().any(|it| it == flag) {
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index 4bdd791eb16..d13a81d287f 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -31,6 +31,7 @@
 //!     eq: sized
 //!     error: fmt
 //!     fmt: option, result, transmute, coerce_unsized, copy, clone, derive
+//!     fmt_before_1_89_0: fmt
 //!     fn: tuple
 //!     from: sized, result
 //!     future: pin
@@ -1175,6 +1176,7 @@ pub mod fmt {
             }
         }
 
+        // region:fmt_before_1_89_0
         #[lang = "format_unsafe_arg"]
         pub struct UnsafeArg {
             _private: (),
@@ -1185,6 +1187,7 @@ pub mod fmt {
                 UnsafeArg { _private: () }
             }
         }
+        // endregion:fmt_before_1_89_0
     }
 
     #[derive(Copy, Clone)]
@@ -1204,6 +1207,7 @@ pub mod fmt {
             Arguments { pieces, fmt: None, args: &[] }
         }
 
+        // region:fmt_before_1_89_0
         pub fn new_v1_formatted(
             pieces: &'a [&'static str],
             args: &'a [rt::Argument<'a>],
@@ -1212,6 +1216,17 @@ pub mod fmt {
         ) -> Arguments<'a> {
             Arguments { pieces, fmt: Some(fmt), args }
         }
+        // endregion:fmt_before_1_89_0
+
+        // region:!fmt_before_1_89_0
+        pub unsafe fn new_v1_formatted(
+            pieces: &'a [&'static str],
+            args: &'a [rt::Argument<'a>],
+            fmt: &'a [rt::Placeholder],
+        ) -> Arguments<'a> {
+            Arguments { pieces, fmt: Some(fmt), args }
+        }
+        // endregion:!fmt_before_1_89_0
 
         pub const fn as_str(&self) -> Option<&'static str> {
             match (self.pieces, self.args) {
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
index 4eb9cfc4e5b..9404b1454a0 100644
--- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -13,6 +13,13 @@ Default: `"todo"`
 Placeholder expression to use for missing expressions in assists.
 
 
+## rust-analyzer.assist.preferSelf {#assist.preferSelf}
+
+Default: `false`
+
+When inserting a type (e.g. in "fill match arms" assist), prefer to use `Self` over the type name where possible.
+
+
 ## rust-analyzer.assist.termSearch.borrowcheck {#assist.termSearch.borrowcheck}
 
 Default: `true`
@@ -1535,7 +1542,11 @@ https://github.com/facebook/buck2/tree/main/integrations/rust-project.
 
 Default: `false`
 
-Exclude imports from symbol search.
+Exclude all imports from workspace symbol search.
+
+In addition to regular imports (which are always excluded),
+this option removes public imports (better known as re-exports)
+and removes imports that rename the imported symbol.
 
 
 ## rust-analyzer.workspace.symbol.search.kind {#workspace.symbol.search.kind}
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index dcdb4fe30ee..26a21c1468d 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -683,6 +683,16 @@
             {
                 "title": "assist",
                 "properties": {
+                    "rust-analyzer.assist.preferSelf": {
+                        "markdownDescription": "When inserting a type (e.g. in \"fill match arms\" assist), prefer to use `Self` over the type name where possible.",
+                        "default": false,
+                        "type": "boolean"
+                    }
+                }
+            },
+            {
+                "title": "assist",
+                "properties": {
                     "rust-analyzer.assist.termSearch.borrowcheck": {
                         "markdownDescription": "Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check.",
                         "default": true,
@@ -2895,7 +2905,7 @@
                 "title": "workspace",
                 "properties": {
                     "rust-analyzer.workspace.symbol.search.excludeImports": {
-                        "markdownDescription": "Exclude imports from symbol search.",
+                        "markdownDescription": "Exclude all imports from workspace symbol search.\n\nIn addition to regular imports (which are always excluded),\nthis option removes public imports (better known as re-exports)\nand removes imports that rename the imported symbol.",
                         "default": false,
                         "type": "boolean"
                     }
diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts
index f36e18a73da..d2dc740c09b 100644
--- a/src/tools/rust-analyzer/editors/code/src/config.ts
+++ b/src/tools/rust-analyzer/editors/code/src/config.ts
@@ -20,15 +20,9 @@ export class Config {
     configureLang: vscode.Disposable | undefined;
 
     readonly rootSection = "rust-analyzer";
-    private readonly requiresServerReloadOpts = [
-        "cargo",
-        "procMacro",
-        "serverPath",
-        "server",
-        "files",
-        "cfg",
-        "showSyntaxTree",
-    ].map((opt) => `${this.rootSection}.${opt}`);
+    private readonly requiresServerReloadOpts = ["server", "files", "showSyntaxTree"].map(
+        (opt) => `${this.rootSection}.${opt}`,
+    );
 
     private readonly requiresWindowReloadOpts = ["testExplorer"].map(
         (opt) => `${this.rootSection}.${opt}`,
@@ -208,7 +202,7 @@ export class Config {
     }
 
     get serverPath() {
-        return this.get<null | string>("server.path") ?? this.get<null | string>("serverPath");
+        return this.get<null | string>("server.path");
     }
 
     get serverExtraEnv(): Env {
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index af0dd5c9acd..a454087b0cd 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-7c10378e1fee5ddc6573b916aeb884ab10e0de17
+27733d46d79f4eb92e240fbba502c43022665735
diff --git a/src/tools/rust-installer/install-template.sh b/src/tools/rust-installer/install-template.sh
index f7f408be882..337aaa95b9a 100644
--- a/src/tools/rust-installer/install-template.sh
+++ b/src/tools/rust-installer/install-template.sh
@@ -160,7 +160,7 @@ valopt() {
     local doc="$*"
     if [ $HELP -eq 0 ]
     then
-        local uop=$(echo $op | tr 'a-z-' 'A-Z_')
+        local uop=$(echo $op | tr '[a-z]-' '[A-Z]_')
         local v="CFG_${uop}"
         eval $v="$default"
         for arg in $CFG_ARGS
@@ -206,8 +206,8 @@ opt() {
         do
             if [ "$arg" = "--${flag}-${op}" ]
             then
-                op=$(echo $op | tr 'a-z-' 'A-Z_')
-                flag=$(echo $flag | tr 'a-z' 'A-Z')
+                op=$(echo $op | tr '[a-z]-' '[A-Z]_')
+                flag=$(echo $flag | tr '[a-z]' '[A-Z]')
                 local v="CFG_${flag}_${op}"
                 eval $v=1
                 putvar $v
@@ -235,7 +235,7 @@ flag() {
         do
             if [ "$arg" = "--${op}" ]
             then
-                op=$(echo $op | tr 'a-z-' 'A-Z_')
+                op=$(echo $op | tr '[a-z]-' '[A-Z]_')
                 local v="CFG_${op}"
                 eval $v=1
                 putvar $v
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index ed67fa7d1a9..7bf3bb195ee 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -4,9 +4,9 @@ version = 4
 
 [[package]]
 name = "adler2"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
 
 [[package]]
 name = "aho-corasick"
@@ -156,18 +156,18 @@ checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
 
 [[package]]
 name = "cc"
-version = "1.2.26"
+version = "1.2.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac"
+checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
 dependencies = [
  "shlex",
 ]
 
 [[package]]
 name = "cfg-if"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
 
 [[package]]
 name = "chrono"
@@ -185,9 +185,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.39"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f"
+checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -195,9 +195,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.39"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51"
+checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
 dependencies = [
  "anstream",
  "anstyle",
@@ -208,18 +208,18 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.5.52"
+version = "4.5.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a554639e42d0c838336fc4fbedb9e2df3ad1fa4acda149f9126b4ccfcd7900f"
+checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677"
 dependencies = [
  "clap",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.5.32"
+version = "4.5.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
+checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -229,9 +229,9 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.7.4"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
 
 [[package]]
 name = "colorchoice"
@@ -527,11 +527,11 @@ dependencies = [
 
 [[package]]
 name = "getopts"
-version = "0.2.21"
+version = "0.2.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
 dependencies = [
- "unicode-width 0.1.14",
+ "unicode-width",
 ]
 
 [[package]]
@@ -768,9 +768,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
 
 [[package]]
 name = "jiff"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93"
+checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
 dependencies = [
  "jiff-static",
  "log",
@@ -781,9 +781,9 @@ dependencies = [
 
 [[package]]
 name = "jiff-static"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442"
+checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -808,9 +808,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libc"
-version = "0.2.172"
+version = "0.2.174"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
 
 [[package]]
 name = "linereader"
@@ -966,15 +966,15 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.7.4"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.8"
+version = "0.8.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
 dependencies = [
  "adler2",
 ]
@@ -1325,7 +1325,7 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6d5b8e8a7c20c600f9b98cbf46b64e63d5c9e69deb98cee1ff264de9f1dda5d"
 dependencies = [
- "unicode-width 0.2.0",
+ "unicode-width",
 ]
 
 [[package]]
@@ -1345,9 +1345,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.12"
+version = "0.5.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
+checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
 dependencies = [
  "bitflags 2.9.1",
 ]
@@ -1548,9 +1548,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.101"
+version = "2.0.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
+checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1760,15 +1760,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
 
 [[package]]
 name = "unicode-width"
-version = "0.1.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
-
-[[package]]
-name = "unicode-width"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
 
 [[package]]
 name = "url"
@@ -1940,9 +1934,9 @@ dependencies = [
 
 [[package]]
 name = "windows-link"
-version = "0.1.1"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
 
 [[package]]
 name = "windows-result"
@@ -2037,9 +2031,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
 name = "winnow"
-version = "0.7.10"
+version = "0.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
+checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
 dependencies = [
  "memchr",
 ]
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index be6b483bfff..08aedff2b20 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -2289,8 +2289,10 @@ fn rewrite_expr_addrof(
 ) -> RewriteResult {
     let operator_str = match (mutability, borrow_kind) {
         (ast::Mutability::Not, ast::BorrowKind::Ref) => "&",
+        (ast::Mutability::Not, ast::BorrowKind::Pin) => "&pin const ",
         (ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ",
         (ast::Mutability::Mut, ast::BorrowKind::Ref) => "&mut ",
+        (ast::Mutability::Mut, ast::BorrowKind::Pin) => "&pin mut ",
         (ast::Mutability::Mut, ast::BorrowKind::Raw) => "&raw mut ",
     };
     rewrite_unary_prefix(context, operator_str, expr, shape)
diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs
index b741dd9b5da..788fed013ad 100644
--- a/src/tools/rustfmt/src/imports.rs
+++ b/src/tools/rustfmt/src/imports.rs
@@ -184,7 +184,7 @@ impl UseSegment {
         modsep: bool,
     ) -> Option<UseSegment> {
         let name = rewrite_ident(context, path_seg.ident);
-        if name.is_empty() || name == "{{root}}" {
+        if name.is_empty() {
             return None;
         }
         let kind = match name {
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index afd847f9515..10e2809e58b 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -5,7 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
 use rustc_data_structures::sync::IntoDynSyncSend;
 use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
 use rustc_errors::registry::Registry;
-use rustc_errors::translation::Translate;
+use rustc_errors::translation::Translator;
 use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
 use rustc_session::parse::ParseSess as RawParseSess;
 use rustc_span::{
@@ -47,16 +47,6 @@ impl SilentOnIgnoredFilesEmitter {
     }
 }
 
-impl Translate for SilentOnIgnoredFilesEmitter {
-    fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
-        self.emitter.fluent_bundle()
-    }
-
-    fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
-        self.emitter.fallback_fluent_bundle()
-    }
-}
-
 impl Emitter for SilentOnIgnoredFilesEmitter {
     fn source_map(&self) -> Option<&SourceMap> {
         None
@@ -84,6 +74,10 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
         }
         self.handle_non_ignoreable_error(diag, registry);
     }
+
+    fn translator(&self) -> &Translator {
+        self.emitter.translator()
+    }
 }
 
 impl From<Color> for ColorConfig {
@@ -110,23 +104,15 @@ fn default_dcx(
         ColorConfig::Never
     };
 
-    let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-        rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-        false,
-    );
-    let emitter = Box::new(
-        HumanEmitter::new(stderr_destination(emit_color), fallback_bundle)
-            .sm(Some(source_map.clone())),
-    );
-
-    let emitter: Box<DynEmitter> = if !show_parse_errors {
-        Box::new(SilentEmitter {
-            fatal_emitter: emitter,
-            fatal_note: None,
-            emit_fatal_diagnostic: false,
-        })
+    let translator = rustc_driver::default_translator();
+
+    let emitter: Box<DynEmitter> = if show_parse_errors {
+        Box::new(
+            HumanEmitter::new(stderr_destination(emit_color), translator)
+                .sm(Some(source_map.clone())),
+        )
     } else {
-        emitter
+        Box::new(SilentEmitter { translator })
     };
     DiagCtxt::new(Box::new(SilentOnIgnoredFilesEmitter {
         has_non_ignorable_parser_errors: false,
@@ -205,7 +191,7 @@ impl ParseSess {
     }
 
     pub(crate) fn set_silent_emitter(&mut self) {
-        self.raw_psess.dcx().make_silent(None, false);
+        self.raw_psess.dcx().make_silent();
     }
 
     pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
@@ -335,16 +321,6 @@ mod tests {
             num_emitted_errors: Arc<AtomicU32>,
         }
 
-        impl Translate for TestEmitter {
-            fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
-                None
-            }
-
-            fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
-                panic!("test emitter attempted to translate a diagnostic");
-            }
-        }
-
         impl Emitter for TestEmitter {
             fn source_map(&self) -> Option<&SourceMap> {
                 None
@@ -353,6 +329,10 @@ mod tests {
             fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {
                 self.num_emitted_errors.fetch_add(1, Ordering::Release);
             }
+
+            fn translator(&self) -> &Translator {
+                panic!("test emitter attempted to translate a diagnostic");
+            }
         }
 
         fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> DiagInner {
diff --git a/src/tools/rustfmt/tests/source/pin_sugar.rs b/src/tools/rustfmt/tests/source/pin_sugar.rs
index 370dfbc196a..e5b47339b92 100644
--- a/src/tools/rustfmt/tests/source/pin_sugar.rs
+++ b/src/tools/rustfmt/tests/source/pin_sugar.rs
@@ -18,3 +18,13 @@ impl Foo {
 mut self) {}
     fn i(&pin      mut   self) {}
 }
+
+fn borrows() {
+    let mut foo = 0_i32;
+    let x: Pin<&mut _> = & pin 
+    mut    foo;
+
+    let x: Pin<&_> = &
+    pin                const 
+    foo;
+}
diff --git a/src/tools/rustfmt/tests/target/pin_sugar.rs b/src/tools/rustfmt/tests/target/pin_sugar.rs
index 7d04efb1b32..09ad23a5807 100644
--- a/src/tools/rustfmt/tests/target/pin_sugar.rs
+++ b/src/tools/rustfmt/tests/target/pin_sugar.rs
@@ -16,3 +16,10 @@ impl Foo {
     fn h<'a>(&'a pin mut self) {}
     fn i(&pin mut self) {}
 }
+
+fn borrows() {
+    let mut foo = 0_i32;
+    let x: Pin<&mut _> = &pin mut foo;
+
+    let x: Pin<&_> = &pin const foo;
+}
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 170dcd626a2..bf813d2131e 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -206,6 +206,7 @@ const EXCEPTIONS_CRANELIFT: ExceptionList = &[
     ("regalloc2", "Apache-2.0 WITH LLVM-exception"),
     ("target-lexicon", "Apache-2.0 WITH LLVM-exception"),
     ("wasmtime-jit-icache-coherence", "Apache-2.0 WITH LLVM-exception"),
+    ("wasmtime-math", "Apache-2.0 WITH LLVM-exception"),
     // tidy-alphabetical-end
 ];
 
@@ -356,6 +357,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "rand",
     "rand_chacha",
     "rand_core",
+    "rand_xorshift", // dependency for doc-tests in rustc_thread_pool
     "rand_xoshiro",
     "redox_syscall",
     "regex",
@@ -364,7 +366,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "rustc-demangle",
     "rustc-hash",
     "rustc-literal-escaper",
-    "rustc-rayon-core",
     "rustc-stable-hash",
     "rustc_apfloat",
     "rustix",
@@ -373,6 +374,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "scoped-tls",
     "scopeguard",
     "self_cell",
+    "semver",
     "serde",
     "serde_derive",
     "serde_json",
@@ -537,6 +539,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
     "indexmap",
     "libc",
     "libloading",
+    "libm",
     "log",
     "mach2",
     "memchr",
@@ -554,6 +557,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
     "target-lexicon",
     "unicode-ident",
     "wasmtime-jit-icache-coherence",
+    "wasmtime-math",
     "windows-sys",
     "windows-targets",
     "windows_aarch64_gnullvm",
diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs
index e2d1b85797f..bb61412f678 100644
--- a/src/tools/tidy/src/error_codes.rs
+++ b/src/tools/tidy/src/error_codes.rs
@@ -43,9 +43,18 @@ macro_rules! verbose_print {
     };
 }
 
-pub fn check(root_path: &Path, search_paths: &[&Path], verbose: bool, bad: &mut bool) {
+pub fn check(
+    root_path: &Path,
+    search_paths: &[&Path],
+    verbose: bool,
+    ci_info: &crate::CiInfo,
+    bad: &mut bool,
+) {
     let mut errors = Vec::new();
 
+    // Check that no error code explanation was removed.
+    check_removed_error_code_explanation(ci_info, bad);
+
     // Stage 1: create list
     let error_codes = extract_error_codes(root_path, &mut errors);
     if verbose {
@@ -68,6 +77,27 @@ pub fn check(root_path: &Path, search_paths: &[&Path], verbose: bool, bad: &mut
     }
 }
 
+fn check_removed_error_code_explanation(ci_info: &crate::CiInfo, bad: &mut bool) {
+    let Some(base_commit) = &ci_info.base_commit else {
+        eprintln!("Skipping error code explanation removal check");
+        return;
+    };
+    let Some(diff) = crate::git_diff(base_commit, "--name-status") else {
+        *bad = true;
+        eprintln!("removed error code explanation tidy check: Failed to run git diff");
+        return;
+    };
+    if diff.lines().any(|line| {
+        line.starts_with('D') && line.contains("compiler/rustc_error_codes/src/error_codes/")
+    }) {
+        *bad = true;
+        eprintln!("tidy check error: Error code explanations should never be removed!");
+        eprintln!("Take a look at E0001 to see how to handle it.");
+        return;
+    }
+    println!("No error code explanation was removed!");
+}
+
 /// Stage 1: Parses a list of error codes from `error_codes.rs`.
 fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String> {
     let path = root_path.join(Path::new(ERROR_CODES_PATH));
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 045f2f0692a..24356c31da4 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -305,7 +305,6 @@ ui/borrowck/issue-104639-lifetime-order.rs
 ui/borrowck/issue-10876.rs
 ui/borrowck/issue-109271-pass-self-into-closure.rs
 ui/borrowck/issue-111554.rs
-ui/borrowck/issue-114374-invalid-help-fmt-args.rs
 ui/borrowck/issue-11493.rs
 ui/borrowck/issue-115259-suggest-iter-mut.rs
 ui/borrowck/issue-119915-bad-clone-suggestion.rs
@@ -2071,7 +2070,6 @@ ui/issues/issue-32782.rs
 ui/issues/issue-32797.rs
 ui/issues/issue-32805.rs
 ui/issues/issue-3290.rs
-ui/issues/issue-32950.rs
 ui/issues/issue-32995-2.rs
 ui/issues/issue-32995.rs
 ui/issues/issue-33202.rs
@@ -2341,7 +2339,6 @@ ui/issues/issue-49934.rs
 ui/issues/issue-49955.rs
 ui/issues/issue-49973.rs
 ui/issues/issue-50187.rs
-ui/issues/issue-50403.rs
 ui/issues/issue-50411.rs
 ui/issues/issue-50415.rs
 ui/issues/issue-50442.rs
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index e8a12d56335..237737f0f16 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -3,6 +3,12 @@
 //! This library contains the tidy lints and exposes it
 //! to be used by tools.
 
+use std::ffi::OsStr;
+use std::process::Command;
+
+use build_helper::ci::CiEnv;
+use build_helper::git::{GitConfig, get_closest_upstream_commit};
+use build_helper::stage0_parser::{Stage0Config, parse_stage0_file};
 use termcolor::WriteColor;
 
 macro_rules! static_regex {
@@ -63,6 +69,61 @@ fn tidy_error(args: &str) -> std::io::Result<()> {
     Ok(())
 }
 
+pub struct CiInfo {
+    pub git_merge_commit_email: String,
+    pub nightly_branch: String,
+    pub base_commit: Option<String>,
+    pub ci_env: CiEnv,
+}
+
+impl CiInfo {
+    pub fn new(bad: &mut bool) -> Self {
+        let stage0 = parse_stage0_file();
+        let Stage0Config { nightly_branch, git_merge_commit_email, .. } = stage0.config;
+
+        let mut info = Self {
+            nightly_branch,
+            git_merge_commit_email,
+            ci_env: CiEnv::current(),
+            base_commit: None,
+        };
+        let base_commit = match get_closest_upstream_commit(None, &info.git_config(), info.ci_env) {
+            Ok(Some(commit)) => Some(commit),
+            Ok(None) => {
+                info.error_if_in_ci("no base commit found", bad);
+                None
+            }
+            Err(error) => {
+                info.error_if_in_ci(&format!("failed to retrieve base commit: {error}"), bad);
+                None
+            }
+        };
+        info.base_commit = base_commit;
+        info
+    }
+
+    pub fn git_config(&self) -> GitConfig<'_> {
+        GitConfig {
+            nightly_branch: &self.nightly_branch,
+            git_merge_commit_email: &self.git_merge_commit_email,
+        }
+    }
+
+    pub fn error_if_in_ci(&self, msg: &str, bad: &mut bool) {
+        if self.ci_env.is_running_in_ci() {
+            *bad = true;
+            eprintln!("tidy check error: {msg}");
+        } else {
+            eprintln!("tidy check warning: {msg}. Some checks will be skipped.");
+        }
+    }
+}
+
+pub fn git_diff<S: AsRef<OsStr>>(base_commit: &str, extra_arg: S) -> Option<String> {
+    let output = Command::new("git").arg("diff").arg(base_commit).arg(extra_arg).output().ok()?;
+    Some(String::from_utf8_lossy(&output.stdout).into())
+}
+
 pub mod alphabetical;
 pub mod bins;
 pub mod debug_artifacts;
@@ -83,6 +144,7 @@ pub mod pal;
 pub mod rustdoc_css_themes;
 pub mod rustdoc_gui_tests;
 pub mod rustdoc_js;
+pub mod rustdoc_json;
 pub mod rustdoc_templates;
 pub mod style;
 pub mod target_policy;
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index 776f1bde2eb..ef6ff5c9277 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -48,7 +48,9 @@ fn main() {
     let extra_checks =
         cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str);
 
-    let bad = std::sync::Arc::new(AtomicBool::new(false));
+    let mut bad = false;
+    let ci_info = CiInfo::new(&mut bad);
+    let bad = std::sync::Arc::new(AtomicBool::new(bad));
 
     let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| {
         // poll all threads for completion before awaiting the oldest one
@@ -110,11 +112,12 @@ fn main() {
         check!(rustdoc_css_themes, &librustdoc_path);
         check!(rustdoc_templates, &librustdoc_path);
         check!(rustdoc_js, &librustdoc_path, &tools_path, &src_path);
+        check!(rustdoc_json, &src_path, &ci_info);
         check!(known_bug, &crashes_path);
         check!(unknown_revision, &tests_path);
 
         // Checks that only make sense for the compiler.
-        check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose);
+        check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info);
         check!(fluent_alphabetical, &compiler_path, bless);
         check!(fluent_period, &compiler_path);
         check!(target_policy, &root_path);
diff --git a/src/tools/tidy/src/rustdoc_js.rs b/src/tools/tidy/src/rustdoc_js.rs
index 2517e2de12c..720f0712ee0 100644
--- a/src/tools/tidy/src/rustdoc_js.rs
+++ b/src/tools/tidy/src/rustdoc_js.rs
@@ -62,6 +62,9 @@ pub fn check(librustdoc_path: &Path, tools_path: &Path, src_path: &Path, bad: &m
             return;
         }
     };
+    // Having the correct `eslint` version installed via `npm` isn't strictly necessary, since we're invoking it via `npx`,
+    // but this check allows the vast majority that is not working on the rustdoc frontend to avoid the penalty of running
+    // `eslint` in tidy. See also: https://github.com/rust-lang/rust/pull/142851
     match get_eslint_version() {
         Some(version) => {
             if version != eslint_version {
diff --git a/src/tools/tidy/src/rustdoc_json.rs b/src/tools/tidy/src/rustdoc_json.rs
new file mode 100644
index 00000000000..dfbb35d69f1
--- /dev/null
+++ b/src/tools/tidy/src/rustdoc_json.rs
@@ -0,0 +1,90 @@
+//! Tidy check to ensure that `FORMAT_VERSION` was correctly updated if `rustdoc-json-types` was
+//! updated as well.
+
+use std::path::Path;
+use std::str::FromStr;
+
+const RUSTDOC_JSON_TYPES: &str = "src/rustdoc-json-types";
+
+pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) {
+    println!("Checking tidy rustdoc_json...");
+    let Some(base_commit) = &ci_info.base_commit else {
+        eprintln!("No base commit, skipping rustdoc_json check");
+        return;
+    };
+
+    // First we check that `src/rustdoc-json-types` was modified.
+    match crate::git_diff(&base_commit, "--name-status") {
+        Some(output) => {
+            if !output
+                .lines()
+                .any(|line| line.starts_with("M") && line.contains(RUSTDOC_JSON_TYPES))
+            {
+                // `rustdoc-json-types` was not modified so nothing more to check here.
+                println!("`rustdoc-json-types` was not modified.");
+                return;
+            }
+        }
+        None => {
+            *bad = true;
+            eprintln!("error: failed to run `git diff` in rustdoc_json check");
+            return;
+        }
+    }
+    // Then we check that if `FORMAT_VERSION` was updated, the `Latest feature:` was also updated.
+    match crate::git_diff(&base_commit, src_path.join("rustdoc-json-types")) {
+        Some(output) => {
+            let mut format_version_updated = false;
+            let mut latest_feature_comment_updated = false;
+            let mut new_version = None;
+            let mut old_version = None;
+            for line in output.lines() {
+                if line.starts_with("+pub const FORMAT_VERSION: u32 =") {
+                    format_version_updated = true;
+                    new_version = line
+                        .split('=')
+                        .nth(1)
+                        .and_then(|s| s.trim().split(';').next())
+                        .and_then(|s| u32::from_str(s.trim()).ok());
+                } else if line.starts_with("-pub const FORMAT_VERSION: u32 =") {
+                    old_version = line
+                        .split('=')
+                        .nth(1)
+                        .and_then(|s| s.trim().split(';').next())
+                        .and_then(|s| u32::from_str(s.trim()).ok());
+                } else if line.starts_with("+// Latest feature:") {
+                    latest_feature_comment_updated = true;
+                }
+            }
+            if format_version_updated != latest_feature_comment_updated {
+                *bad = true;
+                if latest_feature_comment_updated {
+                    eprintln!(
+                        "error in `rustdoc_json` tidy check: `Latest feature` comment was updated \
+                         whereas `FORMAT_VERSION` wasn't in `{RUSTDOC_JSON_TYPES}/lib.rs`"
+                    );
+                } else {
+                    eprintln!(
+                        "error in `rustdoc_json` tidy check: `Latest feature` comment was not \
+                         updated whereas `FORMAT_VERSION` was in `{RUSTDOC_JSON_TYPES}/lib.rs`"
+                    );
+                }
+            }
+            match (new_version, old_version) {
+                (Some(new_version), Some(old_version)) if new_version != old_version + 1 => {
+                    *bad = true;
+                    eprintln!(
+                        "error in `rustdoc_json` tidy check: invalid `FORMAT_VERSION` increase in \
+                         `{RUSTDOC_JSON_TYPES}/lib.rs`, should be `{}`, found `{new_version}`",
+                        old_version + 1,
+                    );
+                }
+                _ => {}
+            }
+        }
+        None => {
+            *bad = true;
+            eprintln!("error: failed to run `git diff` in rustdoc_json check");
+        }
+    }
+}
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 8f9b07c49ac..53226fcb80e 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -17,7 +17,7 @@ use ignore::Walk;
 const ENTRY_LIMIT: u32 = 901;
 // FIXME: The following limits should be reduced eventually.
 
-const ISSUES_ENTRY_LIMIT: u32 = 1623;
+const ISSUES_ENTRY_LIMIT: u32 = 1619;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
     "rs",     // test source files
diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml
index 642d48b9952..ce718902b29 100644
--- a/src/tools/wasm-component-ld/Cargo.toml
+++ b/src/tools/wasm-component-ld/Cargo.toml
@@ -10,4 +10,4 @@ name = "wasm-component-ld"
 path = "src/main.rs"
 
 [dependencies]
-wasm-component-ld = "0.5.13"
+wasm-component-ld = "0.5.14"
diff --git a/src/version b/src/version
index 636ea711ad9..82e24bf241e 100644
--- a/src/version
+++ b/src/version
@@ -1 +1 @@
-1.89.0
+1.90.0
diff --git a/tests/assembly/cmse.rs b/tests/assembly/cmse.rs
index 2984df92225..a68ee99eac6 100644
--- a/tests/assembly/cmse.rs
+++ b/tests/assembly/cmse.rs
@@ -6,7 +6,7 @@
 //@ [hard] needs-llvm-components: arm
 //@ [soft] needs-llvm-components: arm
 #![crate_type = "lib"]
-#![feature(abi_c_cmse_nonsecure_call, cmse_nonsecure_entry, no_core, lang_items)]
+#![feature(abi_cmse_nonsecure_call, cmse_nonsecure_entry, no_core, lang_items)]
 #![no_core]
 
 extern crate minicore;
@@ -53,7 +53,7 @@ use minicore::*;
 // Branch back to non-secure side
 // CHECK: bxns lr
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn entry_point() -> i64 {
+pub extern "cmse-nonsecure-entry" fn entry_point() -> i64 {
     0
 }
 
@@ -95,8 +95,6 @@ pub extern "C-cmse-nonsecure-entry" fn entry_point() -> i64 {
 // Call to non-secure
 // CHECK: blxns r12
 #[no_mangle]
-pub fn call_nonsecure(
-    f: unsafe extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u64,
-) -> u64 {
+pub fn call_nonsecure(f: unsafe extern "cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u64) -> u64 {
     unsafe { f(0, 1, 2, 3) }
 }
diff --git a/tests/assembly/naked-functions/wasm32.rs b/tests/assembly/naked-functions/wasm32.rs
index 984152f2b45..5f114246ad5 100644
--- a/tests/assembly/naked-functions/wasm32.rs
+++ b/tests/assembly/naked-functions/wasm32.rs
@@ -37,7 +37,7 @@ extern "C" fn nop() {
 #[unsafe(naked)]
 #[linkage = "weak"]
 // wasm functions cannot be aligned, so this has no effect
-#[repr(align(32))]
+#[align(32)]
 extern "C" fn weak_aligned_nop() {
     naked_asm!("nop")
 }
diff --git a/tests/assembly/s390x-backchain-toggle.rs b/tests/assembly/s390x-backchain-toggle.rs
index 83c7b82d0d4..9bae15b7d11 100644
--- a/tests/assembly/s390x-backchain-toggle.rs
+++ b/tests/assembly/s390x-backchain-toggle.rs
@@ -1,5 +1,5 @@
 //@ add-core-stubs
-//@ revisions: enable-backchain disable-backchain
+//@ revisions: enable-backchain disable-backchain default-backchain
 //@ assembly-output: emit-asm
 //@ compile-flags: -Copt-level=3 --crate-type=lib --target=s390x-unknown-linux-gnu
 //@ needs-llvm-components: systemz
@@ -26,6 +26,8 @@ extern "C" fn test_backchain() -> i32 {
     // enable-backchain: stg [[REG1]], 0(%r15)
     // disable-backchain: aghi %r15, -160
     // disable-backchain-NOT: stg %r{{.*}}, 0(%r15)
+    // default-backchain: aghi %r15, -160
+    // default-backchain-NOT: stg %r{{.*}}, 0(%r15)
     unsafe {
         extern_func();
     }
@@ -35,6 +37,7 @@ extern "C" fn test_backchain() -> i32 {
     // Make sure that the expected return value is written into %r2 (return register):
     // enable-backchain-NEXT: lghi %r2, 1
     // disable-backchain: lghi %r2, 0
+    // default-backchain: lghi %r2, 0
     #[cfg(target_feature = "backchain")]
     {
         1
diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs
index db11549382f..3e9841b179c 100644
--- a/tests/auxiliary/minicore.rs
+++ b/tests/auxiliary/minicore.rs
@@ -16,6 +16,7 @@
 
 #![feature(
     no_core,
+    intrinsics,
     lang_items,
     auto_traits,
     freeze_impls,
@@ -196,3 +197,9 @@ impl<'a, 'b: 'a, T: PointeeSized + Unsize<U>, U: PointeeSized> CoerceUnsized<&'a
 trait Drop {
     fn drop(&mut self);
 }
+
+pub mod mem {
+    #[rustc_nounwind]
+    #[rustc_intrinsic]
+    pub unsafe fn transmute<Src, Dst>(src: Src) -> Dst;
+}
diff --git a/tests/codegen-units/item-collection/drop-glue-noop.rs b/tests/codegen-units/item-collection/drop-glue-noop.rs
new file mode 100644
index 00000000000..604ba883bb2
--- /dev/null
+++ b/tests/codegen-units/item-collection/drop-glue-noop.rs
@@ -0,0 +1,23 @@
+//@ compile-flags:-Clink-dead-code -Zmir-opt-level=0
+
+#![deny(dead_code)]
+#![crate_type = "lib"]
+
+//~ MONO_ITEM fn start
+#[no_mangle]
+pub fn start(_: isize, _: *const *const u8) -> isize {
+    // No item produced for this, it's a no-op drop and so is removed.
+    unsafe {
+        std::ptr::drop_in_place::<u32>(&mut 0);
+    }
+
+    // No choice but to codegen for indirect drop as a function pointer, since we have to produce a
+    // function with the right signature. In vtables we can avoid that (tested in
+    // instantiation-through-vtable.rs) because we special case null pointer for drop glue since
+    // #122662.
+    //
+    //~ MONO_ITEM fn std::ptr::drop_in_place::<u64> - shim(None) @@ drop_glue_noop-cgu.0[External]
+    std::ptr::drop_in_place::<u64> as unsafe fn(*mut u64);
+
+    0
+}
diff --git a/tests/codegen-units/item-collection/instantiation-through-vtable.rs b/tests/codegen-units/item-collection/instantiation-through-vtable.rs
index 8f13fd55808..7882a526b68 100644
--- a/tests/codegen-units/item-collection/instantiation-through-vtable.rs
+++ b/tests/codegen-units/item-collection/instantiation-through-vtable.rs
@@ -24,7 +24,6 @@ impl<T> Trait for Struct<T> {
 pub fn start(_: isize, _: *const *const u8) -> isize {
     let s1 = Struct { _a: 0u32 };
 
-    //~ MONO_ITEM fn std::ptr::drop_in_place::<Struct<u32>> - shim(None) @@ instantiation_through_vtable-cgu.0[External]
     //~ MONO_ITEM fn <Struct<u32> as Trait>::foo
     //~ MONO_ITEM fn <Struct<u32> as Trait>::bar
     let r1 = &s1 as &Trait;
@@ -32,7 +31,6 @@ pub fn start(_: isize, _: *const *const u8) -> isize {
     r1.bar();
 
     let s1 = Struct { _a: 0u64 };
-    //~ MONO_ITEM fn std::ptr::drop_in_place::<Struct<u64>> - shim(None) @@ instantiation_through_vtable-cgu.0[External]
     //~ MONO_ITEM fn <Struct<u64> as Trait>::foo
     //~ MONO_ITEM fn <Struct<u64> as Trait>::bar
     let _ = &s1 as &Trait;
diff --git a/tests/codegen-units/item-collection/non-generic-closures.rs b/tests/codegen-units/item-collection/non-generic-closures.rs
index 124fe7e3b69..2d9c461e6fd 100644
--- a/tests/codegen-units/item-collection/non-generic-closures.rs
+++ b/tests/codegen-units/item-collection/non-generic-closures.rs
@@ -1,4 +1,4 @@
-//@ compile-flags:-Clink-dead-code -Zinline-mir=no
+//@ compile-flags:-Clink-dead-code -Zinline-mir=no -O
 
 #![deny(dead_code)]
 #![crate_type = "lib"]
@@ -22,9 +22,8 @@ fn assigned_to_variable_but_not_executed() {
 //~ MONO_ITEM fn assigned_to_variable_executed_indirectly @@ non_generic_closures-cgu.0[External]
 fn assigned_to_variable_executed_indirectly() {
     //~ MONO_ITEM fn assigned_to_variable_executed_indirectly::{closure#0} @@ non_generic_closures-cgu.0[External]
-    //~ MONO_ITEM fn <{closure@TEST_PATH:28:13: 28:21} as std::ops::FnOnce<(i32,)>>::call_once - shim @@ non_generic_closures-cgu.0[External]
-    //~ MONO_ITEM fn <{closure@TEST_PATH:28:13: 28:21} as std::ops::FnOnce<(i32,)>>::call_once - shim(vtable) @@ non_generic_closures-cgu.0[External]
-    //~ MONO_ITEM fn std::ptr::drop_in_place::<{closure@TEST_PATH:28:13: 28:21}> - shim(None) @@ non_generic_closures-cgu.0[External]
+    //~ MONO_ITEM fn <{closure@TEST_PATH:27:13: 27:21} as std::ops::FnOnce<(i32,)>>::call_once - shim @@ non_generic_closures-cgu.0[External]
+    //~ MONO_ITEM fn <{closure@TEST_PATH:27:13: 27:21} as std::ops::FnOnce<(i32,)>>::call_once - shim(vtable) @@ non_generic_closures-cgu.0[External]
     let f = |a: i32| {
         let _ = a + 2;
     };
@@ -40,6 +39,20 @@ fn assigned_to_variable_executed_directly() {
     f(4);
 }
 
+// Make sure we generate mono items for stateful closures that need dropping
+//~ MONO_ITEM fn with_drop @@ non_generic_closures-cgu.0[External]
+fn with_drop(v: PresentDrop) {
+    //~ MONO_ITEM fn with_drop::{closure#0} @@ non_generic_closures-cgu.0[External]
+    //~ MONO_ITEM fn std::ptr::drop_in_place::<PresentDrop> - shim(Some(PresentDrop)) @@ non_generic_closures-cgu.0[Internal]
+    //~ MONO_ITEM fn std::ptr::drop_in_place::<{closure@TEST_PATH:49:14: 49:24}> - shim(Some({closure@TEST_PATH:49:14: 49:24})) @@ non_generic_closures-cgu.0[Internal]
+
+    let _f = |a: usize| {
+        let _ = a + 2;
+        //~ MONO_ITEM fn std::mem::drop::<PresentDrop> @@ non_generic_closures-cgu.0[External]
+        drop(v);
+    };
+}
+
 //~ MONO_ITEM fn start @@ non_generic_closures-cgu.0[External]
 #[no_mangle]
 pub fn start(_: isize, _: *const *const u8) -> isize {
@@ -47,6 +60,7 @@ pub fn start(_: isize, _: *const *const u8) -> isize {
     assigned_to_variable_but_not_executed();
     assigned_to_variable_executed_directly();
     assigned_to_variable_executed_indirectly();
+    with_drop(PresentDrop);
 
     0
 }
@@ -55,3 +69,10 @@ pub fn start(_: isize, _: *const *const u8) -> isize {
 fn run_closure(f: &Fn(i32)) {
     f(3);
 }
+
+struct PresentDrop;
+
+impl Drop for PresentDrop {
+    //~ MONO_ITEM fn <PresentDrop as std::ops::Drop>::drop @@ non_generic_closures-cgu.0[External]
+    fn drop(&mut self) {}
+}
diff --git a/tests/codegen-units/item-collection/unsizing.rs b/tests/codegen-units/item-collection/unsizing.rs
index 15e42bce249..b751d2153a9 100644
--- a/tests/codegen-units/item-collection/unsizing.rs
+++ b/tests/codegen-units/item-collection/unsizing.rs
@@ -1,4 +1,4 @@
-//@ compile-flags:-Zmir-opt-level=0
+//@ compile-flags:-Zmir-opt-level=0 -O
 
 #![deny(dead_code)]
 #![feature(coerce_unsized)]
@@ -42,33 +42,47 @@ struct Wrapper<T: ?Sized>(#[allow(dead_code)] *const T);
 
 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Wrapper<U>> for Wrapper<T> {}
 
+struct PresentDrop;
+
+impl Drop for PresentDrop {
+    fn drop(&mut self) {}
+}
+
+// Custom Coercion Case
+impl Trait for PresentDrop {
+    fn foo(&self) {}
+}
+
 //~ MONO_ITEM fn start
 #[no_mangle]
 pub fn start(_: isize, _: *const *const u8) -> isize {
     // simple case
     let bool_sized = &true;
-    //~ MONO_ITEM fn std::ptr::drop_in_place::<bool> - shim(None) @@ unsizing-cgu.0[Internal]
     //~ MONO_ITEM fn <bool as Trait>::foo
     let _bool_unsized = bool_sized as &Trait;
 
     let char_sized = &'a';
 
-    //~ MONO_ITEM fn std::ptr::drop_in_place::<char> - shim(None) @@ unsizing-cgu.0[Internal]
     //~ MONO_ITEM fn <char as Trait>::foo
     let _char_unsized = char_sized as &Trait;
 
     // struct field
     let struct_sized = &Struct { _a: 1, _b: 2, _c: 3.0f64 };
-    //~ MONO_ITEM fn std::ptr::drop_in_place::<f64> - shim(None) @@ unsizing-cgu.0[Internal]
     //~ MONO_ITEM fn <f64 as Trait>::foo
     let _struct_unsized = struct_sized as &Struct<Trait>;
 
     // custom coercion
     let wrapper_sized = Wrapper(&0u32);
-    //~ MONO_ITEM fn std::ptr::drop_in_place::<u32> - shim(None) @@ unsizing-cgu.0[Internal]
     //~ MONO_ITEM fn <u32 as Trait>::foo
     let _wrapper_sized = wrapper_sized as Wrapper<Trait>;
 
+    // with drop
+    let droppable = &PresentDrop;
+    //~ MONO_ITEM fn <PresentDrop as std::ops::Drop>::drop @@ unsizing-cgu.0[Internal]
+    //~ MONO_ITEM fn std::ptr::drop_in_place::<PresentDrop> - shim(Some(PresentDrop)) @@ unsizing-cgu.0[Internal]
+    //~ MONO_ITEM fn <PresentDrop as Trait>::foo
+    let droppable = droppable as &dyn Trait;
+
     false.foo();
 
     0
diff --git a/tests/codegen/abi-x86-interrupt.rs b/tests/codegen/abi-x86-interrupt.rs
index 255ccba2c11..9a1ded2c9e3 100644
--- a/tests/codegen/abi-x86-interrupt.rs
+++ b/tests/codegen/abi-x86-interrupt.rs
@@ -13,8 +13,6 @@
 extern crate minicore;
 use minicore::*;
 
-// CHECK: define x86_intrcc i64 @has_x86_interrupt_abi
+// CHECK: define x86_intrcc void @has_x86_interrupt_abi
 #[no_mangle]
-pub extern "x86-interrupt" fn has_x86_interrupt_abi(a: i64) -> i64 {
-    a
-}
+pub extern "x86-interrupt" fn has_x86_interrupt_abi() {}
diff --git a/tests/codegen/align-fn.rs b/tests/codegen/align-fn.rs
index 660d8cd2bbf..267da060240 100644
--- a/tests/codegen/align-fn.rs
+++ b/tests/codegen/align-fn.rs
@@ -5,7 +5,7 @@
 
 // CHECK: align 16
 #[no_mangle]
-#[repr(align(16))]
+#[align(16)]
 pub fn fn_align() {}
 
 pub struct A;
@@ -13,12 +13,12 @@ pub struct A;
 impl A {
     // CHECK: align 16
     #[no_mangle]
-    #[repr(align(16))]
+    #[align(16)]
     pub fn method_align(self) {}
 
     // CHECK: align 16
     #[no_mangle]
-    #[repr(align(16))]
+    #[align(16)]
     pub fn associated_fn() {}
 }
 
@@ -26,19 +26,19 @@ trait T: Sized {
     fn trait_fn() {}
 
     // CHECK: align 32
-    #[repr(align(32))]
+    #[align(32)]
     fn trait_method(self) {}
 }
 
 impl T for A {
     // CHECK: align 16
     #[no_mangle]
-    #[repr(align(16))]
+    #[align(16)]
     fn trait_fn() {}
 
     // CHECK: align 16
     #[no_mangle]
-    #[repr(align(16))]
+    #[align(16)]
     fn trait_method(self) {}
 }
 
@@ -51,18 +51,20 @@ pub fn foo() {
 // CHECK-LABEL: align_specified_twice_1
 // CHECK-SAME: align 64
 #[no_mangle]
-#[repr(align(32), align(64))]
+#[align(32)]
+#[align(64)]
 pub fn align_specified_twice_1() {}
 
 // CHECK-LABEL: align_specified_twice_2
 // CHECK-SAME: align 128
 #[no_mangle]
-#[repr(align(128), align(32))]
+#[align(128)]
+#[align(32)]
 pub fn align_specified_twice_2() {}
 
 // CHECK-LABEL: align_specified_twice_3
 // CHECK-SAME: align 256
 #[no_mangle]
-#[repr(align(32))]
-#[repr(align(256))]
+#[align(32)]
+#[align(256)]
 pub fn align_specified_twice_3() {}
diff --git a/tests/codegen/asm/critical.rs b/tests/codegen/asm/critical.rs
index 8c039900cab..0f29d7c69b4 100644
--- a/tests/codegen/asm/critical.rs
+++ b/tests/codegen/asm/critical.rs
@@ -1,6 +1,5 @@
 //@ only-x86_64
 //@ compile-flags: -C no-prepopulate-passes
-#![feature(asm_goto)]
 #![feature(asm_goto_with_outputs)]
 #![crate_type = "lib"]
 use std::arch::asm;
diff --git a/tests/codegen/min-function-alignment.rs b/tests/codegen/min-function-alignment.rs
index 7c0ad12402a..78989ec5df2 100644
--- a/tests/codegen/min-function-alignment.rs
+++ b/tests/codegen/min-function-alignment.rs
@@ -1,33 +1,35 @@
 //@ revisions: align16 align1024
-//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0
+//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 -Clink-dead-code
 //@ [align16] compile-flags: -Zmin-function-alignment=16
 //@ [align1024] compile-flags: -Zmin-function-alignment=1024
 
 #![crate_type = "lib"]
 #![feature(fn_align)]
 
-// functions without explicit alignment use the global minimum
+// Functions without explicit alignment use the global minimum.
 //
-// CHECK-LABEL: @no_explicit_align
+// NOTE: this function deliberately has zero (0) attributes! That is to make sure that
+// `-Zmin-function-alignment` is applied regardless of whether attributes are used.
+//
+// CHECK-LABEL: no_explicit_align
 // align16: align 16
 // align1024: align 1024
-#[no_mangle]
 pub fn no_explicit_align() {}
 
 // CHECK-LABEL: @lower_align
 // align16: align 16
 // align1024: align 1024
 #[no_mangle]
-#[repr(align(8))]
+#[align(8)]
 pub fn lower_align() {}
 
-// the higher value of min-function-alignment and repr(align) wins out
+// the higher value of min-function-alignment and the align attribute wins out
 //
 // CHECK-LABEL: @higher_align
 // align16: align 32
 // align1024: align 1024
 #[no_mangle]
-#[repr(align(32))]
+#[align(32)]
 pub fn higher_align() {}
 
 // cold functions follow the same rules as other functions
diff --git a/tests/codegen/naked-asan.rs b/tests/codegen/naked-asan.rs
index 223c41b15bb..46218cf79d6 100644
--- a/tests/codegen/naked-asan.rs
+++ b/tests/codegen/naked-asan.rs
@@ -1,22 +1,28 @@
-// Make sure we do not request sanitizers for naked functions.
+//@ add-core-stubs
+//@ needs-llvm-components: x86
+//@ compile-flags: --target x86_64-unknown-linux-gnu -Zsanitizer=address -Ctarget-feature=-crt-static
 
-//@ only-x86_64
-//@ needs-sanitizer-address
-//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static
+// Make sure we do not request sanitizers for naked functions.
 
 #![crate_type = "lib"]
+#![feature(no_core)]
 #![no_std]
+#![no_core]
 #![feature(abi_x86_interrupt)]
 
+extern crate minicore;
+use minicore::*;
+
+#[no_mangle]
 pub fn caller() {
-    page_fault_handler(1, 2);
+    unsafe { asm!("call {}", sym page_fault_handler) }
 }
 
-// CHECK: declare x86_intrcc void @page_fault_handler(ptr {{.*}}, i64{{.*}}){{.*}}#[[ATTRS:[0-9]+]]
+// CHECK: declare x86_intrcc void @page_fault_handler(){{.*}}#[[ATTRS:[0-9]+]]
 #[unsafe(naked)]
 #[no_mangle]
-pub extern "x86-interrupt" fn page_fault_handler(_: u64, _: u64) {
-    core::arch::naked_asm!("ud2")
+pub extern "x86-interrupt" fn page_fault_handler() {
+    naked_asm!("ud2")
 }
 
 // CHECK: #[[ATTRS]] =
diff --git a/tests/codegen/naked-fn/aligned.rs b/tests/codegen/naked-fn/aligned.rs
index 47ef779f1b2..f9fce8e5a5d 100644
--- a/tests/codegen/naked-fn/aligned.rs
+++ b/tests/codegen/naked-fn/aligned.rs
@@ -8,7 +8,7 @@ use std::arch::naked_asm;
 
 // CHECK: .balign 16
 // CHECK-LABEL: naked_empty:
-#[repr(align(16))]
+#[align(16)]
 #[no_mangle]
 #[unsafe(naked)]
 pub extern "C" fn naked_empty() {
diff --git a/tests/codegen/naked-fn/min-function-alignment.rs b/tests/codegen/naked-fn/min-function-alignment.rs
index 1d778be8c90..59554c1cae5 100644
--- a/tests/codegen/naked-fn/min-function-alignment.rs
+++ b/tests/codegen/naked-fn/min-function-alignment.rs
@@ -16,7 +16,7 @@ pub extern "C" fn naked_no_explicit_align() {
 
 // CHECK: .balign 16
 #[no_mangle]
-#[repr(align(8))]
+#[align(8)]
 #[unsafe(naked)]
 pub extern "C" fn naked_lower_align() {
     core::arch::naked_asm!("ret")
@@ -24,7 +24,7 @@ pub extern "C" fn naked_lower_align() {
 
 // CHECK: .balign 32
 #[no_mangle]
-#[repr(align(32))]
+#[align(32)]
 #[unsafe(naked)]
 pub extern "C" fn naked_higher_align() {
     core::arch::naked_asm!("ret")
diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs
index 2a7eca6fc19..8fec275fd06 100644
--- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs
+++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-drop-in-place.rs
@@ -1,5 +1,9 @@
 // Verifies that type metadata identifiers for drop functions are emitted correctly.
 //
+// Non needs_drop drop glue isn't codegen'd at all, so we don't try to check the IDs there. But we
+// do check it's not emitted which should help catch bugs if we do start generating it again in the
+// future.
+//
 //@ needs-sanitizer-cfi
 //@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static
 
@@ -10,18 +14,18 @@
 // CHECK:       call i1 @llvm.type.test(ptr {{%.+}}, metadata !"_ZTSFvPu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops4drop4Dropu6regionEE")
 
 struct EmptyDrop;
-// CHECK: define{{.*}}4core3ptr{{[0-9]+}}drop_in_place$LT${{.*}}EmptyDrop$GT${{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
+// CHECK-NOT: define{{.*}}4core3ptr{{[0-9]+}}drop_in_place$LT${{.*}}EmptyDrop$GT${{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
 
-struct NonEmptyDrop;
+struct PresentDrop;
 
-impl Drop for NonEmptyDrop {
+impl Drop for PresentDrop {
     fn drop(&mut self) {}
-    // CHECK: define{{.*}}4core3ptr{{[0-9]+}}drop_in_place$LT${{.*}}NonEmptyDrop$GT${{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
+    // CHECK: define{{.*}}4core3ptr{{[0-9]+}}drop_in_place$LT${{.*}}PresentDrop$GT${{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
 }
 
 pub fn foo() {
     let _ = Box::new(EmptyDrop) as Box<dyn Send>;
-    let _ = Box::new(NonEmptyDrop) as Box<dyn Send>;
+    let _ = Box::new(PresentDrop) as Box<dyn Send>;
 }
 
 // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops4drop4Dropu6regionEE"}
diff --git a/tests/codegen/target-feature-negative-implication.rs b/tests/codegen/target-feature-negative-implication.rs
new file mode 100644
index 00000000000..36cd82dd8cf
--- /dev/null
+++ b/tests/codegen/target-feature-negative-implication.rs
@@ -0,0 +1,20 @@
+//@ add-core-stubs
+//@ needs-llvm-components: x86
+//@ compile-flags: --target=x86_64-unknown-linux-gnu
+//@ compile-flags: -Ctarget-feature=-avx2
+
+#![feature(no_core, lang_items)]
+#![crate_type = "lib"]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+#[no_mangle]
+pub unsafe fn banana() {
+    // CHECK-LABEL: @banana()
+    // CHECK-SAME: [[BANANAATTRS:#[0-9]+]] {
+}
+
+// CHECK: attributes [[BANANAATTRS]]
+// CHECK-SAME: -avx512
diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs
index 0fc1e0136b3..eb19b0de2fa 100644
--- a/tests/codegen/target-feature-overrides.rs
+++ b/tests/codegen/target-feature-overrides.rs
@@ -1,3 +1,4 @@
+// ignore-tidy-linelength
 //@ add-core-stubs
 //@ revisions: COMPAT INCOMPAT
 //@ needs-llvm-components: x86
@@ -39,7 +40,7 @@ pub unsafe fn banana() -> u32 {
 
 // CHECK: attributes [[APPLEATTRS]]
 // COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}"
-// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}"
+// INCOMPAT-SAME: "target-features"="{{(-[^,]+,)*}}-avx2{{(,-[^,]+)*}},-avx{{(,-[^,]+)*}},+avx{{(,\+[^,]+)*}}"
 // CHECK: attributes [[BANANAATTRS]]
 // COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}"
-// INCOMPAT-SAME: "target-features"="-avx2,-avx"
+// INCOMPAT-SAME: "target-features"="{{(-[^,]+,)*}}-avx2{{(,-[^,]+)*}},-avx{{(,-[^,]+)*}}"
diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs
index 6be0e21e0ef..81499c070d1 100644
--- a/tests/codegen/tied-features-strength.rs
+++ b/tests/codegen/tied-features-strength.rs
@@ -4,14 +4,23 @@
 //@ compile-flags: --crate-type=rlib --target=aarch64-unknown-linux-gnu
 //@ needs-llvm-components: aarch64
 
+// Rust made SVE require neon.
 //@ [ENABLE_SVE] compile-flags: -C target-feature=+sve -Copt-level=0
-// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" }
+// ENABLE_SVE: attributes #0
+// ENABLE_SVE-SAME: +neon
+// ENABLE_SVE-SAME: +sve
 
+// However, disabling SVE does not disable neon.
 //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0
-// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(-sve,?)|(\+neon,?))*}}" }
+// DISABLE_SVE: attributes #0
+// DISABLE_SVE-NOT: -neon
+// DISABLE_SVE-SAME: -sve
 
+// OTOH, neon fn `fp-armv8` are fully tied; toggling neon must toggle `fp-armv8` the same way.
 //@ [DISABLE_NEON] compile-flags: -C target-feature=-neon -Copt-level=0
-// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(-fp-armv8,?)|(-neon,?))*}}" }
+// DISABLE_NEON: attributes #0
+// DISABLE_NEON-SAME: -neon
+// DISABLE_NEON-SAME: -fp-armv8
 
 //@ [ENABLE_NEON] compile-flags: -C target-feature=+neon -Copt-level=0
 // ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fp-armv8,?)|(\+neon,?))*}}" }
diff --git a/tests/codegen/transmute-scalar.rs b/tests/codegen/transmute-scalar.rs
index c080259a917..c57ade58c30 100644
--- a/tests/codegen/transmute-scalar.rs
+++ b/tests/codegen/transmute-scalar.rs
@@ -55,3 +55,48 @@ pub fn ptr_to_int(p: *mut u16) -> usize {
 pub fn int_to_ptr(i: usize) -> *mut u16 {
     unsafe { std::mem::transmute(i) }
 }
+
+// This is the one case where signedness matters to transmuting:
+// the LLVM type is `i8` here because of `repr(i8)`,
+// whereas below with the `repr(u8)` it's `i1` in LLVM instead.
+#[repr(i8)]
+pub enum FakeBoolSigned {
+    False = 0,
+    True = 1,
+}
+
+// CHECK-LABEL: define{{.*}}i8 @bool_to_fake_bool_signed(i1 zeroext %b)
+// CHECK: %_0 = zext i1 %b to i8
+// CHECK-NEXT: ret i8 %_0
+#[no_mangle]
+pub fn bool_to_fake_bool_signed(b: bool) -> FakeBoolSigned {
+    unsafe { std::mem::transmute(b) }
+}
+
+// CHECK-LABEL: define{{.*}}i1 @fake_bool_signed_to_bool(i8 %b)
+// CHECK: %_0 = trunc nuw i8 %b to i1
+// CHECK-NEXT: ret i1 %_0
+#[no_mangle]
+pub fn fake_bool_signed_to_bool(b: FakeBoolSigned) -> bool {
+    unsafe { std::mem::transmute(b) }
+}
+
+#[repr(u8)]
+pub enum FakeBoolUnsigned {
+    False = 0,
+    True = 1,
+}
+
+// CHECK-LABEL: define{{.*}}i1 @bool_to_fake_bool_unsigned(i1 zeroext %b)
+// CHECK: ret i1 %b
+#[no_mangle]
+pub fn bool_to_fake_bool_unsigned(b: bool) -> FakeBoolUnsigned {
+    unsafe { std::mem::transmute(b) }
+}
+
+// CHECK-LABEL: define{{.*}}i1 @fake_bool_unsigned_to_bool(i1 zeroext %b)
+// CHECK: ret i1 %b
+#[no_mangle]
+pub fn fake_bool_unsigned_to_bool(b: FakeBoolUnsigned) -> bool {
+    unsafe { std::mem::transmute(b) }
+}
diff --git a/tests/crashes/126269.rs b/tests/crashes/126269.rs
deleted file mode 100644
index ca4b76eb930..00000000000
--- a/tests/crashes/126269.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-//@ known-bug: rust-lang/rust#126269
-#![feature(coerce_unsized)]
-
-pub enum Foo<T> {
-    Bar([T; usize::MAX]),
-}
-
-use std::ops::CoerceUnsized;
-
-impl<T, U> CoerceUnsized<U> for T {}
-
-fn main() {}
diff --git a/tests/crashes/126982.rs b/tests/crashes/126982.rs
deleted file mode 100644
index 8522d9415eb..00000000000
--- a/tests/crashes/126982.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//@ known-bug: rust-lang/rust#126982
-
-#![feature(coerce_unsized)]
-use std::ops::CoerceUnsized;
-
-struct Foo<T: ?Sized> {
-    a: T,
-}
-
-impl<T, U> CoerceUnsized<U> for Foo<T> {}
-
-union U {
-    a: usize,
-}
-
-const C: U = Foo { a: 10 };
-
-fn main() {}
diff --git a/tests/crashes/130104.rs b/tests/crashes/130104.rs
index 0ffc21ad360..b961108c923 100644
--- a/tests/crashes/130104.rs
+++ b/tests/crashes/130104.rs
@@ -2,5 +2,5 @@
 
 fn main() {
     let non_secure_function =
-        core::mem::transmute::<fn() -> _, extern "C-cmse-nonsecure-call" fn() -> _>;
+        core::mem::transmute::<fn() -> _, extern "cmse-nonsecure-call" fn() -> _>;
 }
diff --git a/tests/crashes/131048.rs b/tests/crashes/131048.rs
deleted file mode 100644
index d57e9921a8a..00000000000
--- a/tests/crashes/131048.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//@ known-bug: #131048
-
-impl<A> std::ops::CoerceUnsized<A> for A {}
-
-fn main() {
-    format_args!("Hello, world!");
-}
diff --git a/tests/crashes/132142.rs b/tests/crashes/132142.rs
index 9a026f3bca7..813bf0bf0a8 100644
--- a/tests/crashes/132142.rs
+++ b/tests/crashes/132142.rs
@@ -1,3 +1,3 @@
 //@ known-bug: #132142
 
-async extern "C-cmse-nonsecure-entry" fn fun(...) {}
+async extern "cmse-nonsecure-entry" fn fun(...) {}
diff --git a/tests/crashes/132430.rs b/tests/crashes/132430.rs
deleted file mode 100644
index 81c8c6d6f7d..00000000000
--- a/tests/crashes/132430.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//@ known-bug: #132430
-
-//@ compile-flags: --crate-type=lib
-//@ edition: 2018
-#![feature(cmse_nonsecure_entry)]
-struct Test;
-
-impl Test {
-    pub async unsafe extern "C-cmse-nonsecure-entry" fn test(val: &str) {}
-}
diff --git a/tests/crashes/134217.rs b/tests/crashes/134217.rs
deleted file mode 100644
index 1b14c660e8b..00000000000
--- a/tests/crashes/134217.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-//@ known-bug: #134217
-
-impl<A> std::ops::CoerceUnsized<A> for A {}
-
-fn main() {
-    if let _ = true
-        && true
-    {}
-}
diff --git a/tests/crashes/136678.rs b/tests/crashes/136678.rs
deleted file mode 100644
index e7d7de23bfe..00000000000
--- a/tests/crashes/136678.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//@ known-bug: #136678
-#![feature(inherent_associated_types)]
-#![feature(generic_const_exprs)]
-#![allow(incomplete_features)]
-
-struct B<const A: usize>;
-
-struct Test<const A: usize>;
-
-impl<const A: usize> Test<A> {
-    type B = B<{ A }>;
-
-    fn test(a: Self::B) -> Self::B {
-        a
-    }
-}
-
-pub fn main() {}
diff --git a/tests/crashes/138131.rs b/tests/crashes/138131.rs
deleted file mode 100644
index f400c02de8d..00000000000
--- a/tests/crashes/138131.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-//@ known-bug: #138131
-#![feature(min_generic_const_args)]
-#![feature(inherent_associated_types)]
-struct Foo<'a> {
-    x: &'a (),
-}
-
-impl<'a> Foo<'a> {
-    fn foo(_: [u8; Foo::X]) {}
-}
-
-fn main() {}
diff --git a/tests/crashes/138265.rs b/tests/crashes/138265.rs
deleted file mode 100644
index f6c8ea74889..00000000000
--- a/tests/crashes/138265.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-//@ known-bug: #138265
-
-#![feature(coerce_unsized)]
-#![crate_type = "lib"]
-impl<A> std::ops::CoerceUnsized<A> for A {}
-pub fn f() {
-    [0; {
-        let mut c = &0;
-        c = &0;
-        0
-    }]
-}
diff --git a/tests/crashes/138738.rs b/tests/crashes/138738.rs
deleted file mode 100644
index 74e5effa56f..00000000000
--- a/tests/crashes/138738.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//@ known-bug: #138738
-//@ only-x86_64
-
-#![feature(abi_ptx)]
-fn main() {
-    let a = unsafe { core::mem::transmute::<usize, extern "ptx-kernel" fn(i32)>(4) }(2);
-}
diff --git a/tests/incremental/issue-61323.rs b/tests/incremental/issue-61323.rs
index b7423c81fc1..4845648d49c 100644
--- a/tests/incremental/issue-61323.rs
+++ b/tests/incremental/issue-61323.rs
@@ -1,7 +1,7 @@
 //@ revisions: rpass cfail
 
 enum A {
-    //[cfail]~^ ERROR 3:1: 3:7: recursive types `A` and `C` have infinite size [E0072]
+    //[cfail]~^ ERROR recursive types `A` and `C` have infinite size [E0072]
     B(C),
 }
 
diff --git a/tests/incremental/track-deps-in-new-solver.rs b/tests/incremental/track-deps-in-new-solver.rs
index fb013b2b24a..51cd6b89e37 100644
--- a/tests/incremental/track-deps-in-new-solver.rs
+++ b/tests/incremental/track-deps-in-new-solver.rs
@@ -3,6 +3,8 @@
 //@ compile-flags: -Znext-solver
 //@ check-pass
 
+#![allow(dead_code)]
+
 pub trait Future {
     type Error;
     fn poll() -> Self::Error;
diff --git a/tests/mir-opt/copy-prop/write_to_borrowed.main.CopyProp.diff b/tests/mir-opt/copy-prop/write_to_borrowed.main.CopyProp.diff
new file mode 100644
index 00000000000..eab06b1ba1e
--- /dev/null
+++ b/tests/mir-opt/copy-prop/write_to_borrowed.main.CopyProp.diff
@@ -0,0 +1,30 @@
+- // MIR for `main` before CopyProp
++ // MIR for `main` after CopyProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let mut _1: *const char;
+      let mut _2: char;
+      let mut _3: char;
+      let mut _4: char;
+      let mut _5: char;
+      let mut _6: &char;
+      let mut _7: ();
+  
+      bb0: {
+          _1 = &raw const _2;
+          _3 = const 'b';
+          _5 = copy _3;
+          _6 = &_3;
+-         _4 = copy _5;
+          (*_1) = copy (*_6);
+          _6 = &_5;
+-         _7 = dump_var::<char>(copy _4) -> [return: bb1, unwind unreachable];
++         _7 = dump_var::<char>(copy _5) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/copy-prop/write_to_borrowed.rs b/tests/mir-opt/copy-prop/write_to_borrowed.rs
new file mode 100644
index 00000000000..58809749103
--- /dev/null
+++ b/tests/mir-opt/copy-prop/write_to_borrowed.rs
@@ -0,0 +1,45 @@
+//@ test-mir-pass: CopyProp
+
+#![feature(custom_mir, core_intrinsics)]
+#![allow(internal_features)]
+
+use std::intrinsics::mir::*;
+
+#[custom_mir(dialect = "runtime")]
+fn main() {
+    mir! {
+        // Both _3 and _5 are borrowed, check that we do not unify them, and that we do not
+        // introduce a write to any of them.
+        let _1;
+        let _2;
+        let _3;
+        let _4;
+        let _5;
+        let _6;
+        let _7;
+        // CHECK: bb0: {
+        {
+            // CHECK-NEXT: _1 = &raw const _2;
+            _1 = core::ptr::addr_of!(_2);
+            // CHECK-NEXT: _3 = const 'b';
+            _3 = 'b';
+            // CHECK-NEXT: _5 = copy _3;
+            _5 = _3;
+            // CHECK-NEXT: _6 = &_3;
+            _6 = &_3;
+            // CHECK-NOT: {{_.*}} = {{_.*}};
+            _4 = _5;
+            // CHECK-NEXT: (*_1) = copy (*_6);
+            *_1 = *_6;
+            // CHECK-NEXT: _6 = &_5;
+            _6 = &_5;
+            // CHECK-NEXT: _7 = dump_var::<char>(copy _5)
+            Call(_7 = dump_var(_4), ReturnTo(bb1), UnwindUnreachable())
+        }
+        bb1 = { Return() }
+    }
+}
+
+fn dump_var<T>(_: T) {}
+
+// EMIT_MIR write_to_borrowed.main.CopyProp.diff
diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
index 33f1ad9bef4..e2d3c6c41b8 100644
--- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
@@ -11,28 +11,29 @@
       let _9: ();
       let _10: ();
       let mut _11: std::fmt::Arguments<'_>;
-      let mut _12: &[&str; 3];
-      let _13: &[&str; 3];
-      let _14: [&str; 3];
-      let mut _15: &[core::fmt::rt::Argument<'_>; 2];
-      let _16: &[core::fmt::rt::Argument<'_>; 2];
-      let _17: [core::fmt::rt::Argument<'_>; 2];
+      let mut _13: &std::boxed::Box<dyn std::fmt::Display>;
+      let mut _14: &u32;
+      let mut _16: core::fmt::rt::Argument<'_>;
+      let mut _17: &std::boxed::Box<dyn std::fmt::Display>;
       let mut _18: core::fmt::rt::Argument<'_>;
-      let mut _19: &std::boxed::Box<dyn std::fmt::Display>;
-      let _20: &std::boxed::Box<dyn std::fmt::Display>;
-      let mut _21: core::fmt::rt::Argument<'_>;
-      let mut _22: &u32;
-      let _23: &u32;
-      let mut _25: bool;
-      let mut _26: isize;
-      let mut _27: isize;
-      let mut _28: isize;
-+     let _29: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
-+     let _30: u32;
+      let mut _19: &u32;
+      let mut _20: &[&str; 3];
+      let _21: &[&str; 3];
+      let _22: [&str; 3];
+      let mut _23: &[core::fmt::rt::Argument<'_>; 2];
+      let _24: &[core::fmt::rt::Argument<'_>; 2];
+      let mut _26: &std::boxed::Box<dyn std::fmt::Display>;
+      let mut _27: &u32;
+      let mut _28: bool;
+      let mut _29: isize;
+      let mut _30: isize;
+      let mut _31: isize;
++     let _32: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
++     let _33: u32;
       scope 1 {
 -         debug foo => _1;
-+         debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _29;
-+         debug ((foo: Foo<T>).1: u32) => _30;
++         debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _32;
++         debug ((foo: Foo<T>).1: u32) => _33;
           let _5: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
           scope 2 {
               debug x => _5;
@@ -42,17 +43,29 @@
                   scope 4 {
                       debug x => _8;
                       let _8: std::boxed::Box<dyn std::fmt::Display>;
-                      let mut _24: &[&str; 3];
+                      let _12: (&std::boxed::Box<dyn std::fmt::Display>, &u32);
++                     let _34: &std::boxed::Box<dyn std::fmt::Display>;
++                     let _35: &u32;
+                      scope 5 {
+-                         debug args => _12;
++                         debug ((args: (&Box<dyn std::fmt::Display>, &u32)).0: &std::boxed::Box<dyn std::fmt::Display>) => _34;
++                         debug ((args: (&Box<dyn std::fmt::Display>, &u32)).1: &u32) => _35;
+                          let _15: [core::fmt::rt::Argument<'_>; 2];
+                          scope 6 {
+                              debug args => _15;
+                              let mut _25: &[&str; 3];
+                          }
+                      }
                   }
               }
           }
       }
   
       bb0: {
-          _25 = const false;
+          _28 = const false;
 -         StorageLive(_1);
-+         StorageLive(_29);
-+         StorageLive(_30);
++         StorageLive(_32);
++         StorageLive(_33);
 +         nop;
           StorageLive(_2);
           StorageLive(_3);
@@ -66,77 +79,93 @@
           _2 = Result::<Box<dyn std::fmt::Display>, <T as Err>::Err>::Ok(move _3);
           StorageDead(_3);
 -         _1 = Foo::<T> { x: move _2, y: const 7_u32 };
-+         _29 = move _2;
-+         _30 = const 7_u32;
++         _32 = move _2;
++         _33 = const 7_u32;
 +         nop;
           StorageDead(_2);
           StorageLive(_5);
-          _25 = const true;
+          _28 = const true;
 -         _5 = move (_1.0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>);
-+         _5 = move _29;
++         _5 = move _32;
           StorageLive(_6);
 -         _6 = copy (_1.1: u32);
-+         _6 = copy _30;
++         _6 = copy _33;
           _7 = discriminant(_5);
           switchInt(move _7) -> [0: bb2, otherwise: bb7];
       }
   
       bb2: {
           StorageLive(_8);
-          _25 = const false;
+          _28 = const false;
           _8 = move ((_5 as Ok).0: std::boxed::Box<dyn std::fmt::Display>);
           StorageLive(_9);
           StorageLive(_10);
           StorageLive(_11);
-          StorageLive(_12);
+-         StorageLive(_12);
++         StorageLive(_34);
++         StorageLive(_35);
++         nop;
           StorageLive(_13);
-          _24 = const foo::<T>::promoted[0];
-          _13 = &(*_24);
-          _12 = &(*_13);
+          _13 = &_8;
+          StorageLive(_14);
+          _14 = &_6;
+-         _12 = (move _13, move _14);
++         _34 = move _13;
++         _35 = move _14;
++         nop;
+          StorageDead(_14);
+          StorageDead(_13);
           StorageLive(_15);
           StorageLive(_16);
           StorageLive(_17);
-          StorageLive(_18);
-          StorageLive(_19);
-          StorageLive(_20);
-          _20 = &_8;
-          _19 = &(*_20);
-          _18 = core::fmt::rt::Argument::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _19) -> [return: bb3, unwind unreachable];
+-         _26 = deref_copy (_12.0: &std::boxed::Box<dyn std::fmt::Display>);
++         _26 = deref_copy _34;
+          _17 = &(*_26);
+          _16 = core::fmt::rt::Argument::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _17) -> [return: bb3, unwind unreachable];
       }
   
       bb3: {
-          StorageDead(_19);
-          StorageLive(_21);
-          StorageLive(_22);
-          StorageLive(_23);
-          _23 = &_6;
-          _22 = &(*_23);
-          _21 = core::fmt::rt::Argument::<'_>::new_display::<u32>(move _22) -> [return: bb4, unwind unreachable];
+          StorageDead(_17);
+          StorageLive(_18);
+          StorageLive(_19);
+-         _27 = deref_copy (_12.1: &u32);
++         _27 = deref_copy _35;
+          _19 = &(*_27);
+          _18 = core::fmt::rt::Argument::<'_>::new_display::<u32>(move _19) -> [return: bb4, unwind unreachable];
       }
   
       bb4: {
-          StorageDead(_22);
-          _17 = [move _18, move _21];
-          StorageDead(_21);
+          StorageDead(_19);
+          _15 = [move _16, move _18];
           StorageDead(_18);
-          _16 = &_17;
-          _15 = &(*_16);
-          _11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _12, move _15) -> [return: bb5, unwind unreachable];
+          StorageDead(_16);
+          StorageLive(_20);
+          StorageLive(_21);
+          _25 = const foo::<T>::promoted[0];
+          _21 = &(*_25);
+          _20 = &(*_21);
+          StorageLive(_23);
+          StorageLive(_24);
+          _24 = &_15;
+          _23 = &(*_24);
+          _11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _20, move _23) -> [return: bb5, unwind unreachable];
       }
   
       bb5: {
-          StorageDead(_15);
-          StorageDead(_12);
+          StorageDead(_24);
+          StorageDead(_23);
+          StorageDead(_21);
+          StorageDead(_20);
           _10 = _eprint(move _11) -> [return: bb6, unwind unreachable];
       }
   
       bb6: {
           StorageDead(_11);
-          StorageDead(_23);
-          StorageDead(_20);
-          StorageDead(_17);
-          StorageDead(_16);
-          StorageDead(_13);
+          StorageDead(_15);
+-         StorageDead(_12);
++         StorageDead(_34);
++         StorageDead(_35);
++         nop;
           StorageDead(_10);
           _9 = const ();
           StorageDead(_9);
@@ -156,22 +185,22 @@
   
       bb9: {
           StorageDead(_6);
-          _26 = discriminant(_5);
-          switchInt(move _26) -> [0: bb11, otherwise: bb13];
+          _29 = discriminant(_5);
+          switchInt(move _29) -> [0: bb11, otherwise: bb13];
       }
   
       bb10: {
-          _25 = const false;
+          _28 = const false;
           StorageDead(_5);
 -         StorageDead(_1);
-+         StorageDead(_29);
-+         StorageDead(_30);
++         StorageDead(_32);
++         StorageDead(_33);
 +         nop;
           return;
       }
   
       bb11: {
-          switchInt(copy _25) -> [0: bb10, otherwise: bb12];
+          switchInt(copy _28) -> [0: bb10, otherwise: bb12];
       }
   
       bb12: {
diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp
new file mode 100644
index 00000000000..212e0e174da
--- /dev/null
+++ b/tests/pretty/pin-ergonomics-hir.pp
@@ -0,0 +1,44 @@
+//@ pretty-compare-only
+//@ pretty-mode:hir
+//@ pp-exact:pin-ergonomics-hir.pp
+
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+#[prelude_import]
+use ::std::prelude::rust_2015::*;
+#[macro_use]
+extern crate std;
+
+use std::pin::Pin;
+
+struct Foo;
+
+impl Foo {
+    fn baz(&mut self) { }
+
+    fn baz_const(&self) { }
+
+    fn baz_lt<'a>(&mut self) { }
+
+    fn baz_const_lt(&self) { }
+}
+
+fn foo(_: Pin<&'_ mut Foo>) { }
+fn foo_lt<'a>(_: Pin<&'a mut Foo>) { }
+
+fn foo_const(_: Pin<&'_ Foo>) { }
+fn foo_const_lt(_: Pin<&'_ Foo>) { }
+
+fn bar() {
+    let mut x: Pin<&mut _> = &pin mut Foo;
+    foo(x.as_mut());
+    foo(x.as_mut());
+    foo_const(x);
+
+    let x: Pin<&_> = &pin const Foo;
+
+    foo_const(x);
+    foo_const(x);
+}
+
+fn main() { }
diff --git a/tests/pretty/pin-ergonomics-hir.rs b/tests/pretty/pin-ergonomics-hir.rs
new file mode 100644
index 00000000000..5f2158258f0
--- /dev/null
+++ b/tests/pretty/pin-ergonomics-hir.rs
@@ -0,0 +1,40 @@
+//@ pretty-compare-only
+//@ pretty-mode:hir
+//@ pp-exact:pin-ergonomics-hir.pp
+
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+
+use std::pin::Pin;
+
+struct Foo;
+
+impl Foo {
+    fn baz(&mut self) { }
+
+    fn baz_const(&self) { }
+
+    fn baz_lt<'a>(&mut self) { }
+
+    fn baz_const_lt(&self) { }
+}
+
+fn foo(_: Pin<&'_ mut Foo>) { }
+fn foo_lt<'a>(_: Pin<&'a mut Foo>) { }
+
+fn foo_const(_: Pin<&'_ Foo>) { }
+fn foo_const_lt(_: Pin<&'_ Foo>) { }
+
+fn bar() {
+    let mut x: Pin<&mut _> = &pin mut Foo;
+    foo(x.as_mut());
+    foo(x.as_mut());
+    foo_const(x);
+
+    let x: Pin<&_> = &pin const Foo;
+
+    foo_const(x);
+    foo_const(x);
+}
+
+fn main() { }
diff --git a/tests/pretty/pin-ergonomics.rs b/tests/pretty/pin-ergonomics.rs
index 47ffc97b118..8e8ced791b1 100644
--- a/tests/pretty/pin-ergonomics.rs
+++ b/tests/pretty/pin-ergonomics.rs
@@ -3,6 +3,8 @@
 #![feature(pin_ergonomics)]
 #![allow(dead_code, incomplete_features)]
 
+use std::pin::Pin;
+
 struct Foo;
 
 impl Foo {
@@ -21,4 +23,15 @@ fn foo_lt<'a>(_: &'a pin mut Foo) {}
 fn foo_const(_: &pin const Foo) {}
 fn foo_const_lt(_: &'_ pin const Foo) {}
 
+fn bar() {
+    let mut x: Pin<&mut _> = &pin mut Foo;
+    foo(x.as_mut());
+    foo(x.as_mut());
+    foo_const(x);
+
+    let x: Pin<&_> = &pin const Foo;
+    foo_const(x);
+    foo_const(x);
+}
+
 fn main() {}
diff --git a/tests/run-make/allow-warnings-cmdline-stability/rmake.rs b/tests/run-make/allow-warnings-cmdline-stability/rmake.rs
index 66ca3eb3383..033a06741e2 100644
--- a/tests/run-make/allow-warnings-cmdline-stability/rmake.rs
+++ b/tests/run-make/allow-warnings-cmdline-stability/rmake.rs
@@ -1,4 +1,4 @@
-//@ needs-target-std
+//@ ignore-cross-compile
 // Test that `-Awarnings` suppresses warnings for unstable APIs.
 
 use run_make_support::rustc;
diff --git a/tests/run-make/apple-deployment-target/rmake.rs b/tests/run-make/apple-deployment-target/rmake.rs
index 839e21b7496..7297a862224 100644
--- a/tests/run-make/apple-deployment-target/rmake.rs
+++ b/tests/run-make/apple-deployment-target/rmake.rs
@@ -41,7 +41,6 @@ fn main() {
 
     // Remove env vars to get `rustc`'s default
     let output = rustc()
-        .target(target())
         .env_remove("MACOSX_DEPLOYMENT_TARGET")
         .env_remove("IPHONEOS_DEPLOYMENT_TARGET")
         .env_remove("WATCHOS_DEPLOYMENT_TARGET")
@@ -58,7 +57,6 @@ fn main() {
     run_in_tmpdir(|| {
         let rustc = || {
             let mut rustc = rustc();
-            rustc.target(target());
             rustc.crate_type("lib");
             rustc.emit("obj");
             rustc.input("foo.rs");
@@ -82,7 +80,6 @@ fn main() {
 
         let rustc = || {
             let mut rustc = rustc();
-            rustc.target(target());
             rustc.crate_type("dylib");
             rustc.input("foo.rs");
             rustc.output("libfoo.dylib");
@@ -108,7 +105,6 @@ fn main() {
     run_in_tmpdir(|| {
         let rustc = || {
             let mut rustc = rustc();
-            rustc.target(target());
             rustc.crate_type("bin");
             rustc.input("foo.rs");
             rustc.output("foo");
@@ -147,7 +143,6 @@ fn main() {
     run_in_tmpdir(|| {
         let rustc = || {
             let mut rustc = rustc();
-            rustc.target(target());
             rustc.incremental("incremental");
             rustc.crate_type("lib");
             rustc.emit("obj");
diff --git a/tests/run-make/apple-sdk-version/rmake.rs b/tests/run-make/apple-sdk-version/rmake.rs
index 43e80577204..1f4f0ab8aef 100644
--- a/tests/run-make/apple-sdk-version/rmake.rs
+++ b/tests/run-make/apple-sdk-version/rmake.rs
@@ -24,8 +24,7 @@ fn has_sdk_version(file: &str, version: &str) {
 
 fn main() {
     // Fetch rustc's inferred deployment target.
-    let current_deployment_target =
-        rustc().target(target()).print("deployment-target").run().stdout_utf8();
+    let current_deployment_target = rustc().print("deployment-target").run().stdout_utf8();
     let current_deployment_target = current_deployment_target.split('=').last().unwrap().trim();
 
     // Fetch current SDK version via. xcrun.
@@ -45,7 +44,7 @@ fn main() {
     let current_sdk_version = current_sdk_version.trim();
 
     // Check the SDK version in the object file produced by the codegen backend.
-    rustc().target(target()).crate_type("lib").emit("obj").input("foo.rs").output("foo.o").run();
+    rustc().crate_type("lib").emit("obj").input("foo.rs").output("foo.o").run();
     // Set to 0, which means not set or "n/a".
     has_sdk_version("foo.o", "n/a");
 
@@ -53,7 +52,7 @@ fn main() {
     //
     // This is just to ensure that we don't set some odd version in `create_object_file`,
     // if the rmeta file is packed in a different way in the future, this can safely be removed.
-    rustc().target(target()).crate_type("rlib").input("foo.rs").output("libfoo.rlib").run();
+    rustc().crate_type("rlib").input("foo.rs").output("libfoo.rlib").run();
     // Extra .rmeta file (which is encoded as an object file).
     cmd("ar").arg("-x").arg("libfoo.rlib").arg("lib.rmeta").run();
     has_sdk_version("lib.rmeta", "n/a");
@@ -69,7 +68,6 @@ fn main() {
         // Test with clang
         let file_name = format!("foo_cc{file_ext}");
         rustc()
-            .target(target())
             .crate_type("bin")
             .arg("-Clinker-flavor=gcc")
             .input("foo.rs")
@@ -80,7 +78,6 @@ fn main() {
         // Test with ld64
         let file_name = format!("foo_ld{file_ext}");
         rustc()
-            .target(target())
             .crate_type("bin")
             .arg("-Clinker-flavor=ld")
             .input("foo.rs")
diff --git a/tests/run-make/arm64ec-import-export-static/export.rs b/tests/run-make/arm64ec-import-export-static/export.rs
new file mode 100644
index 00000000000..ca6ccf00ca1
--- /dev/null
+++ b/tests/run-make/arm64ec-import-export-static/export.rs
@@ -0,0 +1,27 @@
+#![crate_type = "dylib"]
+#![allow(internal_features)]
+#![feature(no_core, lang_items)]
+#![no_core]
+#![no_std]
+
+// This is needed because of #![no_core]:
+#[lang = "pointee_sized"]
+pub trait PointeeSized {}
+#[lang = "meta_sized"]
+pub trait MetaSized: PointeeSized {}
+#[lang = "sized"]
+pub trait Sized: MetaSized {}
+#[lang = "sync"]
+trait Sync {}
+impl Sync for i32 {}
+#[lang = "copy"]
+pub trait Copy {}
+impl Copy for i32 {}
+#[lang = "drop_in_place"]
+pub unsafe fn drop_in_place<T: ?Sized>(_: *mut T) {}
+#[no_mangle]
+extern "system" fn _DllMainCRTStartup(_: *const u8, _: u32, _: *const u8) -> u32 {
+    1
+}
+
+pub static VALUE: i32 = 42;
diff --git a/tests/run-make/arm64ec-import-export-static/import.rs b/tests/run-make/arm64ec-import-export-static/import.rs
new file mode 100644
index 00000000000..9d52db25125
--- /dev/null
+++ b/tests/run-make/arm64ec-import-export-static/import.rs
@@ -0,0 +1,12 @@
+#![crate_type = "cdylib"]
+#![allow(internal_features)]
+#![feature(no_core)]
+#![no_std]
+#![no_core]
+
+extern crate export;
+
+#[no_mangle]
+pub extern "C" fn func() -> i32 {
+    export::VALUE
+}
diff --git a/tests/run-make/arm64ec-import-export-static/rmake.rs b/tests/run-make/arm64ec-import-export-static/rmake.rs
new file mode 100644
index 00000000000..7fa31144810
--- /dev/null
+++ b/tests/run-make/arm64ec-import-export-static/rmake.rs
@@ -0,0 +1,15 @@
+// Test that a static can be exported from one crate and imported into another.
+//
+// This was broken for Arm64EC as only functions, not variables, should be
+// decorated with `#`.
+// See https://github.com/rust-lang/rust/issues/138541
+
+//@ needs-llvm-components: aarch64
+//@ only-windows
+
+use run_make_support::rustc;
+
+fn main() {
+    rustc().input("export.rs").target("aarch64-pc-windows-msvc").panic("abort").run();
+    rustc().input("import.rs").target("aarch64-pc-windows-msvc").panic("abort").run();
+}
diff --git a/tests/run-make/bin-emit-no-symbols/app.rs b/tests/run-make/bin-emit-no-symbols/app.rs
index e9dc1e9744f..ad74fcc43dc 100644
--- a/tests/run-make/bin-emit-no-symbols/app.rs
+++ b/tests/run-make/bin-emit-no-symbols/app.rs
@@ -12,7 +12,15 @@ fn panic(_: &PanicInfo) -> ! {
 }
 
 #[lang = "eh_personality"]
-fn eh() {}
+fn eh(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
 
 #[alloc_error_handler]
 fn oom(_: Layout) -> ! {
diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
index 63904bea622..cca528c4252 100644
--- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
+++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
@@ -3,7 +3,9 @@
 // prevent the creation of a functional binary.
 // See https://github.com/rust-lang/rust/pull/49878
 
-//@ ignore-cross-compile
+//@ needs-target-std
+//@ ignore-android: FIXME(#142855)
+//@ ignore-sgx: (x86 machine code cannot be directly executed)
 
 use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name};
 
diff --git a/tests/run-make/crate-circular-deps-link/a.rs b/tests/run-make/crate-circular-deps-link/a.rs
index a54f429550e..99b2b65049d 100644
--- a/tests/run-make/crate-circular-deps-link/a.rs
+++ b/tests/run-make/crate-circular-deps-link/a.rs
@@ -21,6 +21,12 @@ extern "C" fn __rust_foreign_exception() -> ! {
 }
 
 #[lang = "eh_personality"]
-fn eh_personality() {
+fn eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
     loop {}
 }
diff --git a/tests/run-make/crate-circular-deps-link/rmake.rs b/tests/run-make/crate-circular-deps-link/rmake.rs
index 6771fdec7e8..38b922c328f 100644
--- a/tests/run-make/crate-circular-deps-link/rmake.rs
+++ b/tests/run-make/crate-circular-deps-link/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // Test that previously triggered a linker failure with root cause
 // similar to one found in the issue #69368.
 //
diff --git a/tests/run-make/doctests-merge/rmake.rs b/tests/run-make/doctests-merge/rmake.rs
index 8236997d72d..7893d4988eb 100644
--- a/tests/run-make/doctests-merge/rmake.rs
+++ b/tests/run-make/doctests-merge/rmake.rs
@@ -1,4 +1,5 @@
-//@ needs-target-std
+//@ ignore-cross-compile (needs to run doctests)
+
 use std::path::Path;
 
 use run_make_support::{cwd, diff, rustc, rustdoc};
diff --git a/tests/run-make/doctests-runtool/rmake.rs b/tests/run-make/doctests-runtool/rmake.rs
index aaba4174910..bc406630932 100644
--- a/tests/run-make/doctests-runtool/rmake.rs
+++ b/tests/run-make/doctests-runtool/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile (needs to run host tool binary)
+
 // Tests behavior of rustdoc `--test-runtool`.
 
 use std::path::PathBuf;
diff --git a/tests/run-make/embed-metadata/rmake.rs b/tests/run-make/embed-metadata/rmake.rs
index a41716d1542..2de6575feb8 100644
--- a/tests/run-make/embed-metadata/rmake.rs
+++ b/tests/run-make/embed-metadata/rmake.rs
@@ -1,5 +1,6 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+//@ needs-crate-type: dylib
+
 // Tests the -Zembed-metadata compiler flag.
 // Tracking issue: https://github.com/rust-lang/rust/issues/139165
 
diff --git a/tests/run-make/embed-source-dwarf/rmake.rs b/tests/run-make/embed-source-dwarf/rmake.rs
index 99fad359054..f57e6aba13e 100644
--- a/tests/run-make/embed-source-dwarf/rmake.rs
+++ b/tests/run-make/embed-source-dwarf/rmake.rs
@@ -1,6 +1,8 @@
 //@ needs-target-std
 //@ ignore-windows
 //@ ignore-apple
+//@ ignore-wasm (`object` doesn't handle wasm object files)
+//@ ignore-cross-compile
 
 // This test should be replaced with one in tests/debuginfo once we can easily
 // tell via GDB or LLDB if debuginfo contains source code. Cheap tricks in LLDB
diff --git a/tests/run-make/emit-shared-files/rmake.rs b/tests/run-make/emit-shared-files/rmake.rs
index c8c113ce944..f88fe69aa9c 100644
--- a/tests/run-make/emit-shared-files/rmake.rs
+++ b/tests/run-make/emit-shared-files/rmake.rs
@@ -5,6 +5,8 @@
 // `all-shared` should only emit files that can be shared between crates.
 // See https://github.com/rust-lang/rust/pull/83478
 
+//@ needs-target-std
+
 use run_make_support::{has_extension, has_prefix, path, rustdoc, shallow_find_files};
 
 fn main() {
diff --git a/tests/run-make/emit-stack-sizes/rmake.rs b/tests/run-make/emit-stack-sizes/rmake.rs
index 886e875cfae..2e7f40896a5 100644
--- a/tests/run-make/emit-stack-sizes/rmake.rs
+++ b/tests/run-make/emit-stack-sizes/rmake.rs
@@ -7,8 +7,7 @@
 // See https://github.com/rust-lang/rust/pull/51946
 
 //@ needs-target-std
-//@ ignore-windows
-//@ ignore-apple
+//@ only-elf
 // Reason: this feature only works when the output object format is ELF.
 // This won't be the case on Windows/OSX - for example, OSX produces a Mach-O binary.
 
diff --git a/tests/run-make/env-dep-info/rmake.rs b/tests/run-make/env-dep-info/rmake.rs
index 97006a63205..18e6250d7d4 100644
--- a/tests/run-make/env-dep-info/rmake.rs
+++ b/tests/run-make/env-dep-info/rmake.rs
@@ -1,5 +1,7 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+//@ needs-crate-type: proc-macro
+//@ ignore-musl (FIXME: can't find `-lunwind`)
+
 // Inside dep-info emit files, #71858 made it so all accessed environment
 // variables are usefully printed. This test checks that this feature works
 // as intended by checking if the environment variables used in compilation
diff --git a/tests/run-make/exit-code/rmake.rs b/tests/run-make/exit-code/rmake.rs
index 5fdf920b55a..21dea06a55a 100644
--- a/tests/run-make/exit-code/rmake.rs
+++ b/tests/run-make/exit-code/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // Test that we exit with the correct exit code for successful / unsuccessful / ICE compilations
 
 use run_make_support::{rustc, rustdoc};
diff --git a/tests/run-make/export-executable-symbols/rmake.rs b/tests/run-make/export-executable-symbols/rmake.rs
index dc8c59b9c74..884c7362822 100644
--- a/tests/run-make/export-executable-symbols/rmake.rs
+++ b/tests/run-make/export-executable-symbols/rmake.rs
@@ -8,9 +8,8 @@
 // Reason: the export-executable-symbols flag only works on Unix
 // due to hardcoded platform-specific implementation
 // (See #85673)
-//@ ignore-wasm32
-//@ ignore-wasm64
-//@ needs-target-std
+//@ ignore-cross-compile
+//@ ignore-wasm
 
 use run_make_support::{bin_name, llvm_readobj, rustc};
 
diff --git a/tests/run-make/export/disambiguator/rmake.rs b/tests/run-make/export/disambiguator/rmake.rs
index f855e42d08e..afbe7f2cdbc 100644
--- a/tests/run-make/export/disambiguator/rmake.rs
+++ b/tests/run-make/export/disambiguator/rmake.rs
@@ -1,4 +1,8 @@
-//@ needs-target-std
+//@ ignore-cross-compile
+
+// NOTE: `sdylib`'s platform support is basically just `dylib`'s platform support.
+//@ needs-crate-type: dylib
+
 use run_make_support::rustc;
 
 fn main() {
diff --git a/tests/run-make/export/extern-opt/rmake.rs b/tests/run-make/export/extern-opt/rmake.rs
index a2f9ba28c2f..2e3a70b251c 100644
--- a/tests/run-make/export/extern-opt/rmake.rs
+++ b/tests/run-make/export/extern-opt/rmake.rs
@@ -1,4 +1,8 @@
-//@ needs-target-std
+//@ ignore-cross-compile
+
+// NOTE: `sdylib`'s platform support is basically that of `dylib`.
+//@ needs-crate-type: dylib
+
 use run_make_support::{dynamic_lib_name, rustc};
 
 fn main() {
diff --git a/tests/run-make/export/simple/rmake.rs b/tests/run-make/export/simple/rmake.rs
index f855e42d08e..6468e38c69b 100644
--- a/tests/run-make/export/simple/rmake.rs
+++ b/tests/run-make/export/simple/rmake.rs
@@ -1,4 +1,8 @@
-//@ needs-target-std
+//@ ignore-cross-compile
+
+// NOTE: `sdylib`'s platform support is basically that of `dylib`.
+//@ needs-crate-type: dylib
+
 use run_make_support::rustc;
 
 fn main() {
diff --git a/tests/run-make/extern-diff-internal-name/rmake.rs b/tests/run-make/extern-diff-internal-name/rmake.rs
index 1bae8decb05..c905de1d9a8 100644
--- a/tests/run-make/extern-diff-internal-name/rmake.rs
+++ b/tests/run-make/extern-diff-internal-name/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // In the following scenario:
 // 1. The crate foo, is referenced multiple times
 // 2. --extern foo=./path/to/libbar.rlib is specified to rustc
diff --git a/tests/run-make/extern-flag-fun/rmake.rs b/tests/run-make/extern-flag-fun/rmake.rs
index 181a76b7cfa..0b5e3c245c5 100644
--- a/tests/run-make/extern-flag-fun/rmake.rs
+++ b/tests/run-make/extern-flag-fun/rmake.rs
@@ -1,4 +1,4 @@
-//@ needs-target-std
+//@ ignore-cross-compile
 //
 // The --extern flag can override the default crate search of
 // the compiler and directly fetch a given path. There are a few rules
diff --git a/tests/run-make/extern-multiple-copies/rmake.rs b/tests/run-make/extern-multiple-copies/rmake.rs
index d9d769d178c..01e4beed1b5 100644
--- a/tests/run-make/extern-multiple-copies/rmake.rs
+++ b/tests/run-make/extern-multiple-copies/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // In this test, the rust library foo1 exists in two different locations, but only one
 // is required by the --extern flag. This test checks that the copy is ignored (as --extern
 // demands fetching only the original instance of foo1) and that no error is emitted, resulting
diff --git a/tests/run-make/extern-multiple-copies2/rmake.rs b/tests/run-make/extern-multiple-copies2/rmake.rs
index 4188d5bdc18..5937929a1f3 100644
--- a/tests/run-make/extern-multiple-copies2/rmake.rs
+++ b/tests/run-make/extern-multiple-copies2/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // Almost identical to `extern-multiple-copies`, but with a variation in the --extern calls
 // and the addition of #[macro_use] in the rust code files, which used to break --extern
 // until #33625.
diff --git a/tests/run-make/fmt-write-bloat/rmake.rs b/tests/run-make/fmt-write-bloat/rmake.rs
index 6875ef9ddc0..3348651d501 100644
--- a/tests/run-make/fmt-write-bloat/rmake.rs
+++ b/tests/run-make/fmt-write-bloat/rmake.rs
@@ -15,14 +15,9 @@
 //! `NO_DEBUG_ASSERTIONS=1`). If debug assertions are disabled, then we can check for the absence of
 //! additional `usize` formatting and padding related symbols.
 
-//@ ignore-windows
-// Reason:
-// - MSVC targets really need to parse the .pdb file (aka the debug information).
-//   On Windows there's an API for that (dbghelp) which maybe we can use
-// - MinGW targets have a lot of symbols included in their runtime which we can't avoid.
-//   We would need to make the symbols we're looking for more specific for this test to work.
 //@ ignore-cross-compile
 
+use run_make_support::artifact_names::bin_name;
 use run_make_support::env::no_debug_assertions;
 use run_make_support::rustc;
 use run_make_support::symbols::any_symbol_contains;
@@ -36,5 +31,5 @@ fn main() {
         // otherwise, add them to the list of symbols to deny.
         panic_syms.extend_from_slice(&["panicking", "panic_fmt", "pad_integral", "Display"]);
     }
-    assert!(!any_symbol_contains("main", &panic_syms));
+    assert!(!any_symbol_contains(bin_name("main"), &panic_syms));
 }
diff --git a/tests/run-make/ice-dep-cannot-find-dep/rmake.rs b/tests/run-make/ice-dep-cannot-find-dep/rmake.rs
index 1c136773f01..cb3cf39e436 100644
--- a/tests/run-make/ice-dep-cannot-find-dep/rmake.rs
+++ b/tests/run-make/ice-dep-cannot-find-dep/rmake.rs
@@ -9,6 +9,7 @@
 
 //@ only-x86_64
 //@ only-linux
+//@ ignore-cross-compile
 // Reason: This is a platform-independent issue, no need to waste time testing
 // everywhere.
 
diff --git a/tests/run-make/include-all-symbols-linking/rmake.rs b/tests/run-make/include-all-symbols-linking/rmake.rs
index 4f85ee179f5..61b83e7a07f 100644
--- a/tests/run-make/include-all-symbols-linking/rmake.rs
+++ b/tests/run-make/include-all-symbols-linking/rmake.rs
@@ -7,7 +7,9 @@
 // See https://github.com/rust-lang/rust/pull/95604
 // See https://github.com/rust-lang/rust/issues/47384
 
-//@ needs-target-std
+//@ ignore-cross-compile
+//@ needs-crate-type: cdylib
+//@ needs-dynamic-linking
 //@ ignore-wasm differences in object file formats causes errors in the llvm_objdump step.
 //@ ignore-windows differences in object file formats causes errors in the llvm_objdump step.
 
diff --git a/tests/run-make/incr-prev-body-beyond-eof/rmake.rs b/tests/run-make/incr-prev-body-beyond-eof/rmake.rs
index cfa8d5b46cd..cdecf127a2c 100644
--- a/tests/run-make/incr-prev-body-beyond-eof/rmake.rs
+++ b/tests/run-make/incr-prev-body-beyond-eof/rmake.rs
@@ -7,7 +7,7 @@
 // was hashed by rustc in addition to the span length, and the fix still
 // works.
 
-//@ needs-target-std
+//@ ignore-cross-compile
 
 use run_make_support::{rfs, rustc};
 
diff --git a/tests/run-make/incr-test-moved-file/rmake.rs b/tests/run-make/incr-test-moved-file/rmake.rs
index dfba95d3fed..9a00a14ae95 100644
--- a/tests/run-make/incr-test-moved-file/rmake.rs
+++ b/tests/run-make/incr-test-moved-file/rmake.rs
@@ -9,7 +9,7 @@
 // for successful compilation.
 // See https://github.com/rust-lang/rust/issues/83112
 
-//@ needs-target-std
+//@ ignore-cross-compile
 
 use run_make_support::{rfs, rustc};
 
diff --git a/tests/run-make/intrinsic-unreachable/rmake.rs b/tests/run-make/intrinsic-unreachable/rmake.rs
index bb189fbdcb5..ea9c0a1434a 100644
--- a/tests/run-make/intrinsic-unreachable/rmake.rs
+++ b/tests/run-make/intrinsic-unreachable/rmake.rs
@@ -4,6 +4,7 @@
 // which means the emitted artifacts should be shorter in length.
 // See https://github.com/rust-lang/rust/pull/16970
 
+//@ needs-target-std
 //@ needs-asm-support
 //@ ignore-windows
 // Reason: Because of Windows exception handling, the code is not necessarily any shorter.
diff --git a/tests/run-make/invalid-so/rmake.rs b/tests/run-make/invalid-so/rmake.rs
index ee886b5ee3a..9e5ce583ece 100644
--- a/tests/run-make/invalid-so/rmake.rs
+++ b/tests/run-make/invalid-so/rmake.rs
@@ -1,5 +1,7 @@
 //@ needs-target-std
-//
+//@ needs-crate-type: dylib
+//@ needs-dynamic-linking
+
 // When a fake library was given to the compiler, it would
 // result in an obscure and unhelpful error message. This test
 // creates a false "foo" dylib, and checks that the standard error
diff --git a/tests/run-make/issue-125484-used-dependencies/rmake.rs b/tests/run-make/issue-125484-used-dependencies/rmake.rs
index afcea34783f..67b0b600b47 100644
--- a/tests/run-make/issue-125484-used-dependencies/rmake.rs
+++ b/tests/run-make/issue-125484-used-dependencies/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // Non-regression test for issues #125474, #125484, #125646, with the repro taken from #125484. Some
 // queries use "used dependencies" while others use "speculatively loaded dependencies", and an
 // indexing ICE appeared in some cases when these were unexpectedly used in the same context.
diff --git a/tests/run-make/json-error-no-offset/rmake.rs b/tests/run-make/json-error-no-offset/rmake.rs
index 3f45778ca04..296d968540a 100644
--- a/tests/run-make/json-error-no-offset/rmake.rs
+++ b/tests/run-make/json-error-no-offset/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // The byte positions in json format error logging used to have a small, difficult
 // to predict offset. This was changed to be the top of the file every time in #42973,
 // and this test checks that the measurements appearing in the standard error are correct.
diff --git a/tests/run-make/link-args-order/rmake.rs b/tests/run-make/link-args-order/rmake.rs
index a4591ea3949..7a67c12f74c 100644
--- a/tests/run-make/link-args-order/rmake.rs
+++ b/tests/run-make/link-args-order/rmake.rs
@@ -1,5 +1,6 @@
 //@ needs-target-std
-//
+//@ ignore-wasm (explicit linker invocations)
+
 // Passing linker arguments to the compiler used to be lost or reordered in a messy way
 // as they were passed further to the linker. This was fixed in #70665, and this test
 // checks that linker arguments remain intact and in the order they were originally passed in.
diff --git a/tests/run-make/link-dedup/rmake.rs b/tests/run-make/link-dedup/rmake.rs
index 0148817f987..874e6e0083b 100644
--- a/tests/run-make/link-dedup/rmake.rs
+++ b/tests/run-make/link-dedup/rmake.rs
@@ -1,5 +1,5 @@
 //@ needs-target-std
-//
+//@ ignore-musl (not passed consecutively)
 // When native libraries are passed to the linker, there used to be an annoyance
 // where multiple instances of the same library in a row would cause duplication in
 // outputs. This has been fixed, and this test checks that it stays fixed.
@@ -9,7 +9,7 @@
 
 use std::fmt::Write;
 
-use run_make_support::{is_msvc, rustc};
+use run_make_support::{is_msvc, rustc, target};
 
 fn main() {
     rustc().input("depa.rs").run();
@@ -33,9 +33,11 @@ fn needle_from_libs(libs: &[&str]) -> String {
     let mut needle = String::new();
     for lib in libs {
         if is_msvc() {
-            let _ = needle.write_fmt(format_args!(r#""{lib}.lib" "#));
+            needle.write_fmt(format_args!(r#""{lib}.lib" "#)).unwrap();
+        } else if target().contains("wasm") {
+            needle.write_fmt(format_args!(r#""-l" "{lib}" "#)).unwrap();
         } else {
-            let _ = needle.write_fmt(format_args!(r#""-l{lib}" "#));
+            needle.write_fmt(format_args!(r#""-l{lib}" "#)).unwrap();
         }
     }
     needle.pop(); // remove trailing space
diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs
index eb1bbbff8ef..26d03fd2d70 100644
--- a/tests/run-make/linker-warning/rmake.rs
+++ b/tests/run-make/linker-warning/rmake.rs
@@ -1,4 +1,5 @@
-//@ needs-target-std
+//@ ignore-cross-compile (need to run fake linker)
+
 use run_make_support::{Rustc, diff, regex, rustc};
 
 fn run_rustc() -> Rustc {
@@ -56,7 +57,8 @@ fn main() {
         diff()
             .expected_file("short-error.txt")
             .actual_text("(linker error)", out.stderr())
-            .normalize(r#"/rustc[^/]*/"#, "/rustc/")
+            .normalize(r#"/rustc[^/_-]*/"#, "/rustc/")
+            .normalize("libpanic_abort", "libpanic_unwind")
             .normalize(
                 regex::escape(run_make_support::build_root().to_str().unwrap()),
                 "/build-root",
diff --git a/tests/run-make/moved-src-dir-fingerprint-ice/rmake.rs b/tests/run-make/moved-src-dir-fingerprint-ice/rmake.rs
index 0d96b40e8a4..70a0853848c 100644
--- a/tests/run-make/moved-src-dir-fingerprint-ice/rmake.rs
+++ b/tests/run-make/moved-src-dir-fingerprint-ice/rmake.rs
@@ -12,7 +12,7 @@
 // sessions.
 // See https://github.com/rust-lang/rust/issues/85019
 
-//@ needs-target-std
+//@ ignore-cross-compile
 
 use run_make_support::{rfs, rust_lib_name, rustc};
 
diff --git a/tests/run-make/mte-ffi/rmake.rs b/tests/run-make/mte-ffi/rmake.rs
index 50f5f14191b..a8da0dc0ee0 100644
--- a/tests/run-make/mte-ffi/rmake.rs
+++ b/tests/run-make/mte-ffi/rmake.rs
@@ -22,11 +22,7 @@ fn run_test(variant: &str) {
         flags
     };
     println!("{variant} test...");
-    rustc()
-        .input(format!("foo_{variant}.rs"))
-        .target(target())
-        .linker("aarch64-linux-gnu-gcc")
-        .run();
+    rustc().input(format!("foo_{variant}.rs")).linker("aarch64-linux-gnu-gcc").run();
     gcc()
         .input(format!("bar_{variant}.c"))
         .input(dynamic_lib_name("foo"))
diff --git a/tests/run-make/naked-symbol-visibility/rmake.rs b/tests/run-make/naked-symbol-visibility/rmake.rs
index c69a9ef9eeb..d73eafcaefe 100644
--- a/tests/run-make/naked-symbol-visibility/rmake.rs
+++ b/tests/run-make/naked-symbol-visibility/rmake.rs
@@ -1,5 +1,8 @@
 //@ ignore-windows
 //@ only-x86_64
+//@ needs-target-std
+//@ needs-crate-type: dylib
+
 use run_make_support::object::ObjectSymbol;
 use run_make_support::object::read::{File, Object, Symbol};
 use run_make_support::targets::is_windows;
diff --git a/tests/run-make/native-lib-alt-naming/rmake.rs b/tests/run-make/native-lib-alt-naming/rmake.rs
index a1dc002533f..e989cece603 100644
--- a/tests/run-make/native-lib-alt-naming/rmake.rs
+++ b/tests/run-make/native-lib-alt-naming/rmake.rs
@@ -1,10 +1,8 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // On MSVC the alternative naming format for static libraries (`libfoo.a`) is accepted in addition
 // to the default format (`foo.lib`).
 
-//REMOVE@ only-msvc
-
 use run_make_support::rustc;
 
 fn main() {
diff --git a/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs
index e06be13d9b9..4fb0690531a 100644
--- a/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs
+++ b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs
@@ -3,8 +3,9 @@
 // This test is the same as native-link-modifier-rustc, but without rlibs.
 // See https://github.com/rust-lang/rust/issues/99425
 
-//@ needs-target-std
+//@ ignore-cross-compile
 //@ ignore-apple
+//@ ignore-wasm
 // Reason: linking fails due to the unusual ".ext" staticlib name.
 
 use run_make_support::rustc;
diff --git a/tests/run-make/no-alloc-shim/foo.rs b/tests/run-make/no-alloc-shim/foo.rs
index b5d0d394d2b..a22307f41b3 100644
--- a/tests/run-make/no-alloc-shim/foo.rs
+++ b/tests/run-make/no-alloc-shim/foo.rs
@@ -12,7 +12,13 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! {
 }
 
 #[no_mangle]
-extern "C" fn rust_eh_personality() {
+extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
     loop {}
 }
 
diff --git a/tests/run-make/no-builtins-attribute/filecheck.main.txt b/tests/run-make/no-builtins-attribute/filecheck.main.txt
index ecd650bdca8..7cbe94f5728 100644
--- a/tests/run-make/no-builtins-attribute/filecheck.main.txt
+++ b/tests/run-make/no-builtins-attribute/filecheck.main.txt
@@ -1,5 +1,5 @@
-CHECK: declare void @foo()
+CHECK: declare{{.*}} void @foo()
 CHECK-SAME: #[[ATTR_3:[0-9]+]]
 
-CHECK: attributes #[[ATTR_3]] 
+CHECK: attributes #[[ATTR_3]]
 CHECK-SAME: no-builtins
diff --git a/tests/run-make/no-builtins-attribute/rmake.rs b/tests/run-make/no-builtins-attribute/rmake.rs
index 038958f19ed..f08316e14ce 100644
--- a/tests/run-make/no-builtins-attribute/rmake.rs
+++ b/tests/run-make/no-builtins-attribute/rmake.rs
@@ -1,5 +1,4 @@
 //@ needs-target-std
-//
 // `no_builtins` is an attribute related to LLVM's optimizations. In order to ensure that it has an
 // effect on link-time optimizations (LTO), it should be added to function declarations in a crate.
 // This test uses the `llvm-filecheck` tool to determine that this attribute is successfully
@@ -11,5 +10,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc};
 fn main() {
     rustc().input("no_builtins.rs").emit("link").run();
     rustc().input("main.rs").emit("llvm-ir").run();
+
     llvm_filecheck().patterns("filecheck.main.txt").stdin_buf(rfs::read("main.ll")).run();
 }
diff --git a/tests/run-make/no-builtins-lto/rmake.rs b/tests/run-make/no-builtins-lto/rmake.rs
index a1d9dc43e71..c7c075f1a66 100644
--- a/tests/run-make/no-builtins-lto/rmake.rs
+++ b/tests/run-make/no-builtins-lto/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // The rlib produced by a no_builtins crate should be explicitly linked
 // during compilation, and as a result be present in the linker arguments.
 // See the comments inside this file for more details.
diff --git a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs
index 5c437a3fe00..aa6b83cf062 100644
--- a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs
+++ b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs
@@ -1,4 +1,4 @@
-//@ needs-target-std
+//@ ignore-cross-compile
 use run_make_support::{rfs, rustc};
 
 fn main() {
diff --git a/tests/run-make/proc-macro-three-crates/rmake.rs b/tests/run-make/proc-macro-three-crates/rmake.rs
index e5a3385acbc..4dfc32fe7e4 100644
--- a/tests/run-make/proc-macro-three-crates/rmake.rs
+++ b/tests/run-make/proc-macro-three-crates/rmake.rs
@@ -1,5 +1,7 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+//@ needs-crate-type: proc-macro
+//@ ignore-musl (FIXME: can't find `-lunwind`)
+
 // A compiler bug caused the following issue:
 // If a crate A depends on crate B, and crate B
 // depends on crate C, and crate C contains a procedural
diff --git a/tests/run-make/relro-levels/rmake.rs b/tests/run-make/relro-levels/rmake.rs
index 91d106a03e4..67aa7c155ea 100644
--- a/tests/run-make/relro-levels/rmake.rs
+++ b/tests/run-make/relro-levels/rmake.rs
@@ -1,6 +1,7 @@
 // This tests the different -Crelro-level values, and makes sure that they work properly.
 
 //@ only-linux
+//@ ignore-cross-compile
 
 use run_make_support::{llvm_readobj, rustc};
 
diff --git a/tests/run-make/repr128-dwarf/rmake.rs b/tests/run-make/repr128-dwarf/rmake.rs
index 1372d2bcc46..96c65d7d897 100644
--- a/tests/run-make/repr128-dwarf/rmake.rs
+++ b/tests/run-make/repr128-dwarf/rmake.rs
@@ -1,4 +1,5 @@
-//@ needs-target-std
+//@ ignore-cross-compile
+//@ ignore-wasm (`object` can't handle wasm object files)
 //@ ignore-windows
 // This test should be replaced with one in tests/debuginfo once GDB or LLDB support 128-bit enums.
 
diff --git a/tests/run-make/reproducible-build-2/rmake.rs b/tests/run-make/reproducible-build-2/rmake.rs
index 0e1781dbfbe..1de5ca1e6f7 100644
--- a/tests/run-make/reproducible-build-2/rmake.rs
+++ b/tests/run-make/reproducible-build-2/rmake.rs
@@ -6,23 +6,37 @@
 // Outputs should be identical.
 // See https://github.com/rust-lang/rust/issues/34902
 
-//@ needs-target-std
-//@ ignore-windows
-// Reasons:
-// 1. The object files are reproducible, but their paths are not, which causes
-// the first assertion in the test to fail.
-// 2. When the sysroot gets copied, some symlinks must be re-created,
-// which is a privileged action on Windows.
+//@ ignore-cross-compile
 
-use run_make_support::{rfs, rust_lib_name, rustc};
+//@ ignore-windows-gnu
+// GNU Linker for Windows is non-deterministic.
+
+use run_make_support::{bin_name, is_windows_msvc, rfs, rust_lib_name, rustc};
 
 fn main() {
     // test 1: fat lto
     rustc().input("reproducible-build-aux.rs").run();
-    rustc().input("reproducible-build.rs").arg("-Clto=fat").output("reproducible-build").run();
-    rfs::rename("reproducible-build", "reproducible-build-a");
-    rustc().input("reproducible-build.rs").arg("-Clto=fat").output("reproducible-build").run();
-    assert_eq!(rfs::read("reproducible-build"), rfs::read("reproducible-build-a"));
+    let make_reproducible_build = || {
+        let mut reproducible_build = rustc();
+        reproducible_build
+            .input("reproducible-build.rs")
+            .arg("-Clto=fat")
+            .output(bin_name("reproducible-build"));
+        if is_windows_msvc() {
+            // Avoids timestamps, etc. when linking.
+            reproducible_build.arg("-Clink-arg=/Brepro");
+        }
+        reproducible_build.run();
+    };
+    make_reproducible_build();
+    rfs::rename(bin_name("reproducible-build"), "reproducible-build-a");
+    if is_windows_msvc() {
+        // Linker acts differently if there is already a PDB file with the same
+        // name.
+        rfs::remove_file("reproducible-build.pdb");
+    }
+    make_reproducible_build();
+    assert_eq!(rfs::read(bin_name("reproducible-build")), rfs::read("reproducible-build-a"));
 
     // test 2: sysroot
     let sysroot = rustc().print("sysroot").run().stdout_utf8();
diff --git a/tests/run-make/rlib-format-packed-bundled-libs-2/rmake.rs b/tests/run-make/rlib-format-packed-bundled-libs-2/rmake.rs
index 70d1ead85b5..50c3f7f6df1 100644
--- a/tests/run-make/rlib-format-packed-bundled-libs-2/rmake.rs
+++ b/tests/run-make/rlib-format-packed-bundled-libs-2/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // `-Z packed_bundled_libs` is an unstable rustc flag that makes the compiler
 // only require a native library and no supplementary object files to compile.
 // This test simply checks that this flag can be passed alongside verbatim syntax
diff --git a/tests/run-make/rustc-macro-dep-files/rmake.rs b/tests/run-make/rustc-macro-dep-files/rmake.rs
index eb4771fea7a..2eb490e5d23 100644
--- a/tests/run-make/rustc-macro-dep-files/rmake.rs
+++ b/tests/run-make/rustc-macro-dep-files/rmake.rs
@@ -1,5 +1,7 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+//@ needs-crate-type: proc-macro
+//@ ignore-musl (FIXME: can't find `-lunwind`)
+
 // --emit dep-info used to print all macro-generated code it could
 // find as if it was part of a nonexistent file named "proc-macro source",
 // which is not a valid path. After this was fixed in #36776, this test checks
@@ -10,7 +12,7 @@ use run_make_support::{diff, rustc, target};
 
 fn main() {
     rustc().input("foo.rs").run();
-    rustc().input("bar.rs").target(target()).emit("dep-info").run();
+    rustc().input("bar.rs").emit("dep-info").run();
     // The emitted file should not contain "proc-macro source".
     diff().expected_file("correct.d").actual_file("bar.d").run();
 }
diff --git a/tests/run-make/rustdoc-default-output/rmake.rs b/tests/run-make/rustdoc-default-output/rmake.rs
index 5f9c501e528..06720445a35 100644
--- a/tests/run-make/rustdoc-default-output/rmake.rs
+++ b/tests/run-make/rustdoc-default-output/rmake.rs
@@ -3,10 +3,10 @@
 // ensures the output of rustdoc's help menu is as expected.
 // See https://github.com/rust-lang/rust/issues/88756
 
-use run_make_support::{diff, rustdoc};
+use run_make_support::{bare_rustdoc, diff};
 
 fn main() {
-    let out = rustdoc().run().stdout_utf8();
+    let out = bare_rustdoc().run().stdout_utf8();
     diff()
         .expected_file("output-default.stdout")
         .actual_text("actual", out)
diff --git a/tests/run-make/rustdoc-dep-info/rmake.rs b/tests/run-make/rustdoc-dep-info/rmake.rs
index 6902bfc21ca..db7a00a5ce2 100644
--- a/tests/run-make/rustdoc-dep-info/rmake.rs
+++ b/tests/run-make/rustdoc-dep-info/rmake.rs
@@ -1,6 +1,8 @@
 // This is a simple smoke test for rustdoc's `--emit dep-info` feature. It prints out
 // information about dependencies in a Makefile-compatible format, as a `.d` file.
 
+//@ needs-target-std
+
 use run_make_support::assertion_helpers::assert_contains;
 use run_make_support::{path, rfs, rustdoc};
 
diff --git a/tests/run-make/rustdoc-determinism/rmake.rs b/tests/run-make/rustdoc-determinism/rmake.rs
index 5a030c6f496..921baef4a97 100644
--- a/tests/run-make/rustdoc-determinism/rmake.rs
+++ b/tests/run-make/rustdoc-determinism/rmake.rs
@@ -1,6 +1,8 @@
 // Assert that the search index is generated deterministically, regardless of the
 // order that crates are documented in.
 
+//@ needs-target-std
+
 use run_make_support::{diff, path, rustdoc};
 
 fn main() {
diff --git a/tests/run-make/rustdoc-error-lines/rmake.rs b/tests/run-make/rustdoc-error-lines/rmake.rs
index 0d8c500ed1e..e15d91e9387 100644
--- a/tests/run-make/rustdoc-error-lines/rmake.rs
+++ b/tests/run-make/rustdoc-error-lines/rmake.rs
@@ -1,6 +1,8 @@
 // Assert that the search index is generated deterministically, regardless of the
 // order that crates are documented in.
 
+//@ needs-target-std
+
 use run_make_support::rustdoc;
 
 fn main() {
diff --git a/tests/run-make/rustdoc-io-error/rmake.rs b/tests/run-make/rustdoc-io-error/rmake.rs
index 31441d7ebc5..766091abf97 100644
--- a/tests/run-make/rustdoc-io-error/rmake.rs
+++ b/tests/run-make/rustdoc-io-error/rmake.rs
@@ -13,6 +13,7 @@
 // containers would use a non-root user, but this leads to issues with
 // `mkfs.ext4 -d`, as well as mounting a loop device for the rootfs.
 //@ ignore-windows - the `set_readonly` functions doesn't work on folders.
+//@ needs-target-std
 
 use run_make_support::{path, rfs, rustdoc};
 
diff --git a/tests/run-make/rustdoc-map-file/rmake.rs b/tests/run-make/rustdoc-map-file/rmake.rs
index 50dcc603c02..802c924d580 100644
--- a/tests/run-make/rustdoc-map-file/rmake.rs
+++ b/tests/run-make/rustdoc-map-file/rmake.rs
@@ -1,6 +1,8 @@
 // This test ensures that all items from `foo` are correctly generated into the `redirect-map.json`
 // file with `--generate-redirect-map` rustdoc option.
 
+//@ needs-target-std
+
 use run_make_support::rfs::read_to_string;
 use run_make_support::{path, rustdoc, serde_json};
 
diff --git a/tests/run-make/rustdoc-output-path/rmake.rs b/tests/run-make/rustdoc-output-path/rmake.rs
index 7f6accf26c2..cece914e947 100644
--- a/tests/run-make/rustdoc-output-path/rmake.rs
+++ b/tests/run-make/rustdoc-output-path/rmake.rs
@@ -1,5 +1,7 @@
 // Checks that if the output folder doesn't exist, rustdoc will create it.
 
+//@ needs-target-std
+
 use run_make_support::{path, rustdoc};
 
 fn main() {
diff --git a/tests/run-make/rustdoc-output-stdout/rmake.rs b/tests/run-make/rustdoc-output-stdout/rmake.rs
index d2fd0451163..e6c007978bd 100644
--- a/tests/run-make/rustdoc-output-stdout/rmake.rs
+++ b/tests/run-make/rustdoc-output-stdout/rmake.rs
@@ -1,6 +1,8 @@
 // This test verifies that rustdoc `-o -` prints JSON on stdout and doesn't generate
 // a JSON file.
 
+//@ needs-target-std
+
 use run_make_support::path_helpers::{cwd, has_extension, read_dir_entries_recursive};
 use run_make_support::{rustdoc, serde_json};
 
diff --git a/tests/run-make/rustdoc-test-args/rmake.rs b/tests/run-make/rustdoc-test-args/rmake.rs
index fddb3795402..7c0223cf732 100644
--- a/tests/run-make/rustdoc-test-args/rmake.rs
+++ b/tests/run-make/rustdoc-test-args/rmake.rs
@@ -1,3 +1,5 @@
+//@ ignore-cross-compile (needs to run doctest binary)
+
 use std::iter;
 use std::path::Path;
 
diff --git a/tests/run-make/rustdoc-themes/rmake.rs b/tests/run-make/rustdoc-themes/rmake.rs
index 4577e47d47e..681e6baaee3 100644
--- a/tests/run-make/rustdoc-themes/rmake.rs
+++ b/tests/run-make/rustdoc-themes/rmake.rs
@@ -1,5 +1,7 @@
 // Test that rustdoc will properly load in a theme file and display it in the theme selector.
 
+//@ needs-target-std
+
 use std::path::Path;
 
 use run_make_support::{htmldocck, rfs, rustdoc, source_root};
diff --git a/tests/run-make/rustdoc-verify-output-files/rmake.rs b/tests/run-make/rustdoc-verify-output-files/rmake.rs
index a4d4050b745..181d321997b 100644
--- a/tests/run-make/rustdoc-verify-output-files/rmake.rs
+++ b/tests/run-make/rustdoc-verify-output-files/rmake.rs
@@ -1,3 +1,5 @@
+//@ needs-target-std
+
 use std::path::{Path, PathBuf};
 
 use run_make_support::{assert_dirs_are_equal, rfs, rustdoc};
diff --git a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
index a82a1965a9c..231a5b36600 100644
--- a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
+++ b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
@@ -1,3 +1,5 @@
+//@ needs-target-std
+
 use run_make_support::{htmldocck, rustdoc};
 
 fn main() {
diff --git a/tests/run-make/rustdoc-with-output-option/rmake.rs b/tests/run-make/rustdoc-with-output-option/rmake.rs
index f7fbbec6986..2c1b76f5b9c 100644
--- a/tests/run-make/rustdoc-with-output-option/rmake.rs
+++ b/tests/run-make/rustdoc-with-output-option/rmake.rs
@@ -1,3 +1,5 @@
+//@ needs-target-std
+
 use run_make_support::{htmldocck, rustdoc};
 
 fn main() {
diff --git a/tests/run-make/sanitizer-dylib-link/program.rs b/tests/run-make/sanitizer-dylib-link/program.rs
index 1026c7f89ba..dbf885d343f 100644
--- a/tests/run-make/sanitizer-dylib-link/program.rs
+++ b/tests/run-make/sanitizer-dylib-link/program.rs
@@ -1,4 +1,4 @@
-#[cfg_attr(windows, link(name = "library.dll.lib", modifiers = "+verbatim"))]
+#[cfg_attr(windows, link(name = "library", kind = "raw-dylib"))]
 #[cfg_attr(not(windows), link(name = "library"))]
 extern "C" {
     fn overflow();
diff --git a/tests/run-make/share-generics-dylib/rmake.rs b/tests/run-make/share-generics-dylib/rmake.rs
index 2d52cd43db7..ae9e51abffd 100644
--- a/tests/run-make/share-generics-dylib/rmake.rs
+++ b/tests/run-make/share-generics-dylib/rmake.rs
@@ -1,5 +1,7 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+//@ needs-dynamic-linking
+//@ needs-crate-type: dylib
+
 // This test makes sure all generic instances get re-exported from Rust dylibs for use by
 // `-Zshare-generics`. There are two rlibs (`instance_provider_a` and `instance_provider_b`)
 // which both provide an instance of `Cell<i32>::set`. There is `instance_user_dylib` which is
diff --git a/tests/run-make/short-ice/rmake.rs b/tests/run-make/short-ice/rmake.rs
index 8377954f467..483def62fc7 100644
--- a/tests/run-make/short-ice/rmake.rs
+++ b/tests/run-make/short-ice/rmake.rs
@@ -5,8 +5,9 @@
 // See https://github.com/rust-lang/rust/issues/107910
 
 //@ needs-target-std
-//@ ignore-windows
-// Reason: the assert_eq! on line 32 fails, as error output on Windows is different.
+//@ ignore-i686-pc-windows-msvc
+// Reason: the assert_eq! on line 37 fails, almost seems like it missing debug info?
+// Haven't been able to reproduce locally, but it happens on CI.
 
 use run_make_support::rustc;
 
@@ -29,10 +30,16 @@ fn main() {
 
     let rustc_query_count_full = count_lines_with(rust_test_log_2, "rustc_query_");
 
-    assert!(rust_test_log_1.lines().count() < rust_test_log_2.lines().count());
+    assert!(
+        rust_test_log_1.lines().count() < rust_test_log_2.lines().count(),
+        "Short backtrace should be shorter than full backtrace.\nShort backtrace:\n\
+        {rust_test_log_1}\nFull backtrace:\n{rust_test_log_2}"
+    );
     assert_eq!(
         count_lines_with(rust_test_log_2, "__rust_begin_short_backtrace"),
-        count_lines_with(rust_test_log_2, "__rust_end_short_backtrace")
+        count_lines_with(rust_test_log_2, "__rust_end_short_backtrace"),
+        "Full backtrace should contain the short backtrace markers.\nFull backtrace:\n\
+        {rust_test_log_2}"
     );
     assert!(count_lines_with(rust_test_log_1, "rustc_query_") + 5 < rustc_query_count_full);
     assert!(rustc_query_count_full > 5);
diff --git a/tests/run-make/static-pie/rmake.rs b/tests/run-make/static-pie/rmake.rs
index 1557c170f56..cb24c0495be 100644
--- a/tests/run-make/static-pie/rmake.rs
+++ b/tests/run-make/static-pie/rmake.rs
@@ -49,7 +49,6 @@ fn test(compiler: &str) {
 
     rustc()
         .input("test-aslr.rs")
-        .target(target())
         .linker(compiler)
         .arg("-Clinker-flavor=gcc")
         .arg("-Ctarget-feature=+crt-static")
diff --git a/tests/run-make/staticlib-thin-archive/rmake.rs b/tests/run-make/staticlib-thin-archive/rmake.rs
index 1fb56ac0538..5281e9f8c9c 100644
--- a/tests/run-make/staticlib-thin-archive/rmake.rs
+++ b/tests/run-make/staticlib-thin-archive/rmake.rs
@@ -1,5 +1,5 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+
 // Regression test for https://github.com/rust-lang/rust/issues/107407 which
 // checks that rustc can read thin archive. Before the object crate added thin
 // archive support rustc would add emit object files to the staticlib and after
diff --git a/tests/run-make/stdin-rustc/rmake.rs b/tests/run-make/stdin-rustc/rmake.rs
index 318d569a760..6ae33766b90 100644
--- a/tests/run-make/stdin-rustc/rmake.rs
+++ b/tests/run-make/stdin-rustc/rmake.rs
@@ -1,9 +1,9 @@
-//@ needs-target-std
+//@ ignore-cross-compile
 //! This test checks rustc `-` (stdin) support
 
 use std::path::PathBuf;
 
-use run_make_support::{is_windows, rustc};
+use run_make_support::{bin_name, rustc};
 
 const HELLO_WORLD: &str = r#"
 fn main() {
@@ -16,11 +16,7 @@ const NOT_UTF8: &[u8] = &[0xff, 0xff, 0xff];
 fn main() {
     // echo $HELLO_WORLD | rustc -
     rustc().arg("-").stdin_buf(HELLO_WORLD).run();
-    assert!(
-        PathBuf::from(if !is_windows() { "rust_out" } else { "rust_out.exe" })
-            .try_exists()
-            .unwrap()
-    );
+    assert!(PathBuf::from(bin_name("rust_out")).try_exists().unwrap());
 
     // echo $NOT_UTF8 | rustc -
     rustc().arg("-").stdin_buf(NOT_UTF8).run_fail().assert_stderr_contains(
diff --git a/tests/run-make/stdin-rustdoc/rmake.rs b/tests/run-make/stdin-rustdoc/rmake.rs
index 30f97b8a2cd..0420eac1993 100644
--- a/tests/run-make/stdin-rustdoc/rmake.rs
+++ b/tests/run-make/stdin-rustdoc/rmake.rs
@@ -1,3 +1,5 @@
+//@ ignore-cross-compile (needs to run doctests)
+
 //! This test checks rustdoc `-` (stdin) handling
 
 use std::path::PathBuf;
diff --git a/tests/run-make/symbol-visibility/rmake.rs b/tests/run-make/symbol-visibility/rmake.rs
index 0175158d08b..49c8e87707b 100644
--- a/tests/run-make/symbol-visibility/rmake.rs
+++ b/tests/run-make/symbol-visibility/rmake.rs
@@ -1,5 +1,7 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+//@ needs-crate-type: dylib, cdylib, proc-macro
+//@ needs-dynamic-linking
+
 // Dynamic libraries on Rust used to export a very high amount of symbols,
 // going as far as filling the output with mangled names and generic function
 // names. After the rework of #38117, this test checks that no mangled Rust symbols
diff --git a/tests/run-make/sysroot-crates-are-unstable/rmake.rs b/tests/run-make/sysroot-crates-are-unstable/rmake.rs
index c81c7fafdab..20ad01bef61 100644
--- a/tests/run-make/sysroot-crates-are-unstable/rmake.rs
+++ b/tests/run-make/sysroot-crates-are-unstable/rmake.rs
@@ -31,7 +31,6 @@ fn check_crate_is_unstable(cr: &Crate) {
     // Trying to use this crate from a user program should fail.
     let output = rustc()
         .crate_type("rlib")
-        .target(target())
         .extern_(name, path)
         .input("-")
         .stdin_buf(format!("extern crate {name};"))
diff --git a/tests/run-make/textrel-on-minimal-lib/rmake.rs b/tests/run-make/textrel-on-minimal-lib/rmake.rs
index 625ded70ad6..08e2b45a75f 100644
--- a/tests/run-make/textrel-on-minimal-lib/rmake.rs
+++ b/tests/run-make/textrel-on-minimal-lib/rmake.rs
@@ -6,25 +6,23 @@
 // See https://github.com/rust-lang/rust/issues/68794
 
 //@ ignore-cross-compile
-//@ ignore-windows
-// Reason: There is no `bar.dll` produced by CC to run readobj on
 
 use run_make_support::{
-    cc, dynamic_lib_name, extra_c_flags, extra_cxx_flags, llvm_readobj, rustc, static_lib_name,
+    bin_name, cc, extra_c_flags, extra_cxx_flags, llvm_readobj, rustc, static_lib_name,
 };
 
 fn main() {
     rustc().input("foo.rs").run();
     cc().input("bar.c")
         .input(static_lib_name("foo"))
-        .out_exe(&dynamic_lib_name("bar"))
+        .out_exe(&bin_name("bar"))
         .arg("-fPIC")
         .arg("-shared")
         .args(extra_c_flags())
         .args(extra_cxx_flags())
         .run();
     llvm_readobj()
-        .input(dynamic_lib_name("bar"))
+        .input(bin_name("bar"))
         .arg("--dynamic")
         .run()
         .assert_stdout_not_contains("TEXTREL");
diff --git a/tests/run-make/track-path-dep-info/rmake.rs b/tests/run-make/track-path-dep-info/rmake.rs
index 4b98a1b48d5..955c46f7e68 100644
--- a/tests/run-make/track-path-dep-info/rmake.rs
+++ b/tests/run-make/track-path-dep-info/rmake.rs
@@ -1,5 +1,7 @@
-//@ needs-target-std
-//
+//@ ignore-cross-compile
+//@ needs-crate-type: proc-macro
+//@ ignore-musl (FIXME: can't find `-lunwind`)
+
 // This test checks the functionality of `tracked_path::path`, a procedural macro
 // feature that adds a dependency to another file inside the procmacro. In this case,
 // the text file is added through this method, and the test checks that the compilation
diff --git a/tests/run-make/used/rmake.rs b/tests/run-make/used/rmake.rs
index daed69c1b38..bcdb84132d3 100644
--- a/tests/run-make/used/rmake.rs
+++ b/tests/run-make/used/rmake.rs
@@ -1,5 +1,6 @@
 //@ needs-target-std
-//
+//@ ignore-wasm (`object` can't handle wasm object files)
+
 // This test ensures that the compiler is keeping static variables, even if not referenced
 // by another part of the program, in the output object file.
 //
diff --git a/tests/rustdoc-js-std/doc-alias-use.js b/tests/rustdoc-js-std/doc-alias-use.js
new file mode 100644
index 00000000000..e08d94533a9
--- /dev/null
+++ b/tests/rustdoc-js-std/doc-alias-use.js
@@ -0,0 +1,12 @@
+// AsciiChar has a doc alias on its reexport and we
+// want to make sure that actually works correctly,
+// since apperently there are no other tests for this.
+
+const EXPECTED = [
+    {
+        'query': 'AsciiChar',
+        'others': [
+            { 'path': 'core::ascii', 'name': 'Char' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/big-result.rs b/tests/rustdoc-js/big-result.rs
index 4dfecd6aaad..c7a52aac1a2 100644
--- a/tests/rustdoc-js/big-result.rs
+++ b/tests/rustdoc-js/big-result.rs
@@ -1,4 +1,3 @@
-#![feature(concat_idents)]
 #![allow(nonstandard_style)]
 /// Generate 250 items that all match the query, starting with the longest.
 /// Those long items should be dropped from the result set, and the short ones
diff --git a/tests/rustdoc-json/attrs/cold.rs b/tests/rustdoc-json/attrs/cold.rs
new file mode 100644
index 00000000000..e219345d669
--- /dev/null
+++ b/tests/rustdoc-json/attrs/cold.rs
@@ -0,0 +1,3 @@
+//@ is "$.index[?(@.name=='cold_fn')].attrs" '["#[attr = Cold]"]'
+#[cold]
+pub fn cold_fn() {}
diff --git a/tests/rustdoc-json/attrs/must_use.rs b/tests/rustdoc-json/attrs/must_use.rs
index 64df8e5f509..3ca6f5a75a5 100644
--- a/tests/rustdoc-json/attrs/must_use.rs
+++ b/tests/rustdoc-json/attrs/must_use.rs
@@ -1,9 +1,9 @@
 #![no_std]
 
-//@ is "$.index[?(@.name=='example')].attrs" '["#[must_use]"]'
+//@ is "$.index[?(@.name=='example')].attrs" '["#[attr = MustUse]"]'
 #[must_use]
 pub fn example() -> impl Iterator<Item = i64> {}
 
-//@ is "$.index[?(@.name=='explicit_message')].attrs" '["#[must_use = \"does nothing if you do not use it\"]"]'
+//@ is "$.index[?(@.name=='explicit_message')].attrs" '["#[attr = MustUse {reason: \"does nothing if you do not use it\"}]"]'
 #[must_use = "does nothing if you do not use it"]
 pub fn explicit_message() -> impl Iterator<Item = i64> {}
diff --git a/tests/rustdoc-json/attrs/optimize.rs b/tests/rustdoc-json/attrs/optimize.rs
new file mode 100644
index 00000000000..0bed0ad18c3
--- /dev/null
+++ b/tests/rustdoc-json/attrs/optimize.rs
@@ -0,0 +1,13 @@
+#![feature(optimize_attribute)]
+
+//@ is "$.index[?(@.name=='speed')].attrs" '["#[attr = Optimize(Speed)]"]'
+#[optimize(speed)]
+pub fn speed() {}
+
+//@ is "$.index[?(@.name=='size')].attrs" '["#[attr = Optimize(Size)]"]'
+#[optimize(size)]
+pub fn size() {}
+
+//@ is "$.index[?(@.name=='none')].attrs" '["#[attr = Optimize(DoNotOptimize)]"]'
+#[optimize(none)]
+pub fn none() {}
diff --git a/tests/rustdoc-json/generic-args.rs b/tests/rustdoc-json/generic-args.rs
new file mode 100644
index 00000000000..b4a73a046b5
--- /dev/null
+++ b/tests/rustdoc-json/generic-args.rs
@@ -0,0 +1,23 @@
+pub struct MyStruct(u32);
+
+pub trait MyTrait {
+    type MyType;
+    fn my_fn(&self);
+}
+
+impl MyTrait for MyStruct {
+    type MyType = u32;
+    fn my_fn(&self) {}
+}
+
+//@ is "$.index[?(@.name=='my_fn1')].inner.function.sig.inputs[0][1].qualified_path.args" null
+//@ is "$.index[?(@.name=='my_fn1')].inner.function.sig.inputs[0][1].qualified_path.self_type.resolved_path.args" null
+pub fn my_fn1(_: <MyStruct as MyTrait>::MyType) {}
+
+//@ is "$.index[?(@.name=='my_fn2')].inner.function.sig.inputs[0][1].dyn_trait.traits[0].trait.args.angle_bracketed.constraints[0].args" null
+pub fn my_fn2(_: IntoIterator<Item = MyStruct, IntoIter = impl Clone>) {}
+
+//@ is "$.index[?(@.name=='my_fn3')].inner.function.sig.inputs[0][1].impl_trait[0].trait_bound.trait.args.parenthesized.inputs" []
+pub fn my_fn3(f: impl FnMut()) {}
+
+fn main() {}
diff --git a/tests/rustdoc/target-feature.rs b/tests/rustdoc/target-feature.rs
new file mode 100644
index 00000000000..59a08a0ca94
--- /dev/null
+++ b/tests/rustdoc/target-feature.rs
@@ -0,0 +1,38 @@
+#![crate_name = "foo"]
+
+//@ has 'foo/index.html'
+
+//@ has   - '//dl[@class="item-table"]/dt[1]//a' 'f1_safe'
+//@ has   - '//dl[@class="item-table"]/dt[1]//code' 'popcnt'
+//@ count - '//dl[@class="item-table"]/dt[1]//sup' 0
+//@ has   - '//dl[@class="item-table"]/dt[2]//a' 'f2_not_safe'
+//@ has   - '//dl[@class="item-table"]/dt[2]//code' 'avx2'
+//@ count - '//dl[@class="item-table"]/dt[2]//sup' 1
+//@ has   - '//dl[@class="item-table"]/dt[2]//sup' '⚠'
+
+#[target_feature(enable = "popcnt")]
+//@ has 'foo/fn.f1_safe.html'
+//@ matches - '//pre[@class="rust item-decl"]' '^pub fn f1_safe'
+//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//        'Available with target feature popcnt only.'
+pub fn f1_safe() {}
+
+//@ has 'foo/fn.f2_not_safe.html'
+//@ matches - '//pre[@class="rust item-decl"]' '^pub unsafe fn f2_not_safe()'
+//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//        'Available with target feature avx2 only.'
+#[target_feature(enable = "avx2")]
+pub unsafe fn f2_not_safe() {}
+
+//@ has 'foo/fn.f3_multifeatures_in_attr.html'
+//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//        'Available on target features popcnt and avx2 only.'
+#[target_feature(enable = "popcnt", enable = "avx2")]
+pub fn f3_multifeatures_in_attr() {}
+
+//@ has 'foo/fn.f4_multi_attrs.html'
+//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//        'Available on target features popcnt and avx2 only.'
+#[target_feature(enable = "popcnt")]
+#[target_feature(enable = "avx2")]
+pub fn f4_multi_attrs() {}
diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
index 90e07bed40e..72b5cfb9063 100644
--- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
+++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
@@ -92,6 +92,21 @@ static EXPRS: &[&str] = &[
     "#[attr] loop {}.field",
     "(#[attr] loop {}).field",
     "loop { #![attr] }.field",
+    // Attributes on a Binary, Cast, Assign, AssignOp, and Range expression
+    // require parentheses. Without parentheses `#[attr] lo..hi` means
+    // `(#[attr] lo)..hi`, and `#[attr] ..hi` is invalid syntax.
+    "#[attr] (1 + 1)",
+    "#[attr] (1 as T)",
+    "#[attr] (x = 1)",
+    "#[attr] (x += 1)",
+    "#[attr] (lo..hi)",
+    "#[attr] (..hi)",
+    // If the attribute were not present on the binary operation, it would be
+    // legal to render this without not just the inner parentheses, but also the
+    // outer ones. `return x + .. .field` (Yes, really.) Currently the
+    // pretty-printer does not take advantage of this edge case.
+    "(return #[attr] (x + ..)).field",
+    "(return x + ..).field",
     // Grammar restriction: break value starting with a labeled loop is not
     // allowed, except if the break is also labeled.
     "break 'outer 'inner: loop {} + 2",
@@ -158,7 +173,12 @@ struct Unparenthesize;
 impl MutVisitor for Unparenthesize {
     fn visit_expr(&mut self, e: &mut Expr) {
         while let ExprKind::Paren(paren) = &mut e.kind {
+            let paren_attrs = mem::take(&mut e.attrs);
             *e = mem::replace(paren, Expr::dummy());
+            if !paren_attrs.is_empty() {
+                assert!(e.attrs.is_empty());
+                e.attrs = paren_attrs;
+            }
         }
         mut_visit::walk_expr(self, e);
     }
diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs
index fa651baa7bc..87504b8301f 100644
--- a/tests/ui-fulldeps/run-compiler-twice.rs
+++ b/tests/ui-fulldeps/run-compiler-twice.rs
@@ -18,7 +18,7 @@ extern crate rustc_span;
 use std::path::{Path, PathBuf};
 
 use rustc_interface::{Linker, interface};
-use rustc_session::config::{Input, Options, OutFileName, OutputType, OutputTypes};
+use rustc_session::config::{Input, Options, OutFileName, OutputType, OutputTypes, Sysroot};
 use rustc_span::FileName;
 
 fn main() {
@@ -32,7 +32,7 @@ fn main() {
         panic!("expected sysroot (and optional linker)");
     }
 
-    let sysroot = PathBuf::from(&args[1]);
+    let sysroot = Sysroot::new(Some(PathBuf::from(&args[1])));
     let linker = args.get(2).map(PathBuf::from);
 
     // compiletest sets the current dir to `output_base_dir` when running.
@@ -43,7 +43,7 @@ fn main() {
     compile(src.to_string(), tmpdir.join("out"), sysroot.clone(), linker.as_deref());
 }
 
-fn compile(code: String, output: PathBuf, sysroot: PathBuf, linker: Option<&Path>) {
+fn compile(code: String, output: PathBuf, sysroot: Sysroot, linker: Option<&Path>) {
     let mut opts = Options::default();
     opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]);
     opts.sysroot = sysroot;
diff --git a/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr b/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr
index f54b6803b34..0c969b9c6d8 100644
--- a/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr
+++ b/tests/ui-fulldeps/rustc-dev-remap.only-remap.stderr
@@ -9,6 +9,7 @@ LL |     type Result = NotAValidResultType;
              ControlFlow<T>
 note: required by a bound in `rustc_ast::visit::Visitor::Result`
   --> /rustc-dev/xyz/compiler/rustc_ast/src/visit.rs:LL:COL
+   = note: this error originates in the macro `common_visitor_and_walkers` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr b/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr
index 438c23458e2..6ac8c3046f6 100644
--- a/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr
+++ b/tests/ui-fulldeps/rustc-dev-remap.remap-unremap.stderr
@@ -10,8 +10,9 @@ LL |     type Result = NotAValidResultType;
 note: required by a bound in `rustc_ast::visit::Visitor::Result`
   --> $COMPILER_DIR_REAL/rustc_ast/src/visit.rs:LL:COL
    |
-LL |     type Result: VisitorResult = ();
-   |                  ^^^^^^^^^^^^^ required by this bound in `Visitor::Result`
+LL | common_visitor_and_walkers!(Visitor<'a>);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Visitor::Result`
+   = note: this error originates in the macro `common_visitor_and_walkers` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/SUMMARY.md b/tests/ui/SUMMARY.md
new file mode 100644
index 00000000000..72b673dd50a
--- /dev/null
+++ b/tests/ui/SUMMARY.md
@@ -0,0 +1,1596 @@
+# UI Test Suite Categories
+
+This is a high-level summary of the organization of the UI test suite (`tests/ui/`). It is not intended to be *prescriptive*, but instead provide a quick survey of existing groupings.
+
+For now, only immediate subdirectories under `tests/ui/` are described, but these subdirectories can themselves include a `SUMMARY.md` to further describe their own organization and intent, should that be helpful.
+
+## `tests/ui/abi`
+
+These tests deal with *Application Binary Interfaces* (ABI), mostly relating to function name mangling (and the `#[no_mangle]` attribute), calling conventions, or compiler flags which affect ABI.
+
+Tests for unsupported ABIs can be made cross-platform by using the `extern "rust-invalid"` ABI, which is considered unsupported on every platform.
+
+## `tests/ui/allocator`
+
+These tests exercise `#![feature(allocator_api)]` and the `#[global_allocator]` attribute.
+
+See [Allocator traits and `std::heap` #32838](https://github.com/rust-lang/rust/issues/32838).
+
+## `tests/ui/alloc-error`
+
+These tests exercise alloc error handling.
+
+See <https://doc.rust-lang.org/std/alloc/fn.handle_alloc_error.html>.
+
+## `tests/ui/annotate-snippet`
+
+These tests exercise the [`annotate-snippets`]-based emitter implementation.
+
+[`annotate-snippets`] is an initiative to share the diagnostics emitting infrastructure between rustc and cargo to reduce duplicate maintenance effort and divergence. See <https://github.com/rust-lang/rust/issues/59346> about the initiative.
+
+[`annotate-snippets`]: https://github.com/rust-lang/annotate-snippets-rs
+
+## `tests/ui/anon-params`
+
+These tests deal with anonymous parameters (no name, only type), a deprecated feature that becomes a hard error in Edition 2018.
+
+## `tests/ui/argfile`: External files providing command line arguments
+
+These tests exercise rustc reading command line arguments from an externally provided argfile (`@argsfile`).
+
+See [Implement `@argsfile` to read arguments from command line #63576](https://github.com/rust-lang/rust/issues/63576).
+
+## `tests/ui/array-slice-vec`: Arrays, slices and vectors
+
+Exercises various aspects surrounding basic collection types `[]`, `&[]` and `Vec`. E.g. type-checking, out-of-bounds indices, attempted instructions which are allowed in other programming languages, and more.
+
+## `tests/ui/argument-suggestions`: Argument suggestions
+
+Calling a function with the wrong number of arguments causes a compilation failure, but the compiler is able to, in some cases, provide suggestions on how to fix the error, such as which arguments to add or delete. These tests exercise the quality of such diagnostics.
+
+## `tests/ui/asm`: `asm!` macro
+
+These tests exercise the `asm!` macro, which is used for adding inline assembly.
+
+See:
+
+- [Inline assembly | Reference](https://doc.rust-lang.org/reference/inline-assembly.html)
+- [`core::arch::asm`](https://doc.rust-lang.org/core/arch/macro.asm.html)
+- [`core::arch::global_asm`](https://doc.rust-lang.org/core/arch/macro.global_asm.html)
+
+This directory contains subdirectories representing various architectures such as `riscv` or `aarch64`. If a test is specifically related to an architecture's particularities, it should be placed within the appropriate subdirectory.Architecture-agnostic tests should be placed below `tests/ui/asm/` directly.
+
+## `tests/ui/associated-consts`: Associated Constants
+
+These tests exercise associated constants in traits and impls, on aspects such as definitions, usage, and type checking in associated contexts.
+
+## `tests/ui/associated-inherent-types`: Inherent Associated Types
+
+These tests cover associated types defined directly within inherent impls (not in traits).
+
+See [RFC 0195 Associated items - Inherent associated items](https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md#inherent-associated-items).
+
+## `tests/ui/associated-item`: Associated Items
+
+Tests for all kinds of associated items within traits and implementations. This directory serves as a catch-all for tests that don't fit the other more specific associated item directories.
+
+## `tests/ui/associated-type-bounds`: Associated Type Bounds
+
+These tests exercise associated type bounds, the feature that gives users a shorthand to express nested type bounds that would otherwise need to be expressed with nested `impl Trait` or broken into several `where` clauses.
+
+See:
+
+- [RFC 2289 Associated Type Bounds](https://rust-lang.github.io/rfcs/2289-associated-type-bounds.html)
+- [Stabilize associated type bounds (RFC 2289) #122055](https://github.com/rust-lang/rust/pull/122055)
+
+## `tests/ui/associated-types`: Trait Associated Types
+
+Tests focused on associated types. If the associated type is not in a trait definition, it belongs in the `tests/ui/associated-inherent-types/` directory. Aspects exercised include e.g. default associated types, overriding defaults, and type inference.
+
+See [Associated Types | Reference](https://doc.rust-lang.org/reference/items/associated-items.html#associated-types).
+
+## `tests/ui/async-await`: Async/Await
+
+Tests for the async/await related features. E.g. async functions, await expressions, and their interaction with other language features.
+
+## `tests/ui/attributes`: Compiler Attributes
+
+Tests for language attributes and compiler attributes. E.g. built-in attributes like `#[derive(..)]`, `#[cfg(..)]`, and `#[repr(..)]`, or proc-macro attributes. See [Attributes | Reference](https://doc.rust-lang.org/reference/attributes.html).
+
+## `tests/ui/auto-traits`: Auto Traits
+
+There are built-in auto traits (`Send`, `Sync`, etc.) but it is possible to define more with the unstable keyword `auto` through `#![feature(auto_traits)]`.
+
+See [Tracking Issue for auto traits (`auto_traits`) -- formerly called opt-in built-in traits (`optin_builtin_traits`) #13231](https://github.com/rust-lang/rust/issues/13231).
+
+## `tests/ui/autodiff`: Automatic Differentiation
+
+The `#[autodiff]` macro supports automatic differentiation.
+
+See [Tracking Issue for autodiff #124509](https://github.com/rust-lang/rust/issues/124509).
+
+## `tests/ui/autoref-autoderef`: Automatic Referencing/Dereferencing
+
+Tests for automatic referencing and dereferencing behavior, such as automatically adding reference operations (`&` or `&mut`) to make a value match a method's receiver type. Sometimes abbreviated as "auto-ref" or "auto-deref".
+
+## `tests/ui/auxiliary/`: Auxiliary files for tests directly under `tests/ui`.
+
+This top-level `auxiliary` subdirectory contains support files for tests immediately under `tests/ui/`.
+
+**FIXME(#133895)**: tests immediately under `tests/ui/` should be rehomed to more suitable subdirectories, after which this subdirectory can be removed.
+
+## `tests/ui/backtrace/`: Backtraces
+
+Runtime panics and error handling generate backtraces to assist in debugging and diagnostics.
+
+## `tests/ui/bench/`: Benchmarks and performance
+
+This directory was originally meant to contain tests related to time complexity and benchmarking.
+
+However, only a single test was ever added to this category: https://github.com/rust-lang/rust/pull/32062
+
+**FIXME**: It is also unclear what would happen were this test to "fail" - would it cause the test suite to remain stuck on this test for a much greater duration than normal?
+
+## `tests/ui/binding/`: Pattern Binding
+
+Tests for pattern binding in match expressions, let statements, and other binding contexts. E.g. binding modes and refutability. See [Patterns | Reference](https://doc.rust-lang.org/reference/patterns.html).
+
+## `tests/ui/binop/`: Binary operators
+
+Tests for binary operators (such as `==`, `&&` or `^`). E.g. overloading, type checking, and diagnostics for invalid operations.
+
+## `tests/ui/blind/`: `struct` or `mod` inside a `mod` having a duplicate identifier
+
+Tests exercising name resolution.
+
+**FIXME**: Probably move to `tests/ui/resolve/`.
+
+## `tests/ui/block-result/`: Block results and returning
+
+Tests for block expression results. E.g. specifying the correct return types, semicolon handling, type inference, and expression/statement differences (for example, the difference between `1` and `1;`).
+
+## `tests/ui/bootstrap/`: RUSTC_BOOTSTRAP environment variable
+
+Meta tests for stability mechanisms surrounding [`RUSTC_BOOTSTRAP`](https://doc.rust-lang.org/nightly/unstable-book/compiler-environment-variables/RUSTC_BOOTSTRAP.html), which is coordinated between `rustc` and the build system, `bootstrap`.
+
+## `tests/ui/borrowck/`: Borrow Checking
+
+Tests for borrow checking. E.g. lifetime analysis, borrowing rules, and diagnostics.
+
+## `tests/ui/box/`: Box Behavior
+
+Tests for `Box<T>` smart pointer and `#![feature(box_patterns)]`. E.g. allocation, deref coercion, and edge cases in box pattern matching and placement.
+
+See:
+
+- [`std::box::Boxed`](https://doc.rust-lang.org/std/boxed/struct.Box.html)
+- [Tracking issue for `box_patterns` feature #29641](https://github.com/rust-lang/rust/issues/29641)
+
+## `tests/ui/btreemap/`: B-Tree Maps
+
+Tests focused on `BTreeMap` collections and their compiler interactions. E.g. collection patterns, iterator behavior, and trait implementations specific to `BTreeMap`. See [`std::collections::BTreeMap`](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html).
+
+## `tests/ui/builtin-superkinds/`: Built-in Trait Hierarchy Tests
+
+Tests for built-in trait hierarchy (Send, Sync, Sized, etc.) and their supertrait relationships. E.g. auto traits and marker trait constraints.
+
+See [RFC 3729: Hierarchy of Sized traits](https://github.com/rust-lang/rfcs/pull/3729).
+
+Defining custom auto traits with the `auto` keyword belongs to `tests/ui/auto-traits/` instead.
+
+## `tests/ui/cast/`: Type Casting
+
+Tests for type casting using the `as` operator. Includes tests for valid/invalid casts between primitive types, trait objects, and custom types. For example, check that trying to cast `i32` into `bool` results in a helpful error message.
+
+See [Type cast expressions | Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html).
+
+## `tests/ui/cfg/`: Configuration Attribute
+
+Tests for `#[cfg]` conditional compilation attribute. E.g. feature flags, target architectures, and other configuration predicates and options.
+
+See [Conditional compilation | Reference](https://doc.rust-lang.org/reference/conditional-compilation.html).
+
+## `tests/ui/check-cfg/`: Configuration Checks
+
+Tests for the `--check-cfg` compiler mechanism  for checking cfg configurations, for `#[cfg(..)]` and `cfg!(..)`.
+
+See [Checking conditional configurations | The rustc book](https://doc.rust-lang.org/rustc/check-cfg.html).
+
+## `tests/ui/closure_context/`: Closure type inference in context
+
+Tests for closure type inference with respect to surrounding scopes, mostly quality of diagnostics.
+
+## `tests/ui/closure-expected-type/`: Closure type inference
+
+Tests targeted at how we deduce the types of closure arguments. This process is a result of some heuristics which take into account the *expected type* we have alongside the *actual types* that we get from inputs.
+
+**FIXME**: Appears to have significant overlap with `tests/ui/closure_context` and `tests/ui/functions-closures/closure-expected-type`. Needs further investigation.
+
+## `tests/ui/closures/`: General Closure Tests
+
+Any closure-focused tests that does not fit in the other more specific closure subdirectories belong here. E.g. syntax, `move`, lifetimes.
+
+## `tests/ui/cmse-nonsecure/`: `cmse-nonsecure` ABIs
+
+Tests for `extern "cmse-nonsecure-call"` and `extern "cmse-nonsecure-entry"` functions. Used specifically for the Armv8-M architecture, the former marks Secure functions with additional behaviours, such as adding a special symbol and constraining the number of parameters, while the latter alters function pointers to indicate they are non-secure and to handle them differently than usual.
+
+See:
+
+- [`cmse_nonsecure_entry` | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/language-features/cmse-nonsecure-entry.html)
+- [`abi_cmse_nonsecure_call` | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/language-features/abi-cmse-nonsecure-call.html)
+
+## `tests/ui/codegen/`: Code Generation
+
+Tests that exercise code generation. E.g. codegen flags (starting with `-C` on the command line), LLVM IR output, optimizations (and the various `opt-level`s), and target-specific code generation (such as tests specific to `x86_64`).
+
+## `tests/ui/codemap_tests/`: Source Mapping
+
+Tests that exercise source code mapping.
+
+## `tests/ui/coercion/`: Type Coercion
+
+Tests for implicit type coercion behavior, where the types of some values are changed automatically when compatible depending on the context. E.g. automatic dereferencing or downgrading a `&mut` into a `&`.
+
+See [Type coercions | Reference](https://doc.rust-lang.org/reference/type-coercions.html).
+
+## `tests/ui/coherence/`: Trait Implementation Coherence
+
+Tests for trait coherence rules, which govern where trait implementations can be defined. E.g. orphan rule, and overlap checks.
+
+See [Coherence | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/coherence.html#coherence).
+
+## `tests/ui/coinduction/`: Coinductive Trait Resolution
+
+Tests for coinduction in trait solving which may involve infinite proof trees.
+
+See:
+
+- [Coinduction | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/solve/coinduction.html).
+- [Inductive cycles | Chalk](https://rust-lang.github.io/chalk/book/recursive/inductive_cycles.html#inductive-cycles)/
+
+This directory only contains one highly specific test. Other coinduction tests can be found down the deeply located `tests/ui/traits/next-solver/cycles/coinduction/` subdirectory.
+
+## `tests/ui/command/`: `std::process::Command`
+
+This directory is actually for the standard library [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html) type, where some tests are too difficult or inconvenient to write as unit tests or integration tests within the standard library itself.
+
+**FIXME**: the test `command-line-diagnostics` seems to have been misplaced in this category.
+
+## `tests/ui/compare-method/`: Trait implementation and definition comparisons
+
+Some traits' implementation must be compared with their definition, checking for problems such as the implementation having stricter requirements (such as needing to implement `Copy`).
+
+This subdirectory is *not* intended comparison traits (`PartialEq`, `Eq`, `PartialOrd`, `Ord`).
+
+## `tests/ui/compiletest-self-test/`: compiletest "meta" tests
+
+Meta test suite of the test harness `compiletest` itself.
+
+## `tests/ui/conditional-compilation/`: Conditional Compilation
+
+Tests for `#[cfg]` attribute or `--cfg` flags, used to compile certain files or code blocks only if certain conditions are met (such as developing on a specific architecture).
+
+**FIXME**: There is significant overlap with `tests/ui/cfg`, which even contains a `tests/ui/cfg/conditional-compile.rs` test. Also investigate `tests/ui/check-cfg`.
+
+## `tests/ui/confuse-field-and-method/`: Field/Method Ambiguity
+
+If a developer tries to create a `struct` where one of the fields is a closure function, it becomes unclear whether `struct.field()` is accessing the field itself or trying to call the closure function within as a method.
+
+**FIXME**: does this really need to be its own immediate subdirectory?
+
+## `tests/ui/const-generics/`: Constant Generics
+
+Tests for const generics, allowing types to be parameterized by constant values. It is generally observed in the form `<const N: Type>` after the `fn` or `struct` keywords. Includes tests for const expressions in generic contexts and associated type bounds.
+
+See:
+
+- [Tracking Issue for complex generic constants: `feature(generic_const_exprs)` #76560](https://github.com/rust-lang/rust/issues/76560)
+- [Const generics | Reference](https://doc.rust-lang.org/reference/items/generics.html#const-generics)
+
+## `tests/ui/const_prop/`: Constant Propagation
+
+Tests exercising `ConstProp` mir-opt pass (mostly regression tests). See <https://blog.rust-lang.org/inside-rust/2019/12/02/const-prop-on-by-default/>.
+
+## `tests/ui/const-ptr/`: Constant Pointers
+
+Tests exercise const raw pointers. E.g. pointer arithmetic, casting and dereferencing, always with a `const`.
+
+See:
+
+- [`std::primitive::pointer`](https://doc.rust-lang.org/std/primitive.pointer.html)
+- [`std::ptr`](https://doc.rust-lang.org/std/ptr/index.html)
+- [Pointer types | Reference](https://doc.rust-lang.org/reference/types/pointer.html)
+
+## `tests/ui/consts/`: General Constant Evaluation
+
+Anything to do with constants, which does not fit in the previous two `const` categories, goes here. This does not always imply use of the `const` keyword - other values considered constant, such as defining an enum variant as `enum Foo { Variant = 5 }` also counts.
+
+## `tests/ui/contracts/`: Contracts feature
+
+Tests exercising `#![feature(contracts)]`.
+
+See [Tracking Issue for Contracts #128044](https://github.com/rust-lang/rust/issues/128044).
+
+## `tests/ui/coroutine/`: Coroutines feature and `gen` blocks
+
+Tests for `#![feature(coroutines)]` and `gen` blocks, it belongs here.
+
+See:
+
+- [Coroutines | The Unstable book](https://doc.rust-lang.org/beta/unstable-book/language-features/coroutines.html)
+- [RFC 3513 Gen blocks](https://rust-lang.github.io/rfcs/3513-gen-blocks.html)
+
+## `tests/ui/coverage-attr/`: `#[coverage]` attribute
+
+Tests for `#![feature(coverage_attribute)]`. See [Tracking issue for function attribute `#[coverage]`](https://github.com/rust-lang/rust/issues/84605).
+
+## `tests/ui/crate-loading/`: Crate Loading
+
+Tests for crate resolution and loading behavior, including `extern crate` declarations, `--extern` flags, or the `use` keyword.
+
+## `tests/ui/cross/`: Various tests related to the concept of "cross"
+
+**FIXME**: The unifying topic of these tests appears to be that their filenames begin with the word "cross". The similarities end there - one test is about "cross-borrowing" a `Box<T>` into `&T`, while another is about a global trait used "across" files. Some of these terminology are really outdated and does not match the current terminology. Additionally, "cross" is also way too generic, it's easy to confuse with cross-compile.
+
+## `tests/ui/cross-crate/`: Cross-Crate Interaction
+
+Tests for behavior spanning multiple crates, including visibility rules, trait implementations, and type resolution across crate boundaries.
+
+## `tests/ui/custom_test_frameworks/`
+
+Tests for `#[bench]`, `#[test_case]` attributes and the `custom_test_frameworks` lang item.
+
+See [Tracking issue for eRFC 2318, Custom test frameworks #50297](https://github.com/rust-lang/rust/issues/50297).
+
+## `tests/ui/c-variadic/`: C Variadic Function
+
+Tests for FFI with C varargs (`va_list`).
+
+## `tests/ui/cycle-trait/`: Trait Cycle Detection
+
+Tests for detection and handling of cyclic trait dependencies.
+
+## `tests/ui/dataflow_const_prop/`
+
+Contains a single regression test for const prop in `SwitchInt` pass crashing when `ptr2int` transmute is involved.
+
+**FIXME**: A category with a single test. Maybe it would fit inside the category `const-prop` or some kind of `mir-opt` directory.
+
+## `tests/ui/debuginfo/`
+
+Tests for generation of debug information (DWARF, etc.) including variable locations, type information, and source line mapping. Also exercises `-C split-debuginfo` and `-C debuginfo`.
+
+## `tests/ui/definition-reachable/`: Definition Reachability
+
+Tests to check whether definitions are reachable.
+
+## `tests/ui/delegation/`: `#![feature(fn_delegation)]`
+
+Tests for `#![feature(fn_delegation)]`. See [Implement function delegation in rustc #3530](https://github.com/rust-lang/rfcs/pull/3530) for the proposed prototype experimentation.
+
+## `tests/ui/dep-graph/`: `-Z query-dep-graph`
+
+These tests use the unstable command line option `query-dep-graph` to examine the dependency graph of a Rust program, which is useful for debugging.
+
+## `tests/ui/deprecation/`: Deprecation Attribute
+
+Tests for `#[deprecated]` attribute and `deprecated_in_future` internal lint.
+
+## `tests/ui/deref-patterns/`: `#![feature(deref_patterns)]` and `#![feature(string_deref_patterns)]`
+
+Tests for `#![feature(deref_patterns)]` and `#![feature(string_deref_patterns)]`. See [Deref patterns | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/language-features/deref-patterns.html).
+
+**FIXME**: May have some overlap with `tests/ui/pattern/deref-patterns`.
+
+## `tests/ui/derived-errors/`: Derived Error Messages
+
+Tests for quality of diagnostics involving suppression of cascading errors in some cases to avoid overwhelming the user.
+
+## `tests/ui/derives/`: Derive Macro
+
+Tests for built-in derive macros (`Debug`, `Clone`, etc.) when used in conjunction with built-in `#[derive(..)]` attributes.
+
+## `tests/ui/deriving/`: Derive Macro
+
+**FIXME**: Coalesce with `tests/ui/derives`.
+
+## `tests/ui/dest-prop/` Destination Propagation
+
+**FIXME**: Contains a single test for the `DestProp` mir-opt, should probably be rehomed.
+
+## `tests/ui/destructuring-assignment/`
+
+Exercises destructuring assignments. See [RFC 2909 Destructuring assignment](https://github.com/rust-lang/rfcs/blob/master/text/2909-destructuring-assignment.md).
+
+## `tests/ui/diagnostic-flags/`
+
+These tests revolve around command-line flags which change the way error/warning diagnostics are emitted. For example, `--error-format=human --color=always`.
+
+**FIXME**: Check redundancy with `annotate-snippet`, which is another emitter.
+
+## `tests/ui/diagnostic_namespace/`
+
+Exercises `#[diagnostic::*]` namespaced attributes. See [RFC 3368 Diagnostic attribute namepsace](https://github.com/rust-lang/rfcs/blob/master/text/3368-diagnostic-attribute-namespace.md).
+
+## `tests/ui/diagnostic-width/`: `--diagnostic-width`
+
+Everything to do with `--diagnostic-width`.
+
+## `tests/ui/did_you_mean/`
+
+Tests for miscellaneous suggestions.
+
+## `tests/ui/directory_ownership/`: Declaring `mod` inside a block
+
+Exercises diagnostics for when a code block attempts to gain ownership of a non-inline module with a `mod` keyword placed inside of it.
+
+## `tests/ui/disallowed-deconstructing/`: Incorrect struct deconstruction
+
+Exercises diagnostics for disallowed struct destructuring.
+
+## `tests/ui/dollar-crate/`: `$crate` used with the `use` keyword
+
+There are a few rules - which are checked in this directory - to follow when using `$crate` - it must be used in the start of a `use` line and is a reserved identifier.
+
+**FIXME**: There are a few other tests in other directories with a filename starting with `dollar-crate`. They should perhaps be redirected here.
+
+## `tests/ui/drop/`: `Drop` and drop order
+
+Not necessarily about `Drop` and its implementation, but also about the drop order of fields inside a struct.
+
+## `tests/ui/drop-bounds/`
+
+Tests for linting on bounding a generic type on `Drop`.
+
+## `tests/ui/dropck/`: Drop Checking
+
+Mostly about checking the validity of `Drop` implementations.
+
+See:
+
+- [Dropck | The Nomicon](https://doc.rust-lang.org/nomicon/dropck.html)
+- [Drop check | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/borrow_check/drop_check.html)
+
+## `tests/ui/dst/`: Dynamically Sized Types
+
+Tests for dynamically-sized types (DSTs). See [Dynamically Sized Types | Reference](https://doc.rust-lang.org/reference/dynamically-sized-types.html).
+
+## `tests/ui/duplicate/`: Duplicate Symbols
+
+Tests about duplicated symbol names and associated errors, such as using the `#[export_name]` attribute to rename a function with the same name as another function.
+
+## `tests/ui/dynamically-sized-types/`: Dynamically Sized Types
+
+**FIXME**: should be coalesced with `tests/ui/dst`.
+
+## `tests/ui/dyn-compatibility/`: Dyn-compatibility
+
+Tests for dyn-compatibility of traits.
+
+See:
+
+- [Trait object | Reference](https://doc.rust-lang.org/reference/types/trait-object.html)
+- [Dyn compatibility | Reference](https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility)
+
+Previously known as "object safety".
+
+## `tests/ui/dyn-drop/`: `dyn Drop`
+
+**FIXME**: Contains a single test, used only to check the `dyn_drop` lint (which is normally `warn` level).
+
+## `tests/ui/dyn-keyword/`: `dyn` and Dynamic Dispatch
+
+The `dyn` keyword is used to highlight that calls to methods on the associated Trait are dynamically dispatched. To use the trait this way, it must be dyn-compatible - tests about dyn-compatibility belong in `tests/ui/dyn-compatibility/`, while more general tests on dynamic dispatch belong here.
+
+See [`dyn` keyword](https://doc.rust-lang.org/std/keyword.dyn.html).
+
+## `tests/ui/dyn-star/`: `dyn*`, Sized `dyn`, `#![feature(dyn_star)]`
+
+See [Tracking issue for dyn-star #102425](https://github.com/rust-lang/rust/issues/102425).
+
+## `tests/ui/editions/`: Rust edition-specific peculiarities
+
+These tests run in specific Rust editions, such as Rust 2015 or Rust 2018, and check errors and functionality related to specific now-deprecated idioms and features.
+
+**FIXME**: Maybe regroup `rust-2018`, `rust-2021` and `rust-2024` under this umbrella?
+
+## `tests/ui/empty/`: Various tests related to the concept of "empty"
+
+**FIXME**: These tests need better homes, this is not very informative.
+
+## `tests/ui/entry-point/`: `main` function
+
+Tests exercising the `main` entry-point.
+
+## `tests/ui/enum/`
+
+General-purpose tests on `enum`s. See [Enumerations | Reference](https://doc.rust-lang.org/reference/items/enumerations.html).
+
+## `tests/ui/enum-discriminant/`
+
+`enum` variants can be differentiated independently of their potential field contents with `discriminant`, which returns the type `Discriminant<T>`. See [`std::mem::discriminant`](https://doc.rust-lang.org/std/mem/fn.discriminant.html).
+
+## `tests/ui/env-macro/`: `env!`
+
+Exercises `env!` and `option_env!` macros.
+
+## `tests/ui/ergonomic-clones/`
+
+See [RFC 3680 Ergonomic clones](https://github.com/rust-lang/rfcs/pull/3680).
+
+## `tests/ui/error-codes/`: Error codes
+
+Tests for errors with dedicated error codes.
+
+## `tests/ui/error-emitter/`
+
+Quite similar to `ui/diagnostic-flags` in some of its tests, this category checks some behaviours of Rust's error emitter into the user's terminal window, such as truncating error in the case of an excessive amount of them.
+
+## `tests/ui/errors/`
+
+These tests are about very different topics, only unified by the fact that they result in errors.
+
+**FIXME**: "Errors" is way too generic, the tests probably need to be rehomed into more descriptive subdirectories.
+
+## `tests/ui/explain/`: `rustc --explain EXXXX`
+
+Accompanies `tests/ui/error-codes/`, exercises the `--explain` cli flag.
+
+## `tests/ui/explicit/`: Errors involving the concept of "explicit"
+
+This category contains three tests: two which are about the specific error `explicit use of destructor method`, and one which is about explicit annotation of lifetimes: https://doc.rust-lang.org/stable/rust-by-example/scope/lifetime/explicit.html.
+
+**FIXME**: Rehome the two tests about the destructor method with `drop`-related categories, and rehome the last test with a category related to lifetimes.
+
+## `tests/ui/explicit-tail-calls/`
+
+Exercises `#![feature(explicit_tail_calls)]` and the `become` keyword. See [Explicit Tail Calls #3407](https://github.com/rust-lang/rfcs/pull/3407).
+
+## `tests/ui/expr/`: Expressions
+
+A broad directory for tests on expressions.
+
+## `tests/ui/extern/`
+
+Tests on the `extern` keyword and `extern` blocks and functions.
+
+## `tests/ui/extern-flag/`: `--extern` command line flag
+
+Tests for the `--extern` CLI flag.
+
+## `tests/ui/feature-gates/`
+
+Tests on feature-gating, and the `#![feature(..)]` mechanism itself.
+
+## `tests/ui/ffi-attrs/`: `#![feature(ffi_const, ffi_pure)]`
+
+The `#[ffi_const]` and `#[ffi_pure]` attributes applies clang's `const` and `pure` attributes to foreign functions declarations, respectively. These attributes are the core element of the tests in this category.
+
+See:
+
+- [`ffi_const` | The Unstable book](https://doc.rust-lang.org/unstable-book/language-features/ffi-const.html)
+- [`ffi_pure` | The Unstable book](https://doc.rust-lang.org/beta/unstable-book/language-features/ffi-pure.html)
+
+## `tests/ui/fmt/`
+
+Exercises the `format!` macro.
+
+## `tests/ui/fn/`
+
+A broad category of tests on functions.
+
+## `tests/ui/fn-main/`
+
+**FIXME**: Serves a duplicate purpose with `ui/entry-point`, should be combined.
+
+## `tests/ui/for/`: `for` keyword
+
+Tests on the `for` keyword and some of its associated errors, such as attempting to write the faulty pattern `for _ in 0..1 {} else {}`.
+
+**FIXME**: Should be merged with `ui/for-loop-while`.
+
+## `tests/ui/force-inlining/`: `#[rustc_force_inline]`
+
+Tests for `#[rustc_force_inline]`, which will force a function to always be labelled as inline by the compiler (it will be inserted at the point of its call instead of being used as a normal function call.) If the compiler is unable to inline the function, an error will be reported. See <https://github.com/rust-lang/rust/pull/134082>.
+
+## `tests/ui/foreign/`: Foreign Function Interface (FFI)
+
+Tests for `extern "C"` and `extern "Rust`.
+
+**FIXME**: Check for potential overlap/merge with `ui/c-variadic` and/or `ui/extern`.
+
+## `tests/ui/for-loop-while/`
+
+Anything to do with loops and `for`, `loop` and `while` keywords to express them.
+
+**FIXME**: After `ui/for` is merged into this, also carry over its SUMMARY text.
+
+## `tests/ui/frontmatter/`
+
+Tests for `#![feature(frontmatter)]`. See [Tracking Issue for `frontmatter` #136889](https://github.com/rust-lang/rust/issues/136889).
+
+## `tests/ui/fully-qualified-type/`
+
+Tests for diagnostics when there may be identically named types that need further qualifications to disambiguate.
+
+## `tests/ui/functional-struct-update/`
+
+Functional Struct Update is the name for the idiom by which one can write `..<expr>` at the end of a struct literal expression to fill in all remaining fields of the struct literal by using `<expr>` as the source for them.
+
+See [RFC 0736 Privacy-respecting Functional Struct Update](https://github.com/rust-lang/rfcs/blob/master/text/0736-privacy-respecting-fru.md).
+
+## `tests/ui/function-pointer/`
+
+Tests on function pointers, such as testing their compatibility with higher-ranked trait bounds.
+
+See:
+
+- [Function pointer types | Reference](https://doc.rust-lang.org/reference/types/function-pointer.html)
+- [Higher-ranked trait bounds | Nomicon](https://doc.rust-lang.org/nomicon/hrtb.html)
+
+## `tests/ui/functions-closures/`
+
+Tests on closures. See [Closure expressions | Reference](https://doc.rust-lang.org/reference/expressions/closure-expr.html).
+
+## `tests/ui/generic-associated-types/`
+
+Tests on Generic Associated Types (GATs).
+
+## `tests/ui/generic-const-items/`
+
+Tests for `#![feature(generic_const_items)]`. See [Tracking issue for generic const items #113521](https://github.com/rust-lang/rust/issues/113521).
+
+## `tests/ui/generics/`
+
+A broad category of tests on generics, usually used when no more specific subdirectories are fitting.
+
+## `tests/ui/half-open-range-patterns/`: `x..` or `..x` range patterns
+
+Tests on range patterns where one of the bounds is not a direct value.
+
+**FIXME**: Overlaps with `ui/range`. `impossible_range.rs` is particularly suspected to be a duplicate test.
+
+## `tests/ui/hashmap/`
+
+Tests for the standard library collection [`std::collections::HashMap`](https://doc.rust-lang.org/std/collections/struct.HashMap.html).
+
+## `tests/ui/hello_world/`
+
+Tests that the basic hello-world program is not somehow broken.
+
+## `tests/ui/higher-ranked/`
+
+Tests for higher-ranked trait bounds.
+
+See:
+
+- [Higher-ranked trait bounds | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/traits/hrtb.html)
+- [Higher-ranked trait bounds | Nomicon](https://doc.rust-lang.org/nomicon/hrtb.html)
+
+## `tests/ui/hygiene/`
+
+This seems to have been originally intended for "hygienic macros" - macros which work in all contexts, independent of what surrounds them. However, this category has grown into a mish-mash of many tests that may belong in the other directories.
+
+**FIXME**: Reorganize this directory properly.
+
+## `tests/ui/illegal-sized-bound/`
+
+This test category revolves around trait objects with `Sized` having illegal operations performed on them.
+
+**FIXME**: There seems to be unrelated testing in this directory, such as `tests/ui/illegal-sized-bound/mutability-mismatch-arg.rs`. Investigate.
+
+## `tests/ui/impl-header-lifetime-elision/`
+
+Tests on lifetime elision in impl function signatures. See [Lifetime elision | Nomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html).
+
+## `tests/ui/implied-bounds/`
+
+See [Implied bounds | Reference](https://doc.rust-lang.org/reference/trait-bounds.html#implied-bounds).
+
+## `tests/ui/impl-trait/`
+
+Tests for trait impls.
+
+## `tests/ui/imports/`
+
+Tests for module system and imports.
+
+## `tests/ui/include-macros/`
+
+Exercise `include!`, `include_str!`, and `include_bytes!` macros.
+
+## `tests/ui/incoherent-inherent-impls/`
+
+Exercise forbidding inherent impls on a type defined in a different crate.
+
+## `tests/ui/indexing/`
+
+Tests on collection types (arrays, slices, vectors) and various errors encountered when indexing their contents, such as accessing out-of-bounds values.
+
+**FIXME**: (low-priority) could maybe be a subdirectory of `ui/array-slice-vec`
+
+## `tests/ui/inference/`
+
+Tests on type inference.
+
+## `tests/ui/infinite/`
+
+Tests for diagnostics on infinitely recursive types without indirection.
+
+## `tests/ui/inherent-impls-overlap-check/`
+
+Checks that repeating the same function names across separate `impl` blocks triggers an informative error, but not if the `impl` are for different types, such as `Bar<u8>` and `Bar<u16>`.
+
+NOTE: This should maybe be a subdirectory within another related to duplicate definitions, such as `tests/ui/duplicate/`.
+
+## `tests/ui/inline-const/`
+
+These tests revolve around the inline `const` block that forces the compiler to const-eval its content.
+
+## `tests/ui/instrument-coverage/`: `-Cinstrument-coverage` command line flag
+
+See [Instrument coverage | The rustc book](https://doc.rust-lang.org/rustc/instrument-coverage.html).
+
+## `tests/ui/instrument-xray/`: `-Z instrument-xray`
+
+See [Tracking issue for `-Z instrument-xray` #102921](https://github.com/rust-lang/rust/issues/102921).
+
+## `tests/ui/interior-mutability/`
+
+**FIXME**: contains a single test, probably better rehomed.
+
+## `tests/ui/internal/`
+
+Tests for `internal_unstable` and the attribute header `#![feature(allow_internal_unstable)]`, which lets compiler developers mark features as internal to the compiler, and unstable for standard library use.
+
+## `tests/ui/internal-lints/`
+
+Tests for rustc-internal lints.
+
+## `tests/ui/intrinsics/`
+
+Tests for the `{std,core}::intrinsics`, internal implementation detail.
+
+## `tests/ui/invalid/`
+
+Various tests related to rejecting invalid inputs.
+
+**FIXME**: This is rather uninformative, possibly rehome into more meaningful directories.
+
+## `tests/ui/invalid-compile-flags/`
+
+Tests for checking that invalid usage of compiler flags are rejected.
+
+## `tests/ui/invalid-module-declaration/`
+
+**FIXME**: Consider merging into module/resolve directories.
+
+## `tests/ui/invalid-self-argument/`: `self` as a function argument incorrectly
+
+Tests with erroneous ways of using `self`, such as having it not be the first argument, or using it in a non-associated function (no `impl` or `trait`).
+
+**FIXME**: Maybe merge with `ui/self`.
+
+## `tests/ui/io-checks/`
+
+Contains a single test. The test tries to output a file into an invalid directory with `-o`, then checks that the result is an error, not an internal compiler error.
+
+**FIXME**: Rehome to invalid compiler flags maybe.
+
+## `tests/ui/issues/`: Tests directly related to GitHub issues
+
+**FIXME (#133895)**: Random collection of regression tests and tests for issues, tests in this directory should be audited and rehomed.
+
+## `tests/ui/iterators/`
+
+These tests revolve around anything to do with iterators, e.g. mismatched types.
+
+**FIXME**: Check for potential overlap with `ui/for-loop-while`.
+
+## `tests/ui/json/`
+
+These tests revolve around the `--json` compiler flag. See [JSON Output](https://doc.rust-lang.org/rustc/json.html#json-output).
+
+## `tests/ui/keyword/`
+
+Tests exercising keywords, such as attempting to use them as identifiers when not contextual keywords.
+
+## `tests/ui/kindck/`
+
+**FIXME**: `kindck` is no longer a thing, these tests probably need to be audited and rehomed.
+
+## `tests/ui/label/`
+
+Exercises block and loop `'label`s.
+
+## `tests/ui/lang-items/`
+
+See [Lang items | The Unstable book](https://doc.rust-lang.org/unstable-book/language-features/lang-items.html).
+
+## `tests/ui/late-bound-lifetimes/`
+
+See [Early vs Late bound parameters | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/early_late_parameters.html#early-vs-late-bound-parameters).
+
+## `tests/ui/layout/`
+
+See [Type Layout | Reference](https://doc.rust-lang.org/reference/type-layout.html).
+
+## `tests/ui/lazy-type-alias/`
+
+Tests for `#![feature(lazy_type_alias)]`. See [Tracking issue for lazy type aliases #112792
+](https://github.com/rust-lang/rust/issues/112792).
+
+## `tests/ui/lazy-type-alias-impl-trait/`
+
+This feature allows use of an `impl Trait` in multiple locations while actually using the same concrete type (`type Alias = impl Trait;`) everywhere, keeping the original `impl Trait` hidden.
+
+**FIXME**: merge this with `tests/ui/type-alias-impl-trait/`?
+
+## `tests/ui/let-else/`
+
+Exercises let-else constructs.
+
+## `tests/ui/lexer/`
+
+Exercises of the lexer.
+
+## `tests/ui/lifetimes/`
+
+Broad directory on lifetimes, including proper specifiers, lifetimes not living long enough, or undeclared lifetime names.
+
+## `tests/ui/limits/`
+
+These tests exercises numerical limits, such as `[[u8; 1518599999]; 1518600000]`.
+
+## `tests/ui/linkage-attr/`
+
+Tests for the linkage attribute `#[linkage]` of `#![feature(linkage)]`.
+
+**FIXME**: Some of these tests do not use the feature at all, which should be moved under `ui/linking` instead.
+
+## `tests/ui/linking/`
+
+Tests on code which fails during the linking stage, or which contain arguments and lines that have been known to cause unjustified errors in the past, such as specifying an unusual `#[export_name]`.
+
+See [Linkage | Reference](https://doc.rust-lang.org/reference/linkage.html).
+
+## `tests/ui/link-native-libs/`
+
+Tests for `#[link(name = "", kind = "")]` and `-l` command line flag.
+
+See [Tracking Issue for linking modifiers for native libraries #81490](https://github.com/rust-lang/rust/issues/81490).
+
+## `tests/ui/lint/`
+
+Tests for the lint infrastructure, lint levels, lint reasons, etc.
+
+See:
+
+- [Lints | The rustc book](https://doc.rust-lang.org/rustc/lints/index.html)
+- [Lint reasons | Reference](https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-reasons)
+
+## `tests/ui/liveness/`
+
+Tests exercising analysis for unused variables, unreachable statements, functions which are supposed to return a value but do not, as well as values moved elsewhere before they could be used by a function.
+
+**FIXME**: This seems unrelated to "liveness" as defined in the rustc compiler guide. Is this misleadingly named? https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/lifetime_parameters.html#liveness-and-universal-regions
+
+## `tests/ui/loops/`
+
+Tests on the `loop` construct.
+
+**FIXME**: Consider merging with `ui/for-loop-while`.
+
+## `tests/ui/lowering/`
+
+Tests on [AST lowering](https://rustc-dev-guide.rust-lang.org/ast-lowering.html).
+
+## `tests/ui/lto/`
+
+Exercise *Link-Time Optimization* (LTO), involving the flags `-C lto` or `-Z thinlto`.
+
+## `tests/ui/lub-glb/`: LUB/GLB algorithm update
+
+Tests on changes to inference variable lattice LUB/GLB, see <https://github.com/rust-lang/rust/pull/45853>.
+
+## `tests/ui/macro_backtrace/`: `-Zmacro-backtrace`
+
+Contains a single test, checking the unstable command-line flag to enable detailed macro backtraces.
+
+**FIXME**: This could be merged with `ui/macros`, which already contains other macro backtrace tests.
+
+## `tests/ui/macros/`
+
+Broad category of tests on macros.
+
+## `tests/ui/malformed/`
+
+Broad category of tests on malformed inputs.
+
+**FIXME**: this is kinda vague, probably best to audit and rehome tests.
+
+## `tests/ui/marker_trait_attr/`
+
+See [Tracking issue for allowing overlapping implementations for marker trait #29864](https://github.com/rust-lang/rust/issues/29864).
+
+## `tests/ui/match/`
+
+Broad category of tests on `match` constructs.
+
+## `tests/ui/meta/`: Tests for compiletest itself
+
+These tests check the function of the UI test suite at large and Compiletest in itself.
+
+**FIXME**: This should absolutely be merged with `tests/ui/compiletest-self-test/`.
+
+## `tests/ui/methods/`
+
+A broad category for anything related to methods and method resolution.
+
+## `tests/ui/mir/`
+
+Certain mir-opt regression tests.
+
+## `tests/ui/mir-dataflow`
+
+Tests for MIR dataflow analysis.
+
+See [MIR Dataflow | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/mir/dataflow.html).
+
+## `tests/ui/mismatched_types/`
+
+Exercises on mismatched type diagnostics.
+
+## `tests/ui/missing/`
+
+Something is missing which could be added to fix (e.g. suggestions).
+
+**FIXME**: this is way too vague, tests should be rehomed.
+
+## `tests/ui/missing_non_modrs_mod/`
+
+This directory is a small tree of `mod` dependencies, but the root, `foo.rs`, is looking for a file which does not exist. The test checks that the error is reported at the top-level module.
+
+**FIXME**: Merge with `tests/ui/modules/`.
+
+## `tests/ui/missing-trait-bounds/`
+
+Tests for checking missing trait bounds, and their diagnostics.
+
+**FIMXE**: Maybe a subdirectory of `ui/trait-bounds` would be more appropriate.
+
+## `tests/ui/modules/`
+
+Tests on the module system.
+
+**FIXME**: `tests/ui/imports/` should probably be merged with this.
+
+## `tests/ui/modules_and_files_visibility/`
+
+**FIXME**: Merge with `tests/ui/modules/`.
+
+## `tests/ui/moves`
+
+Tests on moves (destructive moves).
+
+## `tests/ui/mut/`
+
+Broad category of tests on mutability, such as the `mut` keyword, borrowing a value as both immutable and mutable (and the associated error), or adding mutable references to `const` declarations.
+
+## `tests/ui/namespace/`
+
+Contains a single test. It imports a massive amount of very similar types from a crate, then attempts various permutations of their namespace paths, checking for errors or the lackthereof.
+
+**FIXME**: Move under either `tests/ui/modules/` or `tests/ui/resolve/`.
+
+## `tests/ui/never_type/`
+
+See [Tracking issue for promoting `!` to a type (RFC 1216) #35121](https://github.com/rust-lang/rust/issues/35121).
+
+## `tests/ui/new-range/`
+
+See [RFC 3550 New Range](https://github.com/rust-lang/rfcs/blob/master/text/3550-new-range.md).
+
+## `tests/ui/nll/`: Non-lexical lifetimes
+
+Tests for Non-lexical lifetimes. See [RFC 2094 NLL](https://rust-lang.github.io/rfcs/2094-nll.html).
+
+## `tests/ui/non_modrs_mods/`
+
+Despite the size of the directory, this is a single test, spawning a sprawling `mod` dependency tree and checking its successful build.
+
+**FIXME**: Consider merge with `tests/ui/modules/`, keeping the directory structure.
+
+## `tests/ui/non_modrs_mods_and_inline_mods/`
+
+A very similar principle as `non_modrs_mods`, but with an added inline `mod` statement inside another `mod`'s code block.
+
+**FXIME**: Consider merge with `tests/ui/modules/`, keeping the directory structure.
+
+## `tests/ui/no_std/`
+
+Tests for where the standard library is disabled through `#![no_std]`.
+
+## `tests/ui/not-panic/`
+
+Tests checking various types, such as `&RefCell<i32>`, and whether they are not `UnwindSafe` as expected.
+
+## `tests/ui/numbers-arithmetic/`
+
+Tests that exercises edge cases, such as specific floats, large or very small numbers, or bit conversion, and check that the arithmetic results are as expected.
+
+## `tests/ui/numeric/`
+
+Tests that checks numeric types and their interactions, such as casting among them with `as` or providing the wrong numeric suffix.
+
+## `tests/ui/object-lifetime/`
+
+Tests on lifetimes on objects, such as a lifetime bound not being able to be deduced from context, or checking that lifetimes are inherited properly.
+
+**FIXME**: Just a more specific subset of `ui/lifetimes`.
+
+## `tests/ui/obsolete-in-place/`
+
+Contains a single test. Check that we reject the ancient Rust syntax `x <- y` and `in(BINDING) {}` construct.
+
+**FIXME**: Definitely should be rehomed, maybe to `tests/ui/deprecation/`.
+
+## `tests/ui/offset-of/`
+
+Exercises the [`std::mem::offset_of` macro](https://doc.rust-lang.org/beta/std/mem/macro.offset_of.html).
+
+## `tests/ui/on-unimplemented/`
+
+Exercises the `#[rustc_on_unimplemented]`.
+
+## `tests/ui/operator-recovery/`
+
+**FIXME**: Probably move under `tests/ui/binop/` or `tests/ui/parser/`.
+
+## `tests/ui/or-patterns/`
+
+Exercises `||` and `|` in patterns.
+
+## `tests/ui/overloaded/`
+
+Exercises operator overloading via [`std::ops`](https://doc.rust-lang.org/std/ops/index.html).
+
+## `tests/ui/packed/`
+
+See [`repr(packed)` | Nomicon](https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked-reprpackedn).
+
+## `tests/ui/panic-handler/`
+
+See [panic handler | Nomicon](https://doc.rust-lang.org/nomicon/panic-handler.html).
+
+## `tests/ui/panic-runtime/`
+
+Exercises `#![panic_runtime]`, `-C panic`, panic runtimes and panic unwind strategy.
+
+See [RFC 1513 Less unwinding](https://github.com/rust-lang/rfcs/blob/master/text/1513-less-unwinding.md).
+
+## `tests/ui/panics/`
+
+Broad category of tests about panics in general, often but not necessarily using the `panic!` macro.
+
+## `tests/ui/parallel-rustc/`
+
+Efforts towards a [Parallel Rustc Front-end](https://github.com/rust-lang/rust/issues/113349). Includes `-Zthreads=`.
+
+## `tests/ui/parser/`
+
+Various parser tests
+
+**FIXME**: Maybe move `tests/ui/keywords/` under this?
+
+## `tests/ui/patchable-function-entry/`
+
+See [Patchable function entry | The Unstable book](https://doc.rust-lang.org/unstable-book/compiler-flags/patchable-function-entry.html).
+
+## `tests/ui/pattern/`
+
+Broad category of tests surrounding patterns. See [Patterns | Reference](https://doc.rust-lang.org/reference/patterns.html).
+
+**FIXME**: Some overlap with `tests/ui/match/`.
+
+## `tests/ui/pin-macro/`
+
+See [`std::pin`](https://doc.rust-lang.org/std/pin/).
+
+## `tests/ui/precondition-checks/`
+
+Exercises on some unsafe precondition checks.
+
+## `tests/ui/print-request/`
+
+Tests on `--print` compiler flag. See [print options | The rustc book](https://doc.rust-lang.org/rustc/command-line-arguments/print-options.html).
+
+## `tests/ui/print_type_sizes/`
+
+Exercises the `-Z print-type-sizes` flag.
+
+## `tests/ui/privacy/`
+
+Exercises on name privacy. E.g. the meaning of `pub`, `pub(crate)`, etc.
+
+## `tests/ui/process/`
+
+Some standard library process tests which are hard to write within standard library crate tests.
+
+## `tests/ui/process-termination/`
+
+Some standard library process termination tests which are hard to write within standard library crate tests.
+
+## `tests/ui/proc-macro/`
+
+Broad category of tests on proc-macros. See [Procedural Macros | Reference](https://doc.rust-lang.org/reference/procedural-macros.html).
+
+## `tests/ui/ptr_ops/`: Using operations on a pointer
+
+Contains only 2 tests, related to a single issue, which was about an error caused by using addition on a pointer to `i8`.
+
+**FIXME**: Probably rehome under some typecheck / binop directory.
+
+## `tests/ui/pub/`: `pub` keyword
+
+A large category about function and type public/private visibility, and its impact when using features across crates. Checks both visibility-related error messages and previously buggy cases.
+
+**FIXME**: merge with `tests/ui/privacy/`.
+
+## `tests/ui/qualified/`
+
+Contains few tests on qualified paths where a type parameter is provided at the end: `type A = <S as Tr>::A::f<u8>;`. The tests check if this fails during type checking, not parsing.
+
+**FIXME**: Should be rehomed to `ui/typeck`.
+
+## `tests/ui/query-system/`
+
+Tests on Rust methods and functions which use the query system, such as `std::mem::size_of`. These compute information about the current runtime and return it. See [Query system | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/query.html).
+
+## `tests/ui/range/`
+
+Broad category of tests ranges, both in their `..` or `..=` form, as well as the standard library `Range`, `RangeTo`, `RangeFrom` or `RangeBounds` types.
+
+**FIXME**: May have some duplicate tests with `ui/new-range`.
+
+## `tests/ui/raw-ref-op/`: Using operators on `&raw` values
+
+Exercises `&raw mut <place>` and `&raw const <place>`. See [RFC 2582 Raw reference MIR operator](https://github.com/rust-lang/rfcs/blob/master/text/2582-raw-reference-mir-operator.md).
+
+## `tests/ui/reachable`
+
+Reachability tests, primarily unreachable code and coercions into the never type `!` from diverging expressions.
+
+**FIXME**: Check for overlap with `ui/liveness`.
+
+## `tests/ui/recursion/`
+
+Broad category of tests exercising recursions (compile test and run time), in functions, macros, `type` definitions, and more.
+
+Also exercises the `#![recursion_limit = ""]` attribute.
+
+## `tests/ui/recursion_limit/`: `#![recursion_limit = ""]`
+
+Sets a recursion limit on recursive code.
+
+**FIXME**: Should be merged with `tests/ui/recursion/`.
+
+## `tests/ui/regions/`
+
+**FIXME**: Maybe merge with `ui/lifetimes`.
+
+## `tests/ui/repeat-expr/`
+
+Exercises `[Type; n]` syntax for creating arrays with repeated types across a set size.
+
+**FIXME**: Maybe make this a subdirectory of `ui/array-slice-vec`.
+
+## `tests/ui/repr/`: `#[repr(_)]`
+
+Tests on the `#[repr(..)]` attribute. See [Representations | Reference](https://doc.rust-lang.org/reference/type-layout.html#representations).
+
+## `tests/ui/reserved/`
+
+Reserved keywords and attribute names.
+
+See e.g. [Reserved keywords | Reference](https://doc.rust-lang.org/reference/keywords.html).
+
+**FIXME**: maybe merge under `tests/ui/keyword/`.
+
+## `tests/ui/resolve/`: Name resolution
+
+See [Name resolution | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/name-resolution.html).
+
+## `tests/ui/return/`
+
+Exercises the `return` keyword, return expressions and statements.
+
+## `tests/ui/rfcs/`
+
+Tests that accompanies an implementation for an RFC.
+
+## `tests/ui/rmeta/`
+
+Exercises `.rmeta` crate metadata and the `--emit=metadata` cli flag.
+
+## `tests/ui/runtime/`
+
+Tests for runtime environment on which Rust programs are executed. E.g. Unix `SIGPIPE`.
+
+## `tests/ui/rust-{2018,2021,2024}/`
+
+Tests that exercise behaviors and features that are specific to editions.
+
+## `tests/ui/rustc-env`
+
+Tests on environmental variables that affect `rustc`.
+
+## `tests/ui/rustdoc`
+
+Hybrid tests that exercises `rustdoc`, and also some joint `rustdoc`/`rustc` interactions.
+
+## `tests/ui/sanitizer/`
+
+Exercises sanitizer support. See [Sanitizer | The rustc book](https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html).
+
+## `tests/ui/self/`: `self` keyword
+
+Tests with erroneous ways of using `self`, such as using `this.x` syntax as seen in other languages, having it not be the first argument, or using it in a non-associated function (no `impl` or `trait`). It also contains correct uses of `self` which have previously been observed to cause ICEs.
+
+## `tests/ui/sepcomp/`: Separate Compilation
+
+In this directory, multiple crates are compiled, but some of them have `inline` functions, meaning they must be inlined into a different crate despite having been compiled separately.
+
+**FIXME**: this directory might need some better docs, also this directory might want a better name.
+
+## `tests/ui/shadowed/`
+
+Tests on name shadowing.
+
+## `tests/ui/shell-argfiles/`: `-Z shell-argfiles` command line flag
+
+The `-Zshell-argfiles` compiler flag allows argfiles to be parsed using POSIX "shell-style" quoting. When enabled, the compiler will use shlex to parse the arguments from argfiles specified with `@shell:<path>`.
+
+Because this feature controls the parsing of input arguments, the `-Zshell-argfiles` flag must be present before the argument specifying the shell-style argument file.
+
+**FIXME**: maybe group this with `tests/ui/argfile/`
+
+## `tests/ui/simd/`
+
+Some tests exercising SIMD support.
+
+## `tests/ui/single-use-lifetime/`
+
+This is a test directory for the specific error case where a lifetime never gets used beyond a single annotation on, for example, a `struct`.
+
+## `tests/ui/sized/`: `Sized` trait, sized types
+
+While many tests here involve the `Sized` trait directly, some instead test, for example the slight variations between returning a zero-sized `Vec` and a `Vec` with one item, where one has no known type and the other does.
+
+## `tests/ui/span/`
+
+An assorted collection of tests that involves specific diagnostic spans.
+
+**FIXME**: This is a big directory with numerous only-tangentially related tests. Maybe some moving is in order.
+
+## `tests/ui/specialization`
+
+See [Tracking issue for specialization (RFC 1210) #31844](https://github.com/rust-lang/rust/issues/31844).
+
+## `tests/ui/stability-attribute/`
+
+Stability attributes used internally by the standard library: `#[stable()]` and `#[unstable()]`.
+
+## `tests/ui/stable-mir-print/`
+
+Some tests for pretty printing of StableMIR.
+
+## `tests/ui/stack-protector/`: `-Z stack-protector` command line flag
+
+See [Tracking Issue for stabilizing stack smashing protection (i.e., `-Z stack-protector`) #114903](https://github.com/rust-lang/rust/issues/114903).
+
+## `tests/ui/static/`
+
+Tests on static items.
+
+## `tests/ui/statics/`
+
+**FIXME**: should probably be merged with `tests/ui/static/`.
+
+## `tests/ui/stats/`
+
+Tests for compiler-internal stats; `-Z meta-stats` and `-Z input-stats` flags.
+
+## `tests/ui/std/`: Tests which use features from the standard library
+
+A catch-all category about anything that can come from `std`.
+
+**FIXME**: this directory is probably too vague, tests might need to be audited and rehomed.
+
+## `tests/ui/stdlib-unit-tests/`
+
+Some standard library tests which are too inconvenient or annoying to implement as std crate tests.
+
+## `tests/ui/str/`
+
+Exercise `str` keyword and string slices.
+
+## `tests/ui/structs/`
+
+Assorted tests surrounding the `struct` keyword, struct type definitions and usages.
+
+## `tests/ui/structs-enums/`
+
+Tests on both structs and enums.
+
+**FIXME**: maybe coalesce {`tests/ui/structs/`, `tests/ui/structs-enums/`, `tests/ui/enums/`} into one `tests/ui/adts` directory...
+
+## `tests/ui/suggestions/`
+
+Generic collection of tests for suggestions, when no more specific directories are applicable.
+
+**FIXME**: Some overlap with `tests/ui/did_you_mean/`, that directory should probably be moved under here.
+
+## `tests/ui/svh/`: Strict Version Hash
+
+Tests on the *Strict Version Hash* (SVH, also known as the "crate hash").
+
+See [Strict Version Hash](https://rustc-dev-guide.rust-lang.org/backend/libs-and-metadata.html#strict-version-hash).
+
+## `tests/ui/symbol-mangling-version/`: `-Csymbol-mangling-version` command line flag
+
+**FIXME**: Should be merged with `ui/symbol-names`.
+
+## `tests/ui/symbol-names/`: Symbol mangling and related attributes
+
+These tests revolve around `#[no_mangle]` attribute, as well as consistently mangled symbol names (checked with the `rustc_symbol_name` attribute), which is important to build reproducible binaries.
+
+## `tests/ui/sync/`: `Sync` trait
+
+Exercises `Sync` trait and auto-derive thereof.
+
+## `tests/ui/target-cpu/`: `-C target-cpu` command line flag
+
+This command line option instructs rustc to generate code specifically for a particular processor.
+
+**FIXME**: Contains a single test, maybe put it in a directory about misc codegen options?
+
+## `tests/ui/target-feature/`: `#[target_feature(enable = "relaxed-simd")]`
+
+Exercises the `#[target_feature(..)]` attribute. See [Target feature attribute | Reference](https://doc.rust-lang.org/reference/attributes/codegen.html#the-target_feature-attribute).
+
+## `tests/ui/target_modifiers/`
+
+Tests for [RFC 3716: Target Modifiers](https://github.com/rust-lang/rfcs/pull/3716).
+
+See [Tracking Issue for target modifiers #136966](https://github.com/rust-lang/rust/issues/136966).
+
+## `tests/ui/test-attrs/`
+
+Exercises the [`#[test]` attribute](https://doc.rust-lang.org/reference/attributes/testing.html#testing-attributes).
+
+## `tests/ui/thir-print/`
+
+Pretty print of THIR trees via `-Zunpretty=thir-tree`.
+
+## `tests/ui/thread-local/`
+
+Exercises thread local values and `#[thread_local]` attribute.
+
+See [Tracking issue for thread_local stabilization #29594](https://github.com/rust-lang/rust/issues/29594).
+
+## `tests/ui/threads-sendsync/`
+
+Broad category for parallelism and multi-threaded tests, including attempting to send types across threads which are not thread-safe.
+
+## `tests/ui/tool-attributes/`: External tool attributes
+
+Exercises [tool attributes](https://doc.rust-lang.org/reference/attributes.html#tool-attributes).
+
+## `tests/ui/track-diagnostics/`
+
+Exercises `#[track_caller]` and `-Z track-diagnostics`.
+
+## `tests/ui/trait-bounds/`
+
+Collection of tests for [trait bounds](https://doc.rust-lang.org/reference/trait-bounds.html).
+
+## `tests/ui/traits/`
+
+Broad collection of tests on traits in general.
+
+**FIXME**: This could be better organized in subdirectories containing tests such as `ui/traits/trait-bounds`.
+
+## `tests/ui/transmutability/`: `#![feature(transmutability)]`
+
+See [Tracking Issue for Transmutability Trait: `#[transmutability]` #99571](https://github.com/rust-lang/rust/issues/99571).
+
+See also [Project Safe Transmute](https://github.com/rust-lang/project-safe-transmute).
+
+## `tests/ui/transmute/`
+
+Tests surrounding [`std::mem::transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html).
+
+## `tests/ui/treat-err-as-bug/`
+
+Exercises compiler development support flag `-Z treat-err-as-bug`.
+
+## `tests/ui/trivial-bounds/`
+
+`#![feature(trivial_bounds)]`. See [RFC 2056 Allow trivial where clause constraints](https://github.com/rust-lang/rfcs/blob/master/text/2056-allow-trivial-where-clause-constraints.md).
+
+## `tests/ui/try-block/`
+
+`#![feature(try_blocks)]`. See [Tracking issue for `?` operator and `try` blocks (RFC 243, `question_mark` & `try_blocks` features)](https://github.com/rust-lang/rust/issues/31436).
+
+## `tests/ui/try-trait/`
+
+`#![feature(try_trait_v2)]`. See [RFC 3058 Try Trait v2](https://github.com/rust-lang/rfcs/blob/master/text/3058-try-trait-v2.md).
+
+## `tests/ui/tuple/`
+
+Tests surrounding the tuple type `()`.
+
+## `tests/ui/type/`
+
+Assorted collection of tests surrounding the concept of a "type".
+
+**FIXME**: There is very little consistency across tests of this category, and should probably be sent to other subdirectories.
+
+## `tests/ui/type-alias/`
+
+Exercises [type aliases](https://doc.rust-lang.org/reference/items/type-aliases.html).
+
+## `tests/ui/type-alias-enum-variants/`
+
+Tests for `type` aliases in the context of `enum` variants, such as that applied type arguments of enums are respected independently of being the original type or the `type` alias.
+
+## `tests/ui/type-alias-impl-trait/`
+
+`#![feature(type_alias_impl_trait)]`. See [Type Alias Impl Trait | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/language-features/type-alias-impl-trait.html).
+
+## `tests/ui/typeck/`
+
+General collection of type checking related tests.
+
+## `tests/ui/type-inference/`
+
+General collection of type inference related tests.
+
+## `tests/ui/typeof/`
+
+`typeof` keyword, reserved but unimplemented.
+
+## `tests/ui/ufcs/`
+
+See [RFC 0132 Unified Function Call Syntax](https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md).
+
+## `tests/ui/unboxed-closures/`
+
+`#![feature(unboxed_closures)]`, `Fn`, `FnMut` and `FnOnce` traits
+
+See [Tracking issue for Fn traits (`unboxed_closures` & `fn_traits` feature)](https://github.com/rust-lang/rust/issues/29625).
+
+## `tests/ui/underscore-imports/`
+
+See [Underscore imports | Reference](https://doc.rust-lang.org/reference/items/use-declarations.html#underscore-imports).
+
+**FIXME**: should become a subdirectory of `tests/ui/imports/`.
+
+## `tests/ui/underscore-lifetime/`: `'_` elided lifetime
+
+Exercises [anonymous elided lifetimes](https://doc.rust-lang.org/reference/lifetime-elision.html).
+
+## `tests/ui/uniform-paths/`
+
+In uniform paths, if a submodule and an external dependencies have the same name, to depend on the external dependency, one needs to disambiguate it from the submodule using `use ::foo`. Tests revolve around this, for example, check that `self::foo` and `::foo` are not considered ambiguously identical by the compiler.
+
+Remark: As they are an important Rust 2018 feature, they also get a big subdirectory in `ui/rust-2018/uniform-paths`
+
+## `tests/ui/uninhabited/`: Uninhabited types
+
+See [Uninhabited | Reference](https://doc.rust-lang.org/reference/glossary.html?highlight=Uninhabit#uninhabited).
+
+## `tests/ui/union/`
+
+See [Unions | Reference](https://doc.rust-lang.org/reference/items/unions.html).
+
+## `tests/ui/unknown-unstable-lints/`: Attempting to refer to an unstable lint which does not exist
+
+Tests for trying to use non-existent unstable lints.
+
+**FIXME**: move this under `tests/ui/lints/`.
+
+## `tests/ui/unop/`: Unary operators `-`, `*` and `!`
+
+Tests the three unary operators for negating, dereferencing and inverting, across different contexts.
+
+## `tests/ui/unpretty/`: `-Z unpretty` command line flag
+
+The `-Z unpretty` flag outputs various representations of a program's tree in a certain way.
+
+## `tests/ui/unresolved/`
+
+Exercises various unresolved errors, ranging from earlier name resolution failures to later method resolution failures.
+
+## `tests/ui/unsafe/`
+
+A broad category of tests about unsafe Rust code.
+
+## `tests/ui/unsafe-binders/`: `#![feature(unsafe_binders)]`
+
+See [Tracking issue for unsafe binder types #130516](https://github.com/rust-lang/rust/issues/130516).
+
+## `tests/ui/unsafe-fields/`: `struct`s and `enum`s with an `unsafe` field
+
+See [Tracking issue for RFC 3458: Unsafe fields #132922](https://github.com/rust-lang/rust/issues/132922).
+
+## `tests/ui/unsized/`: Zero-sized types, `Sized` trait, object has no known size at compile time
+
+**FIXME**: between `tests/ui/zero-sized/`, `tests/ui/sized/` and this directory, might need to reorganize them a bit.
+
+## `tests/ui/unsized-locals/`: `#![feature(unsized_locals, unsized_fn_params)]`
+
+See:
+
+- [RFC 1909 Unsized rvalues](https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md)
+- [de-RFC 3829: Remove unsized_locals](https://github.com/rust-lang/rfcs/pull/3829)
+- [Tracking issue for RFC #1909: Unsized Rvalues (`unsized_locals`, `unsized_fn_params`)](https://github.com/rust-lang/rust/issues/48055)
+
+**FIXME**: Seems to also contain more generic tests that fit in `tests/ui/unsized/`.
+
+## `tests/ui/unused-crate-deps/`
+
+Exercises the `unused_crate_dependencies` lint.
+
+## `tests/ui/unwind-abis/`
+
+**FIXME**: Contains a single test, should likely be rehomed to `tests/ui/abi/`.
+
+## `tests/ui/use/`
+
+**FIXME**: merge with `ui/imports`.
+
+## `tests/ui/variance/`: Covariants, invariants and contravariants
+
+See [Variance | Reference](https://doc.rust-lang.org/reference/subtyping.html#variance).
+
+## `tests/ui/variants/`: `enum` variants
+
+Tests on `enum` variants.
+
+**FIXME**: Should be rehomed with `tests/ui/enum/`.
+
+## `tests/ui/version/`
+
+**FIXME**: Contains a single test described as "Check that rustc accepts various version info flags.", should be rehomed.
+
+## `tests/ui/warnings/`
+
+**FIXME**: Contains a single test on non-explicit paths (`::one()`). Should be rehomed probably to `tests/ui/resolve/`.
+
+## `tests/ui/wasm/`
+
+These tests target the `wasm32` architecture specifically. They are usually regression tests for WASM-specific bugs which were observed in the past.
+
+## `tests/ui/wf/`: Well-formedness checking
+
+Tests on various well-formedness checks, e.g. [Type-checking normal functions](https://rustc-dev-guide.rust-lang.org/traits/lowering-to-logic.html).
+
+## `tests/ui/where-clauses/`
+
+Tests on `where` clauses. See [Where clauses | Reference](https://doc.rust-lang.org/reference/items/generics.html#where-clauses).
+
+## `tests/ui/while/`
+
+Tests on the `while` keyword and the `while` construct.
+
+**FIXME**: merge with `ui/for-loop-while`.
+
+## `tests/ui/windows-subsystem/`: `#![windows_subsystem = ""]`
+
+See [the `windows_subsystem` attribute](https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute).
+
+## `tests/ui/zero-sized/`: Zero-sized types
+
+See [Zero-Sized Types | Reference](https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts).
diff --git a/tests/ui/abi/bad-custom.rs b/tests/ui/abi/bad-custom.rs
index e792f0955b9..7c881134ccb 100644
--- a/tests/ui/abi/bad-custom.rs
+++ b/tests/ui/abi/bad-custom.rs
@@ -5,7 +5,7 @@
 
 #[unsafe(naked)]
 extern "custom" fn must_be_unsafe(a: i64) -> i64 {
-    //~^ ERROR functions with the `"custom"` ABI must be unsafe
+    //~^ ERROR functions with the "custom" ABI must be unsafe
     //~| ERROR invalid signature for `extern "custom"` function
     std::arch::naked_asm!("")
 }
@@ -23,7 +23,7 @@ unsafe extern "custom" fn no_return_type() -> i64 {
 }
 
 unsafe extern "custom" fn double(a: i64) -> i64 {
-    //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
     //~| ERROR invalid signature for `extern "custom"` function
     unimplemented!()
 }
@@ -32,7 +32,7 @@ struct Thing(i64);
 
 impl Thing {
     unsafe extern "custom" fn is_even(self) -> bool {
-        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
         //~| ERROR invalid signature for `extern "custom"` function
         unimplemented!()
     }
@@ -40,7 +40,7 @@ impl Thing {
 
 trait BitwiseNot {
     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
-        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
         //~| ERROR invalid signature for `extern "custom"` function
         unimplemented!()
     }
@@ -50,14 +50,14 @@ impl BitwiseNot for Thing {}
 
 trait Negate {
     extern "custom" fn negate(a: i64) -> i64;
-    //~^ ERROR functions with the `"custom"` ABI must be unsafe
+    //~^ ERROR functions with the "custom" ABI must be unsafe
     //~| ERROR invalid signature for `extern "custom"` function
 }
 
 impl Negate for Thing {
     extern "custom" fn negate(a: i64) -> i64 {
-        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
-        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
+        //~| ERROR functions with the "custom" ABI must be unsafe
         //~| ERROR invalid signature for `extern "custom"` function
         -a
     }
@@ -68,24 +68,24 @@ unsafe extern "custom" {
     //~^ ERROR invalid signature for `extern "custom"` function
 
     safe fn extern_cannot_be_safe();
-    //~^ ERROR foreign functions with the `"custom"` ABI cannot be safe
+    //~^ ERROR foreign functions with the "custom" ABI cannot be safe
 }
 
 fn caller(f: unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
     unsafe { f(x) }
-    //~^ ERROR functions with the `"custom"` ABI cannot be called
+    //~^ ERROR functions with the "custom" ABI cannot be called
 }
 
 fn caller_by_ref(f: &unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
     unsafe { f(x) }
-    //~^ ERROR functions with the `"custom"` ABI cannot be called
+    //~^ ERROR functions with the "custom" ABI cannot be called
 }
 
 type Custom = unsafe extern "custom" fn(i64) -> i64;
 
 fn caller_alias(f: Custom, mut x: i64) -> i64 {
     unsafe { f(x) }
-    //~^ ERROR functions with the `"custom"` ABI cannot be called
+    //~^ ERROR functions with the "custom" ABI cannot be called
 }
 
 #[unsafe(naked)]
@@ -95,8 +95,8 @@ const unsafe extern "custom" fn no_const_fn() {
 }
 
 async unsafe extern "custom" fn no_async_fn() {
-    //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
-    //~| ERROR functions with the `"custom"` ABI cannot be `async`
+    //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
+    //~| ERROR functions with the "custom" ABI cannot be `async`
 }
 
 fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn()  {
@@ -107,15 +107,15 @@ fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn()  {
 pub fn main() {
     unsafe {
         assert_eq!(double(21), 42);
-        //~^ ERROR functions with the `"custom"` ABI cannot be called
+        //~^ ERROR functions with the "custom" ABI cannot be called
 
         assert_eq!(unsafe { increment(41) }, 42);
-        //~^ ERROR functions with the `"custom"` ABI cannot be called
+        //~^ ERROR functions with the "custom" ABI cannot be called
 
         assert!(Thing(41).is_even());
-        //~^ ERROR functions with the `"custom"` ABI cannot be called
+        //~^ ERROR functions with the "custom" ABI cannot be called
 
         assert_eq!(Thing::bitwise_not(42), !42);
-        //~^ ERROR functions with the `"custom"` ABI cannot be called
+        //~^ ERROR functions with the "custom" ABI cannot be called
     }
 }
diff --git a/tests/ui/abi/bad-custom.stderr b/tests/ui/abi/bad-custom.stderr
index ec0f11af898..893382875a2 100644
--- a/tests/ui/abi/bad-custom.stderr
+++ b/tests/ui/abi/bad-custom.stderr
@@ -1,4 +1,4 @@
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/bad-custom.rs:7:1
    |
 LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
@@ -15,7 +15,7 @@ error: invalid signature for `extern "custom"` function
 LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
    |                                   ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL - extern "custom" fn must_be_unsafe(a: i64) -> i64 {
@@ -28,7 +28,7 @@ error: invalid signature for `extern "custom"` function
 LL | unsafe extern "custom" fn no_parameters(a: i64) {
    |                                         ^^^^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL - unsafe extern "custom" fn no_parameters(a: i64) {
@@ -41,7 +41,7 @@ error: invalid signature for `extern "custom"` function
 LL | unsafe extern "custom" fn no_return_type() -> i64 {
    |                                               ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL - unsafe extern "custom" fn no_return_type() -> i64 {
@@ -54,7 +54,7 @@ error: invalid signature for `extern "custom"` function
 LL | unsafe extern "custom" fn double(a: i64) -> i64 {
    |                                  ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL - unsafe extern "custom" fn double(a: i64) -> i64 {
@@ -67,7 +67,7 @@ error: invalid signature for `extern "custom"` function
 LL |     unsafe extern "custom" fn is_even(self) -> bool {
    |                                       ^^^^     ^^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     unsafe extern "custom" fn is_even(self) -> bool {
@@ -80,14 +80,14 @@ error: invalid signature for `extern "custom"` function
 LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
    |                                           ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
 LL +     unsafe extern "custom" fn bitwise_not() {
    |
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/bad-custom.rs:52:5
    |
 LL |     extern "custom" fn negate(a: i64) -> i64;
@@ -104,14 +104,14 @@ error: invalid signature for `extern "custom"` function
 LL |     extern "custom" fn negate(a: i64) -> i64;
    |                               ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     extern "custom" fn negate(a: i64) -> i64;
 LL +     extern "custom" fn negate();
    |
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/bad-custom.rs:58:5
    |
 LL |     extern "custom" fn negate(a: i64) -> i64 {
@@ -128,7 +128,7 @@ error: invalid signature for `extern "custom"` function
 LL |     extern "custom" fn negate(a: i64) -> i64 {
    |                               ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     extern "custom" fn negate(a: i64) -> i64 {
@@ -141,14 +141,14 @@ error: invalid signature for `extern "custom"` function
 LL |     fn increment(a: i64) -> i64;
    |                  ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     fn increment(a: i64) -> i64;
 LL +     fn increment();
    |
 
-error: foreign functions with the `"custom"` ABI cannot be safe
+error: foreign functions with the "custom" ABI cannot be safe
   --> $DIR/bad-custom.rs:70:5
    |
 LL |     safe fn extern_cannot_be_safe();
@@ -160,7 +160,7 @@ LL -     safe fn extern_cannot_be_safe();
 LL +     fn extern_cannot_be_safe();
    |
 
-error: functions with the `"custom"` ABI cannot be `async`
+error: functions with the "custom" ABI cannot be `async`
   --> $DIR/bad-custom.rs:97:1
    |
 LL | async unsafe extern "custom" fn no_async_fn() {
@@ -172,7 +172,7 @@ LL - async unsafe extern "custom" fn no_async_fn() {
 LL + unsafe extern "custom" fn no_async_fn() {
    |
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:97:1
    |
 LL | async unsafe extern "custom" fn no_async_fn() {
@@ -197,7 +197,7 @@ LL |     f
    = note: unsafe function cannot be called generically without an unsafe block
    = note: wrap the `unsafe extern "custom" fn()` in a closure with no arguments: `|| { /* code */ }`
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:25:1
    |
 LL | unsafe extern "custom" fn double(a: i64) -> i64 {
@@ -209,7 +209,7 @@ LL + #[unsafe(naked)]
 LL | unsafe extern "custom" fn double(a: i64) -> i64 {
    |
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:34:5
    |
 LL |     unsafe extern "custom" fn is_even(self) -> bool {
@@ -221,7 +221,7 @@ LL +     #[unsafe(naked)]
 LL |     unsafe extern "custom" fn is_even(self) -> bool {
    |
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:42:5
    |
 LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
@@ -233,7 +233,7 @@ LL +     #[unsafe(naked)]
 LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
    |
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:58:5
    |
 LL |     extern "custom" fn negate(a: i64) -> i64 {
@@ -245,43 +245,85 @@ LL +     #[unsafe(naked)]
 LL |     extern "custom" fn negate(a: i64) -> i64 {
    |
 
-error: functions with the `"custom"` ABI cannot be called
+error: functions with the "custom" ABI cannot be called
+  --> $DIR/bad-custom.rs:75:14
+   |
+LL |     unsafe { f(x) }
+   |              ^^^^
+   |
+note: an `extern "custom"` function can only be called using inline assembly
   --> $DIR/bad-custom.rs:75:14
    |
 LL |     unsafe { f(x) }
    |              ^^^^
 
-error: functions with the `"custom"` ABI cannot be called
+error: functions with the "custom" ABI cannot be called
+  --> $DIR/bad-custom.rs:80:14
+   |
+LL |     unsafe { f(x) }
+   |              ^^^^
+   |
+note: an `extern "custom"` function can only be called using inline assembly
   --> $DIR/bad-custom.rs:80:14
    |
 LL |     unsafe { f(x) }
    |              ^^^^
 
-error: functions with the `"custom"` ABI cannot be called
+error: functions with the "custom" ABI cannot be called
+  --> $DIR/bad-custom.rs:87:14
+   |
+LL |     unsafe { f(x) }
+   |              ^^^^
+   |
+note: an `extern "custom"` function can only be called using inline assembly
   --> $DIR/bad-custom.rs:87:14
    |
 LL |     unsafe { f(x) }
    |              ^^^^
 
-error: functions with the `"custom"` ABI cannot be called
+error: functions with the "custom" ABI cannot be called
+  --> $DIR/bad-custom.rs:109:20
+   |
+LL |         assert_eq!(double(21), 42);
+   |                    ^^^^^^^^^^
+   |
+note: an `extern "custom"` function can only be called using inline assembly
   --> $DIR/bad-custom.rs:109:20
    |
 LL |         assert_eq!(double(21), 42);
    |                    ^^^^^^^^^^
 
-error: functions with the `"custom"` ABI cannot be called
+error: functions with the "custom" ABI cannot be called
+  --> $DIR/bad-custom.rs:112:29
+   |
+LL |         assert_eq!(unsafe { increment(41) }, 42);
+   |                             ^^^^^^^^^^^^^
+   |
+note: an `extern "custom"` function can only be called using inline assembly
   --> $DIR/bad-custom.rs:112:29
    |
 LL |         assert_eq!(unsafe { increment(41) }, 42);
    |                             ^^^^^^^^^^^^^
 
-error: functions with the `"custom"` ABI cannot be called
+error: functions with the "custom" ABI cannot be called
+  --> $DIR/bad-custom.rs:115:17
+   |
+LL |         assert!(Thing(41).is_even());
+   |                 ^^^^^^^^^^^^^^^^^^^
+   |
+note: an `extern "custom"` function can only be called using inline assembly
   --> $DIR/bad-custom.rs:115:17
    |
 LL |         assert!(Thing(41).is_even());
    |                 ^^^^^^^^^^^^^^^^^^^
 
-error: functions with the `"custom"` ABI cannot be called
+error: functions with the "custom" ABI cannot be called
+  --> $DIR/bad-custom.rs:118:20
+   |
+LL |         assert_eq!(Thing::bitwise_not(42), !42);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: an `extern "custom"` function can only be called using inline assembly
   --> $DIR/bad-custom.rs:118:20
    |
 LL |         assert_eq!(Thing::bitwise_not(42), !42);
diff --git a/tests/ui/abi/cannot-be-called.avr.stderr b/tests/ui/abi/cannot-be-called.avr.stderr
new file mode 100644
index 00000000000..1129893cbfa
--- /dev/null
+++ b/tests/ui/abi/cannot-be-called.avr.stderr
@@ -0,0 +1,75 @@
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:37:8
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   |        ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:41:8
+   |
+LL | extern "riscv-interrupt-m" fn riscv_m() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:43:8
+   |
+LL | extern "riscv-interrupt-s" fn riscv_s() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:45:8
+   |
+LL | extern "x86-interrupt" fn x86() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:76:26
+   |
+LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:82:26
+   |
+LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:88:22
+   |
+LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error: functions with the "avr-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:50:5
+   |
+LL |     avr();
+   |     ^^^^^
+   |
+note: an `extern "avr-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:50:5
+   |
+LL |     avr();
+   |     ^^^^^
+
+error: functions with the "avr-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:66:5
+   |
+LL |     f()
+   |     ^^^
+   |
+note: an `extern "avr-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:66:5
+   |
+LL |     f()
+   |     ^^^
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/cannot-be-called.i686.stderr b/tests/ui/abi/cannot-be-called.i686.stderr
new file mode 100644
index 00000000000..024d5e2e93d
--- /dev/null
+++ b/tests/ui/abi/cannot-be-called.i686.stderr
@@ -0,0 +1,75 @@
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:37:8
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   |        ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:39:8
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:41:8
+   |
+LL | extern "riscv-interrupt-m" fn riscv_m() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:43:8
+   |
+LL | extern "riscv-interrupt-s" fn riscv_s() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:64:22
+   |
+LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:76:26
+   |
+LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:82:26
+   |
+LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error: functions with the "x86-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:58:5
+   |
+LL |     x86();
+   |     ^^^^^
+   |
+note: an `extern "x86-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:58:5
+   |
+LL |     x86();
+   |     ^^^^^
+
+error: functions with the "x86-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:90:5
+   |
+LL |     f()
+   |     ^^^
+   |
+note: an `extern "x86-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:90:5
+   |
+LL |     f()
+   |     ^^^
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/cannot-be-called.msp430.stderr b/tests/ui/abi/cannot-be-called.msp430.stderr
new file mode 100644
index 00000000000..52d7d792510
--- /dev/null
+++ b/tests/ui/abi/cannot-be-called.msp430.stderr
@@ -0,0 +1,75 @@
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:39:8
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:41:8
+   |
+LL | extern "riscv-interrupt-m" fn riscv_m() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:43:8
+   |
+LL | extern "riscv-interrupt-s" fn riscv_s() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:45:8
+   |
+LL | extern "x86-interrupt" fn x86() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:64:22
+   |
+LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:76:26
+   |
+LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:82:26
+   |
+LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:88:22
+   |
+LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error: functions with the "msp430-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:52:5
+   |
+LL |     msp430();
+   |     ^^^^^^^^
+   |
+note: an `extern "msp430-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:52:5
+   |
+LL |     msp430();
+   |     ^^^^^^^^
+
+error: functions with the "msp430-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:72:5
+   |
+LL |     f()
+   |     ^^^
+   |
+note: an `extern "msp430-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:72:5
+   |
+LL |     f()
+   |     ^^^
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/cannot-be-called.riscv32.stderr b/tests/ui/abi/cannot-be-called.riscv32.stderr
new file mode 100644
index 00000000000..119d93bd58e
--- /dev/null
+++ b/tests/ui/abi/cannot-be-called.riscv32.stderr
@@ -0,0 +1,87 @@
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:37:8
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   |        ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:39:8
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:45:8
+   |
+LL | extern "x86-interrupt" fn x86() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:64:22
+   |
+LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:88:22
+   |
+LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error: functions with the "riscv-interrupt-m" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:54:5
+   |
+LL |     riscv_m();
+   |     ^^^^^^^^^
+   |
+note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:54:5
+   |
+LL |     riscv_m();
+   |     ^^^^^^^^^
+
+error: functions with the "riscv-interrupt-s" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:56:5
+   |
+LL |     riscv_s();
+   |     ^^^^^^^^^
+   |
+note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:56:5
+   |
+LL |     riscv_s();
+   |     ^^^^^^^^^
+
+error: functions with the "riscv-interrupt-m" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:78:5
+   |
+LL |     f()
+   |     ^^^
+   |
+note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:78:5
+   |
+LL |     f()
+   |     ^^^
+
+error: functions with the "riscv-interrupt-s" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:84:5
+   |
+LL |     f()
+   |     ^^^
+   |
+note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:84:5
+   |
+LL |     f()
+   |     ^^^
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/cannot-be-called.riscv64.stderr b/tests/ui/abi/cannot-be-called.riscv64.stderr
new file mode 100644
index 00000000000..119d93bd58e
--- /dev/null
+++ b/tests/ui/abi/cannot-be-called.riscv64.stderr
@@ -0,0 +1,87 @@
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:37:8
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   |        ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:39:8
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:45:8
+   |
+LL | extern "x86-interrupt" fn x86() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:64:22
+   |
+LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:88:22
+   |
+LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error: functions with the "riscv-interrupt-m" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:54:5
+   |
+LL |     riscv_m();
+   |     ^^^^^^^^^
+   |
+note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:54:5
+   |
+LL |     riscv_m();
+   |     ^^^^^^^^^
+
+error: functions with the "riscv-interrupt-s" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:56:5
+   |
+LL |     riscv_s();
+   |     ^^^^^^^^^
+   |
+note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:56:5
+   |
+LL |     riscv_s();
+   |     ^^^^^^^^^
+
+error: functions with the "riscv-interrupt-m" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:78:5
+   |
+LL |     f()
+   |     ^^^
+   |
+note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:78:5
+   |
+LL |     f()
+   |     ^^^
+
+error: functions with the "riscv-interrupt-s" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:84:5
+   |
+LL |     f()
+   |     ^^^
+   |
+note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:84:5
+   |
+LL |     f()
+   |     ^^^
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/cannot-be-called.rs b/tests/ui/abi/cannot-be-called.rs
new file mode 100644
index 00000000000..af979d65d33
--- /dev/null
+++ b/tests/ui/abi/cannot-be-called.rs
@@ -0,0 +1,92 @@
+/*! Tests entry-point ABIs cannot be called
+
+Interrupt ABIs share similar semantics, in that they are special entry-points unusable by Rust.
+So we test that they error in essentially all of the same places.
+*/
+//@ add-core-stubs
+//@ revisions: x64 x64_win i686 riscv32 riscv64 avr msp430
+//
+//@ [x64] needs-llvm-components: x86
+//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib
+//@ [x64_win] needs-llvm-components: x86
+//@ [x64_win] compile-flags: --target=x86_64-pc-windows-msvc --crate-type=rlib
+//@ [i686] needs-llvm-components: x86
+//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib
+//@ [riscv32] needs-llvm-components: riscv
+//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib
+//@ [riscv64] needs-llvm-components: riscv
+//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib
+//@ [avr] needs-llvm-components: avr
+//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib
+//@ [msp430] needs-llvm-components: msp430
+//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib
+#![no_core]
+#![feature(
+    no_core,
+    abi_msp430_interrupt,
+    abi_avr_interrupt,
+    abi_x86_interrupt,
+    abi_riscv_interrupt
+)]
+
+extern crate minicore;
+use minicore::*;
+
+/* extern "interrupt" definition */
+
+extern "msp430-interrupt" fn msp430() {}
+//[x64,x64_win,i686,riscv32,riscv64,avr]~^ ERROR is not a supported ABI
+extern "avr-interrupt" fn avr() {}
+//[x64,x64_win,i686,riscv32,riscv64,msp430]~^ ERROR is not a supported ABI
+extern "riscv-interrupt-m" fn riscv_m() {}
+//[x64,x64_win,i686,avr,msp430]~^ ERROR is not a supported ABI
+extern "riscv-interrupt-s" fn riscv_s() {}
+//[x64,x64_win,i686,avr,msp430]~^ ERROR is not a supported ABI
+extern "x86-interrupt" fn x86() {}
+//[riscv32,riscv64,avr,msp430]~^ ERROR is not a supported ABI
+
+/* extern "interrupt" calls  */
+fn call_the_interrupts() {
+    avr();
+    //[avr]~^ ERROR functions with the "avr-interrupt" ABI cannot be called
+    msp430();
+    //[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
+    riscv_m();
+    //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-m" ABI cannot be called
+    riscv_s();
+    //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-s" ABI cannot be called
+    x86();
+    //[x64,x64_win,i686]~^ ERROR functions with the "x86-interrupt" ABI cannot be called
+}
+
+/* extern "interrupt" fnptr calls */
+
+fn avr_ptr(f: extern "avr-interrupt" fn()) {
+    //[x64,x64_win,i686,riscv32,riscv64,msp430]~^ ERROR is not a supported ABI
+    f()
+    //[avr]~^ ERROR functions with the "avr-interrupt" ABI cannot be called
+}
+
+fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+    //[x64,x64_win,i686,riscv32,riscv64,avr]~^ ERROR is not a supported ABI
+    f()
+    //[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
+}
+
+fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
+    //[x64,x64_win,i686,avr,msp430]~^ ERROR is not a supported ABI
+    f()
+    //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-m" ABI cannot be called
+}
+
+fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
+    //[x64,x64_win,i686,avr,msp430]~^ ERROR is not a supported ABI
+    f()
+    //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-s" ABI cannot be called
+}
+
+fn x86_ptr(f: extern "x86-interrupt" fn()) {
+    //[riscv32,riscv64,avr,msp430]~^ ERROR is not a supported ABI
+    f()
+    //[x64,x64_win,i686]~^ ERROR functions with the "x86-interrupt" ABI cannot be called
+}
diff --git a/tests/ui/abi/cannot-be-called.x64.stderr b/tests/ui/abi/cannot-be-called.x64.stderr
new file mode 100644
index 00000000000..024d5e2e93d
--- /dev/null
+++ b/tests/ui/abi/cannot-be-called.x64.stderr
@@ -0,0 +1,75 @@
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:37:8
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   |        ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:39:8
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:41:8
+   |
+LL | extern "riscv-interrupt-m" fn riscv_m() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:43:8
+   |
+LL | extern "riscv-interrupt-s" fn riscv_s() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:64:22
+   |
+LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:76:26
+   |
+LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:82:26
+   |
+LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error: functions with the "x86-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:58:5
+   |
+LL |     x86();
+   |     ^^^^^
+   |
+note: an `extern "x86-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:58:5
+   |
+LL |     x86();
+   |     ^^^^^
+
+error: functions with the "x86-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:90:5
+   |
+LL |     f()
+   |     ^^^
+   |
+note: an `extern "x86-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:90:5
+   |
+LL |     f()
+   |     ^^^
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/cannot-be-called.x64_win.stderr b/tests/ui/abi/cannot-be-called.x64_win.stderr
new file mode 100644
index 00000000000..024d5e2e93d
--- /dev/null
+++ b/tests/ui/abi/cannot-be-called.x64_win.stderr
@@ -0,0 +1,75 @@
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:37:8
+   |
+LL | extern "msp430-interrupt" fn msp430() {}
+   |        ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:39:8
+   |
+LL | extern "avr-interrupt" fn avr() {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:41:8
+   |
+LL | extern "riscv-interrupt-m" fn riscv_m() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:43:8
+   |
+LL | extern "riscv-interrupt-s" fn riscv_s() {}
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:64:22
+   |
+LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
+   |                      ^^^^^^^^^^^^^^^
+
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:76:26
+   |
+LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:82:26
+   |
+LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
+   |                          ^^^^^^^^^^^^^^^^^^^
+
+error: functions with the "x86-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:58:5
+   |
+LL |     x86();
+   |     ^^^^^
+   |
+note: an `extern "x86-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:58:5
+   |
+LL |     x86();
+   |     ^^^^^
+
+error: functions with the "x86-interrupt" ABI cannot be called
+  --> $DIR/cannot-be-called.rs:90:5
+   |
+LL |     f()
+   |     ^^^
+   |
+note: an `extern "x86-interrupt"` function can only be called using inline assembly
+  --> $DIR/cannot-be-called.rs:90:5
+   |
+LL |     f()
+   |     ^^^
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/cannot-be-coroutine.avr.stderr b/tests/ui/abi/cannot-be-coroutine.avr.stderr
new file mode 100644
index 00000000000..b06da0f3352
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.avr.stderr
@@ -0,0 +1,23 @@
+error: functions with the "avr-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:36:1
+   |
+LL | async extern "avr-interrupt" fn avr() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "avr-interrupt" fn avr() {
+LL + extern "avr-interrupt" fn avr() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.i686.stderr b/tests/ui/abi/cannot-be-coroutine.i686.stderr
new file mode 100644
index 00000000000..cbbddd087c8
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.i686.stderr
@@ -0,0 +1,23 @@
+error: functions with the "x86-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:52:1
+   |
+LL | async extern "x86-interrupt" fn x86() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "x86-interrupt" fn x86() {
+LL + extern "x86-interrupt" fn x86() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.msp430.stderr b/tests/ui/abi/cannot-be-coroutine.msp430.stderr
new file mode 100644
index 00000000000..951ce13b605
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.msp430.stderr
@@ -0,0 +1,23 @@
+error: functions with the "msp430-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:40:1
+   |
+LL | async extern "msp430-interrupt" fn msp430() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "msp430-interrupt" fn msp430() {
+LL + extern "msp430-interrupt" fn msp430() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.riscv32.stderr b/tests/ui/abi/cannot-be-coroutine.riscv32.stderr
new file mode 100644
index 00000000000..8e3b3a2940a
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.riscv32.stderr
@@ -0,0 +1,35 @@
+error: functions with the "riscv-interrupt-m" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:44:1
+   |
+LL | async extern "riscv-interrupt-m" fn riscv_m() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "riscv-interrupt-m" fn riscv_m() {
+LL + extern "riscv-interrupt-m" fn riscv_m() {
+   |
+
+error: functions with the "riscv-interrupt-s" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:48:1
+   |
+LL | async extern "riscv-interrupt-s" fn riscv_s() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "riscv-interrupt-s" fn riscv_s() {
+LL + extern "riscv-interrupt-s" fn riscv_s() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.riscv64.stderr b/tests/ui/abi/cannot-be-coroutine.riscv64.stderr
new file mode 100644
index 00000000000..8e3b3a2940a
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.riscv64.stderr
@@ -0,0 +1,35 @@
+error: functions with the "riscv-interrupt-m" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:44:1
+   |
+LL | async extern "riscv-interrupt-m" fn riscv_m() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "riscv-interrupt-m" fn riscv_m() {
+LL + extern "riscv-interrupt-m" fn riscv_m() {
+   |
+
+error: functions with the "riscv-interrupt-s" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:48:1
+   |
+LL | async extern "riscv-interrupt-s" fn riscv_s() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "riscv-interrupt-s" fn riscv_s() {
+LL + extern "riscv-interrupt-s" fn riscv_s() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.rs b/tests/ui/abi/cannot-be-coroutine.rs
new file mode 100644
index 00000000000..7270a55f69e
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.rs
@@ -0,0 +1,54 @@
+//@ add-core-stubs
+//@ edition: 2021
+//@ revisions: x64 x64_win i686 riscv32 riscv64 avr msp430
+//
+//@ [x64] needs-llvm-components: x86
+//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib
+//@ [x64_win] needs-llvm-components: x86
+//@ [x64_win] compile-flags: --target=x86_64-pc-windows-msvc --crate-type=rlib
+//@ [i686] needs-llvm-components: x86
+//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib
+//@ [riscv32] needs-llvm-components: riscv
+//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib
+//@ [riscv64] needs-llvm-components: riscv
+//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib
+//@ [avr] needs-llvm-components: avr
+//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib
+//@ [msp430] needs-llvm-components: msp430
+//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib
+#![no_core]
+#![feature(
+    no_core,
+    abi_msp430_interrupt,
+    abi_avr_interrupt,
+    abi_x86_interrupt,
+    abi_riscv_interrupt
+)]
+
+extern crate minicore;
+use minicore::*;
+
+// We ignore this error; implementing all of the async-related lang items is not worth it.
+async fn vanilla(){
+    //~^ ERROR requires `ResumeTy` lang_item
+}
+
+async extern "avr-interrupt" fn avr() {
+    //[avr]~^ ERROR functions with the "avr-interrupt" ABI cannot be `async`
+}
+
+async extern "msp430-interrupt" fn msp430() {
+    //[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be `async`
+}
+
+async extern "riscv-interrupt-m" fn riscv_m() {
+    //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-m" ABI cannot be `async`
+}
+
+async extern "riscv-interrupt-s" fn riscv_s() {
+    //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-s" ABI cannot be `async`
+}
+
+async extern "x86-interrupt" fn x86() {
+    //[x64,x64_win,i686]~^ ERROR functions with the "x86-interrupt" ABI cannot be `async`
+}
diff --git a/tests/ui/abi/cannot-be-coroutine.x64.stderr b/tests/ui/abi/cannot-be-coroutine.x64.stderr
new file mode 100644
index 00000000000..cbbddd087c8
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.x64.stderr
@@ -0,0 +1,23 @@
+error: functions with the "x86-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:52:1
+   |
+LL | async extern "x86-interrupt" fn x86() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "x86-interrupt" fn x86() {
+LL + extern "x86-interrupt" fn x86() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.x64_win.stderr b/tests/ui/abi/cannot-be-coroutine.x64_win.stderr
new file mode 100644
index 00000000000..cbbddd087c8
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.x64_win.stderr
@@ -0,0 +1,23 @@
+error: functions with the "x86-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:52:1
+   |
+LL | async extern "x86-interrupt" fn x86() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "x86-interrupt" fn x86() {
+LL + extern "x86-interrupt" fn x86() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/invalid-call-abi-ctfe.rs b/tests/ui/abi/invalid-call-abi-ctfe.rs
new file mode 100644
index 00000000000..343cc728fe3
--- /dev/null
+++ b/tests/ui/abi/invalid-call-abi-ctfe.rs
@@ -0,0 +1,14 @@
+// Fix for #142969 where an invalid ABI in a signature still had its call ABI computed
+// because CTFE tried to evaluate it, despite previous errors during AST-to-HIR lowering.
+
+#![feature(rustc_attrs)]
+
+const extern "rust-invalid" fn foo() {
+    //~^ ERROR "rust-invalid" is not a supported ABI for the current target
+    panic!()
+}
+
+const _: () = foo();
+
+
+fn main() {}
diff --git a/tests/ui/abi/invalid-call-abi-ctfe.stderr b/tests/ui/abi/invalid-call-abi-ctfe.stderr
new file mode 100644
index 00000000000..402de4b69b9
--- /dev/null
+++ b/tests/ui/abi/invalid-call-abi-ctfe.stderr
@@ -0,0 +1,9 @@
+error[E0570]: "rust-invalid" is not a supported ABI for the current target
+  --> $DIR/invalid-call-abi-ctfe.rs:6:14
+   |
+LL | const extern "rust-invalid" fn foo() {
+   |              ^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/invalid-call-abi.rs b/tests/ui/abi/invalid-call-abi.rs
new file mode 100644
index 00000000000..076ddd91ab0
--- /dev/null
+++ b/tests/ui/abi/invalid-call-abi.rs
@@ -0,0 +1,12 @@
+// Tests the `"rustc-invalid"` ABI, which is never canonizable.
+
+#![feature(rustc_attrs)]
+
+const extern "rust-invalid" fn foo() {
+    //~^ ERROR "rust-invalid" is not a supported ABI for the current target
+    panic!()
+}
+
+fn main() {
+    foo();
+}
diff --git a/tests/ui/abi/invalid-call-abi.stderr b/tests/ui/abi/invalid-call-abi.stderr
new file mode 100644
index 00000000000..c4a90158dcf
--- /dev/null
+++ b/tests/ui/abi/invalid-call-abi.stderr
@@ -0,0 +1,9 @@
+error[E0570]: "rust-invalid" is not a supported ABI for the current target
+  --> $DIR/invalid-call-abi.rs:5:14
+   |
+LL | const extern "rust-invalid" fn foo() {
+   |              ^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported-abi-transmute.rs b/tests/ui/abi/unsupported-abi-transmute.rs
new file mode 100644
index 00000000000..42aa180e1fd
--- /dev/null
+++ b/tests/ui/abi/unsupported-abi-transmute.rs
@@ -0,0 +1,12 @@
+// Check we error before unsupported ABIs reach codegen stages.
+
+//@ edition: 2018
+//@ compile-flags: --crate-type=lib
+#![feature(rustc_attrs)]
+
+use core::mem;
+
+fn anything() {
+    let a = unsafe { mem::transmute::<usize, extern "rust-invalid" fn(i32)>(4) }(2);
+    //~^ ERROR: is not a supported ABI for the current target [E0570]
+}
diff --git a/tests/ui/abi/unsupported-abi-transmute.stderr b/tests/ui/abi/unsupported-abi-transmute.stderr
new file mode 100644
index 00000000000..f1d202b1a1c
--- /dev/null
+++ b/tests/ui/abi/unsupported-abi-transmute.stderr
@@ -0,0 +1,9 @@
+error[E0570]: "rust-invalid" is not a supported ABI for the current target
+  --> $DIR/unsupported-abi-transmute.rs:10:53
+   |
+LL |     let a = unsafe { mem::transmute::<usize, extern "rust-invalid" fn(i32)>(4) }(2);
+   |                                                     ^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported-in-impls.rs b/tests/ui/abi/unsupported-in-impls.rs
new file mode 100644
index 00000000000..71797954865
--- /dev/null
+++ b/tests/ui/abi/unsupported-in-impls.rs
@@ -0,0 +1,36 @@
+// Test for https://github.com/rust-lang/rust/issues/86232
+// Due to AST-to-HIR lowering nuances, we used to allow unsupported ABIs to "leak" into the HIR
+// without being checked, as we would check after generating the ExternAbi.
+// Checking afterwards only works if we examine every HIR construct that contains an ExternAbi,
+// and those may be very different in HIR, even if they read the same in source.
+// This made it very easy to make mistakes.
+//
+// Here we test that an unsupported ABI in various impl-related positions will be rejected,
+// both in the original declarations and the actual implementations.
+
+#![feature(rustc_attrs)]
+//@ compile-flags: --crate-type lib
+
+pub struct FnPtrBearer {
+    pub ptr: extern "rust-invalid" fn(),
+    //~^ ERROR: is not a supported ABI
+}
+
+impl FnPtrBearer {
+    pub extern "rust-invalid" fn inherent_fn(self) {
+        //~^ ERROR: is not a supported ABI
+        (self.ptr)()
+    }
+}
+
+pub trait Trait {
+    extern "rust-invalid" fn trait_fn(self);
+    //~^ ERROR: is not a supported ABI
+}
+
+impl Trait for FnPtrBearer {
+    extern "rust-invalid" fn trait_fn(self) {
+        //~^ ERROR: is not a supported ABI
+        self.inherent_fn()
+    }
+}
diff --git a/tests/ui/abi/unsupported-in-impls.stderr b/tests/ui/abi/unsupported-in-impls.stderr
new file mode 100644
index 00000000000..d7a188f8a04
--- /dev/null
+++ b/tests/ui/abi/unsupported-in-impls.stderr
@@ -0,0 +1,27 @@
+error[E0570]: "rust-invalid" is not a supported ABI for the current target
+  --> $DIR/unsupported-in-impls.rs:15:21
+   |
+LL |     pub ptr: extern "rust-invalid" fn(),
+   |                     ^^^^^^^^^^^^^^
+
+error[E0570]: "rust-invalid" is not a supported ABI for the current target
+  --> $DIR/unsupported-in-impls.rs:20:16
+   |
+LL |     pub extern "rust-invalid" fn inherent_fn(self) {
+   |                ^^^^^^^^^^^^^^
+
+error[E0570]: "rust-invalid" is not a supported ABI for the current target
+  --> $DIR/unsupported-in-impls.rs:27:12
+   |
+LL |     extern "rust-invalid" fn trait_fn(self);
+   |            ^^^^^^^^^^^^^^
+
+error[E0570]: "rust-invalid" is not a supported ABI for the current target
+  --> $DIR/unsupported-in-impls.rs:32:12
+   |
+LL |     extern "rust-invalid" fn trait_fn(self) {
+   |            ^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported-varargs-fnptr.rs b/tests/ui/abi/unsupported-varargs-fnptr.rs
new file mode 100644
index 00000000000..1d23916d039
--- /dev/null
+++ b/tests/ui/abi/unsupported-varargs-fnptr.rs
@@ -0,0 +1,19 @@
+// FIXME(workingjubilee): add revisions and generalize to other platform-specific varargs ABIs,
+// preferably after the only-arch directive is enhanced with an "or pattern" syntax
+// NOTE: This deliberately tests an ABI that supports varargs, so no `extern "rust-invalid"`
+//@ only-x86_64
+
+// We have to use this flag to force ABI computation of an invalid ABI
+//@ compile-flags: -Clink-dead-code
+
+#![feature(extended_varargs_abi_support)]
+
+// sometimes fn ptrs with varargs make layout and ABI computation ICE
+// as found in https://github.com/rust-lang/rust/issues/142107
+
+fn aapcs(f: extern "aapcs" fn(usize, ...)) {
+//~^ ERROR [E0570]
+// Note we DO NOT have to actually make a call to trigger the ICE!
+}
+
+fn main() {}
diff --git a/tests/ui/abi/unsupported-varargs-fnptr.stderr b/tests/ui/abi/unsupported-varargs-fnptr.stderr
new file mode 100644
index 00000000000..238f2b31330
--- /dev/null
+++ b/tests/ui/abi/unsupported-varargs-fnptr.stderr
@@ -0,0 +1,9 @@
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported-varargs-fnptr.rs:14:20
+   |
+LL | fn aapcs(f: extern "aapcs" fn(usize, ...)) {
+   |                    ^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.aarch64.stderr b/tests/ui/abi/unsupported.aarch64.stderr
index 4721c26026d..61d07f29fd7 100644
--- a/tests/ui/abi/unsupported.aarch64.stderr
+++ b/tests/ui/abi/unsupported.aarch64.stderr
@@ -1,403 +1,202 @@
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:8
    |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "ptx-kernel" fn ptx() {}
+   |        ^^^^^^^^^^^^
+
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:22
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
+   |                      ^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:43:1
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:42:8
    |
 LL | extern "ptx-kernel" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:44:8
    |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "gpu-kernel" fn gpu() {}
+   |        ^^^^^^^^^^^^
 
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:55:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:47:8
    |
-LL | extern "aapcs" {}
-   | ^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" fn aapcs() {}
+   |        ^^^^^^^
 
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:49:24
    |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
+   |                        ^^^^^^^
 
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:65:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:53:8
    |
-LL | extern "msp430-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" {}
+   |        ^^^^^^^
 
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:56:8
    |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "msp430-interrupt" {}
+   |        ^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:75:1
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:59:8
    |
 LL | extern "avr-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
-   |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |        ^^^^^^^^^^^^^^^
 
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:85:1
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:62:8
    |
 LL | extern "riscv-interrupt-m" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^
 
-warning: the calling convention "x86-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:90:15
-   |
-LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:65:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "x86-interrupt" {}
+   |        ^^^^^^^^^^^^^^^
 
-error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:95:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:68:8
    |
-LL | extern "x86-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "thiscall" fn thiscall() {}
+   |        ^^^^^^^^^^
 
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:70:27
    |
 LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |                           ^^^^^^^^^^
 
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:105:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:74:8
    |
 LL | extern "thiscall" {}
-   | ^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^
+
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:77:8
+   |
+LL | extern "stdcall" fn stdcall() {}
+   |        ^^^^^^^^^
+   |
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:81:26
    |
 LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
+   |                          ^^^^^^^^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:119:1
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:87:8
    |
 LL | extern "stdcall" {}
-   | ^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^
    |
    = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall-unwind"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:123:1
+error[E0570]: "stdcall-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:91:8
    |
 LL | extern "stdcall-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^
    |
    = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"`
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
-   |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:111:8
    |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
+LL | extern "vectorcall" fn vectorcall() {}
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "vectorcall" is not supported on this target
-  --> $DIR/unsupported.rs:145:22
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:113:29
    |
 LL | fn vectorcall_ptr(f: extern "vectorcall" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |                             ^^^^^^^^^^^^
 
-error[E0570]: `"vectorcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:150:1
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:117:8
    |
 LL | extern "vectorcall" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "cmse-nonsecure-call" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:120:28
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | fn cmse_call_ptr(f: extern "cmse-nonsecure-call" fn()) {
+   |                            ^^^^^^^^^^^^^^^^^^^^^
 
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:125:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "cmse-nonsecure-entry" fn cmse_entry() {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:166:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:127:29
    |
-LL | extern "C-cmse-nonsecure-entry" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cmse_entry_ptr(f: extern "cmse-nonsecure-entry" fn()) {
+   |                             ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:36:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:131:8
    |
-LL | extern "ptx-kernel" fn ptx() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:45:1
-   |
-LL | extern "gpu-kernel" fn gpu() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:48:1
-   |
-LL | extern "aapcs" fn aapcs() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:58:1
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:99:17
    |
-LL | extern "msp430-interrupt" fn msp430() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:68:1
-   |
-LL | extern "avr-interrupt" fn avr() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:78:1
-   |
-LL | extern "riscv-interrupt-m" fn riscv() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:88:1
-   |
-LL | extern "x86-interrupt" fn x86() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:98:1
-   |
-LL | extern "thiscall" fn thiscall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:108:1
-   |
-LL | extern "stdcall" fn stdcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
-   |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
+   |                 ^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
+   = note: `#[warn(unsupported_calling_conventions)]` on by default
 
-error[E0570]: `"vectorcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:143:1
-   |
-LL | extern "vectorcall" fn vectorcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:159:1
-   |
-LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 22 previous errors; 15 warnings emitted
-
-For more information about this error, try `rustc --explain E0570`.
-Future incompatibility report: Future breakage diagnostic:
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
-   |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
-   |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
-   |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "x86-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:90:15
-   |
-LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
-   |
-LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:104:1
    |
-LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl" {}
+   | ^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "vectorcall" is not supported on this target
-  --> $DIR/unsupported.rs:145:22
+warning: "cdecl-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:107:1
    |
-LL | fn vectorcall_ptr(f: extern "vectorcall" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl-unwind" {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C-unwind"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:96:1
    |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl" fn cdecl() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+error: aborting due to 25 previous errors; 4 warnings emitted
 
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.arm.stderr b/tests/ui/abi/unsupported.arm.stderr
index ed9cd2ab2c5..37b6e2316b0 100644
--- a/tests/ui/abi/unsupported.arm.stderr
+++ b/tests/ui/abi/unsupported.arm.stderr
@@ -1,371 +1,184 @@
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:8
    |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "ptx-kernel" fn ptx() {}
+   |        ^^^^^^^^^^^^
+
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:22
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
+   |                      ^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:43:1
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:42:8
    |
 LL | extern "ptx-kernel" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:44:8
    |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "gpu-kernel" fn gpu() {}
+   |        ^^^^^^^^^^^^
 
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:65:1
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:56:8
    |
 LL | extern "msp430-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |        ^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:75:1
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:59:8
    |
 LL | extern "avr-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^
 
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
-   |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:85:1
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:62:8
    |
 LL | extern "riscv-interrupt-m" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^
 
-warning: the calling convention "x86-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:90:15
-   |
-LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:65:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "x86-interrupt" {}
+   |        ^^^^^^^^^^^^^^^
 
-error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:95:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:68:8
    |
-LL | extern "x86-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "thiscall" fn thiscall() {}
+   |        ^^^^^^^^^^
 
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:70:27
    |
 LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |                           ^^^^^^^^^^
 
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:105:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:74:8
    |
 LL | extern "thiscall" {}
-   | ^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^
 
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:77:8
+   |
+LL | extern "stdcall" fn stdcall() {}
+   |        ^^^^^^^^^
+   |
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
+
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:81:26
    |
 LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
+   |                          ^^^^^^^^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:119:1
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:87:8
    |
 LL | extern "stdcall" {}
-   | ^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^
    |
    = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall-unwind"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:123:1
+error[E0570]: "stdcall-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:91:8
    |
 LL | extern "stdcall-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^
    |
    = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"`
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
-   |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:111:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
+LL | extern "vectorcall" fn vectorcall() {}
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "vectorcall" is not supported on this target
-  --> $DIR/unsupported.rs:145:22
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:113:29
    |
 LL | fn vectorcall_ptr(f: extern "vectorcall" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |                             ^^^^^^^^^^^^
 
-error[E0570]: `"vectorcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:150:1
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:117:8
    |
 LL | extern "vectorcall" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
-
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:166:1
-   |
-LL | extern "C-cmse-nonsecure-entry" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:36:1
-   |
-LL | extern "ptx-kernel" fn ptx() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:45:1
-   |
-LL | extern "gpu-kernel" fn gpu() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:58:1
-   |
-LL | extern "msp430-interrupt" fn msp430() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:68:1
-   |
-LL | extern "avr-interrupt" fn avr() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^
 
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:78:1
+error[E0570]: "cmse-nonsecure-call" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:120:28
    |
-LL | extern "riscv-interrupt-m" fn riscv() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cmse_call_ptr(f: extern "cmse-nonsecure-call" fn()) {
+   |                            ^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:88:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:125:8
    |
-LL | extern "x86-interrupt" fn x86() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" fn cmse_entry() {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:98:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:127:29
    |
-LL | extern "thiscall" fn thiscall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cmse_entry_ptr(f: extern "cmse-nonsecure-entry" fn()) {
+   |                             ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:108:1
-   |
-LL | extern "stdcall" fn stdcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:131:8
    |
-   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
+LL | extern "cmse-nonsecure-entry" {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:99:17
    |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
+   |                 ^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
+   = note: `#[warn(unsupported_calling_conventions)]` on by default
 
-error[E0570]: `"vectorcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:143:1
-   |
-LL | extern "vectorcall" fn vectorcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:159:1
-   |
-LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 20 previous errors; 14 warnings emitted
-
-For more information about this error, try `rustc --explain E0570`.
-Future incompatibility report: Future breakage diagnostic:
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
-   |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
-   |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "x86-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:90:15
-   |
-LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
-   |
-LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:104:1
    |
-LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl" {}
+   | ^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "vectorcall" is not supported on this target
-  --> $DIR/unsupported.rs:145:22
+warning: "cdecl-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:107:1
    |
-LL | fn vectorcall_ptr(f: extern "vectorcall" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl-unwind" {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C-unwind"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:96:1
    |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl" fn cdecl() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+error: aborting due to 22 previous errors; 4 warnings emitted
 
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.i686.stderr b/tests/ui/abi/unsupported.i686.stderr
index 4d903b435d8..8478c481941 100644
--- a/tests/ui/abi/unsupported.i686.stderr
+++ b/tests/ui/abi/unsupported.i686.stderr
@@ -1,222 +1,87 @@
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:8
    |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "ptx-kernel" fn ptx() {}
+   |        ^^^^^^^^^^^^
+
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:22
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
+   |                      ^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:43:1
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:42:8
    |
 LL | extern "ptx-kernel" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:44:8
    |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "gpu-kernel" fn gpu() {}
+   |        ^^^^^^^^^^^^
 
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:55:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:47:8
    |
-LL | extern "aapcs" {}
-   | ^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" fn aapcs() {}
+   |        ^^^^^^^
 
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:49:24
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
+   |                        ^^^^^^^
 
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:65:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:53:8
    |
-LL | extern "msp430-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" {}
+   |        ^^^^^^^
 
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:56:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "msp430-interrupt" {}
+   |        ^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:75:1
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:59:8
    |
 LL | extern "avr-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^
 
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
-   |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:85:1
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:62:8
    |
 LL | extern "riscv-interrupt-m" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:166:1
-   |
-LL | extern "C-cmse-nonsecure-entry" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:36:1
-   |
-LL | extern "ptx-kernel" fn ptx() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:45:1
+error[E0570]: "cmse-nonsecure-call" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:120:28
    |
-LL | extern "gpu-kernel" fn gpu() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:48:1
-   |
-LL | extern "aapcs" fn aapcs() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cmse_call_ptr(f: extern "cmse-nonsecure-call" fn()) {
+   |                            ^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:58:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:125:8
    |
-LL | extern "msp430-interrupt" fn msp430() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" fn cmse_entry() {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:68:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:127:29
    |
-LL | extern "avr-interrupt" fn avr() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cmse_entry_ptr(f: extern "cmse-nonsecure-entry" fn()) {
+   |                             ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:78:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:131:8
    |
-LL | extern "riscv-interrupt-m" fn riscv() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:159:1
-   |
-LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 13 previous errors; 7 warnings emitted
+error: aborting due to 14 previous errors
 
 For more information about this error, try `rustc --explain E0570`.
-Future incompatibility report: Future breakage diagnostic:
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
-   |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
-   |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
-   |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
diff --git a/tests/ui/abi/unsupported.riscv32.stderr b/tests/ui/abi/unsupported.riscv32.stderr
index 9e75dfafca0..d7eb222eb76 100644
--- a/tests/ui/abi/unsupported.riscv32.stderr
+++ b/tests/ui/abi/unsupported.riscv32.stderr
@@ -1,371 +1,196 @@
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:8
    |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "ptx-kernel" fn ptx() {}
+   |        ^^^^^^^^^^^^
+
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:22
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
+   |                      ^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:43:1
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:42:8
    |
 LL | extern "ptx-kernel" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:44:8
    |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "gpu-kernel" fn gpu() {}
+   |        ^^^^^^^^^^^^
 
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:55:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:47:8
    |
-LL | extern "aapcs" {}
-   | ^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" fn aapcs() {}
+   |        ^^^^^^^
 
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:49:24
    |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
+   |                        ^^^^^^^
 
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:65:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:53:8
    |
-LL | extern "msp430-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" {}
+   |        ^^^^^^^
 
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:56:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "msp430-interrupt" {}
+   |        ^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:75:1
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:59:8
    |
 LL | extern "avr-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^
 
-warning: the calling convention "x86-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:90:15
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:65:8
    |
-LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "x86-interrupt" {}
+   |        ^^^^^^^^^^^^^^^
 
-error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:95:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:68:8
    |
-LL | extern "x86-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "thiscall" fn thiscall() {}
+   |        ^^^^^^^^^^
 
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:70:27
    |
 LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |                           ^^^^^^^^^^
 
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:105:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:74:8
    |
 LL | extern "thiscall" {}
-   | ^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^
 
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:77:8
+   |
+LL | extern "stdcall" fn stdcall() {}
+   |        ^^^^^^^^^
+   |
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
+
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:81:26
    |
 LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
+   |                          ^^^^^^^^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:119:1
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:87:8
    |
 LL | extern "stdcall" {}
-   | ^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^
    |
    = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall-unwind"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:123:1
+error[E0570]: "stdcall-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:91:8
    |
 LL | extern "stdcall-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^
    |
    = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"`
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:111:8
    |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
+LL | extern "vectorcall" fn vectorcall() {}
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "vectorcall" is not supported on this target
-  --> $DIR/unsupported.rs:145:22
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:113:29
    |
 LL | fn vectorcall_ptr(f: extern "vectorcall" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |                             ^^^^^^^^^^^^
 
-error[E0570]: `"vectorcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:150:1
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:117:8
    |
 LL | extern "vectorcall" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
-
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "cmse-nonsecure-call" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:120:28
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | fn cmse_call_ptr(f: extern "cmse-nonsecure-call" fn()) {
+   |                            ^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:166:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:125:8
    |
-LL | extern "C-cmse-nonsecure-entry" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" fn cmse_entry() {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:36:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:127:29
    |
-LL | extern "ptx-kernel" fn ptx() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cmse_entry_ptr(f: extern "cmse-nonsecure-entry" fn()) {
+   |                             ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:45:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:131:8
    |
-LL | extern "gpu-kernel" fn gpu() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:48:1
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:99:17
    |
-LL | extern "aapcs" fn aapcs() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:58:1
-   |
-LL | extern "msp430-interrupt" fn msp430() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:68:1
-   |
-LL | extern "avr-interrupt" fn avr() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:88:1
-   |
-LL | extern "x86-interrupt" fn x86() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:98:1
-   |
-LL | extern "thiscall" fn thiscall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:108:1
-   |
-LL | extern "stdcall" fn stdcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
-   |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
+   |                 ^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
+   = note: `#[warn(unsupported_calling_conventions)]` on by default
 
-error[E0570]: `"vectorcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:143:1
-   |
-LL | extern "vectorcall" fn vectorcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:159:1
-   |
-LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 20 previous errors; 14 warnings emitted
-
-For more information about this error, try `rustc --explain E0570`.
-Future incompatibility report: Future breakage diagnostic:
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
-   |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
-   |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "x86-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:90:15
-   |
-LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
-   |
-LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:104:1
    |
-LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl" {}
+   | ^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "vectorcall" is not supported on this target
-  --> $DIR/unsupported.rs:145:22
+warning: "cdecl-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:107:1
    |
-LL | fn vectorcall_ptr(f: extern "vectorcall" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl-unwind" {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C-unwind"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:96:1
    |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl" fn cdecl() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+error: aborting due to 24 previous errors; 4 warnings emitted
 
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.riscv64.stderr b/tests/ui/abi/unsupported.riscv64.stderr
index 9e75dfafca0..d7eb222eb76 100644
--- a/tests/ui/abi/unsupported.riscv64.stderr
+++ b/tests/ui/abi/unsupported.riscv64.stderr
@@ -1,371 +1,196 @@
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:8
    |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "ptx-kernel" fn ptx() {}
+   |        ^^^^^^^^^^^^
+
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:22
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
+   |                      ^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:43:1
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:42:8
    |
 LL | extern "ptx-kernel" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:44:8
    |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "gpu-kernel" fn gpu() {}
+   |        ^^^^^^^^^^^^
 
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:55:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:47:8
    |
-LL | extern "aapcs" {}
-   | ^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" fn aapcs() {}
+   |        ^^^^^^^
 
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:49:24
    |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
+   |                        ^^^^^^^
 
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:65:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:53:8
    |
-LL | extern "msp430-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" {}
+   |        ^^^^^^^
 
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:56:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "msp430-interrupt" {}
+   |        ^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:75:1
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:59:8
    |
 LL | extern "avr-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^
 
-warning: the calling convention "x86-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:90:15
+error[E0570]: "x86-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:65:8
    |
-LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "x86-interrupt" {}
+   |        ^^^^^^^^^^^^^^^
 
-error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:95:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:68:8
    |
-LL | extern "x86-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "thiscall" fn thiscall() {}
+   |        ^^^^^^^^^^
 
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:70:27
    |
 LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |                           ^^^^^^^^^^
 
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:105:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:74:8
    |
 LL | extern "thiscall" {}
-   | ^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^
 
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:77:8
+   |
+LL | extern "stdcall" fn stdcall() {}
+   |        ^^^^^^^^^
+   |
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
+
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:81:26
    |
 LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
+   |                          ^^^^^^^^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:119:1
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:87:8
    |
 LL | extern "stdcall" {}
-   | ^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^
    |
    = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall-unwind"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:123:1
+error[E0570]: "stdcall-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:91:8
    |
 LL | extern "stdcall-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^
    |
    = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"`
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:111:8
    |
-LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-   = note: `#[warn(unsupported_calling_conventions)]` on by default
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
-   |
-LL | extern "cdecl" {}
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C"` instead
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
-   |
-LL | extern "cdecl-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
-   = help: use `extern "C-unwind"` instead
+LL | extern "vectorcall" fn vectorcall() {}
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "vectorcall" is not supported on this target
-  --> $DIR/unsupported.rs:145:22
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:113:29
    |
 LL | fn vectorcall_ptr(f: extern "vectorcall" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |                             ^^^^^^^^^^^^
 
-error[E0570]: `"vectorcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:150:1
+error[E0570]: "vectorcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:117:8
    |
 LL | extern "vectorcall" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
-
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "cmse-nonsecure-call" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:120:28
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | fn cmse_call_ptr(f: extern "cmse-nonsecure-call" fn()) {
+   |                            ^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:166:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:125:8
    |
-LL | extern "C-cmse-nonsecure-entry" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" fn cmse_entry() {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:36:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:127:29
    |
-LL | extern "ptx-kernel" fn ptx() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cmse_entry_ptr(f: extern "cmse-nonsecure-entry" fn()) {
+   |                             ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:45:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:131:8
    |
-LL | extern "gpu-kernel" fn gpu() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:48:1
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:99:17
    |
-LL | extern "aapcs" fn aapcs() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:58:1
-   |
-LL | extern "msp430-interrupt" fn msp430() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:68:1
-   |
-LL | extern "avr-interrupt" fn avr() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:88:1
-   |
-LL | extern "x86-interrupt" fn x86() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:98:1
-   |
-LL | extern "thiscall" fn thiscall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:108:1
-   |
-LL | extern "stdcall" fn stdcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
-   |
-LL | extern "cdecl" fn cdecl() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
+   |                 ^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
+   = note: `#[warn(unsupported_calling_conventions)]` on by default
 
-error[E0570]: `"vectorcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:143:1
-   |
-LL | extern "vectorcall" fn vectorcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:159:1
-   |
-LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 20 previous errors; 14 warnings emitted
-
-For more information about this error, try `rustc --explain E0570`.
-Future incompatibility report: Future breakage diagnostic:
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
-   |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
-   |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "x86-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:90:15
-   |
-LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
-   |
-LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:104:1
    |
-LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl" {}
+   | ^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "vectorcall" is not supported on this target
-  --> $DIR/unsupported.rs:145:22
+warning: "cdecl-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:107:1
    |
-LL | fn vectorcall_ptr(f: extern "vectorcall" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl-unwind" {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C-unwind"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:96:1
    |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cdecl" fn cdecl() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+   = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
+   = help: use `extern "C"` instead
 
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+error: aborting due to 24 previous errors; 4 warnings emitted
 
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/unsupported.rs b/tests/ui/abi/unsupported.rs
index 43bdfe3ea24..828fcc147a5 100644
--- a/tests/ui/abi/unsupported.rs
+++ b/tests/ui/abi/unsupported.rs
@@ -25,7 +25,7 @@
     abi_gpu_kernel,
     abi_x86_interrupt,
     abi_riscv_interrupt,
-    abi_c_cmse_nonsecure_call,
+    abi_cmse_nonsecure_call,
     abi_vectorcall,
     cmse_nonsecure_entry
 )]
@@ -36,8 +36,7 @@ use minicore::*;
 extern "ptx-kernel" fn ptx() {}
 //~^ ERROR is not a supported ABI
 fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-    //~^ WARN unsupported_fn_ptr_calling_conventions
-    //~^^ WARN this was previously accepted
+//~^ ERROR is not a supported ABI
     f()
 }
 extern "ptx-kernel" {}
@@ -48,58 +47,28 @@ extern "gpu-kernel" fn gpu() {}
 extern "aapcs" fn aapcs() {}
 //[x64,x64_win,i686,aarch64,riscv32,riscv64]~^ ERROR is not a supported ABI
 fn aapcs_ptr(f: extern "aapcs" fn()) {
-    //[x64,x64_win,i686,aarch64,riscv32,riscv64]~^ WARN unsupported_fn_ptr_calling_conventions
-    //[x64,x64_win,i686,aarch64,riscv32,riscv64]~^^ WARN this was previously accepted
+    //[x64,x64_win,i686,aarch64,riscv32,riscv64]~^ ERROR is not a supported ABI
     f()
 }
 extern "aapcs" {}
 //[x64,x64_win,i686,aarch64,riscv32,riscv64]~^ ERROR is not a supported ABI
 
-extern "msp430-interrupt" fn msp430() {}
-//~^ ERROR is not a supported ABI
-fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-    //~^ WARN unsupported_fn_ptr_calling_conventions
-    //~^^ WARN this was previously accepted
-    f()
-}
 extern "msp430-interrupt" {}
 //~^ ERROR is not a supported ABI
 
-extern "avr-interrupt" fn avr() {}
-//~^ ERROR is not a supported ABI
-fn avr_ptr(f: extern "avr-interrupt" fn()) {
-    //~^ WARN unsupported_fn_ptr_calling_conventions
-    //~^^ WARN this was previously accepted
-    f()
-}
 extern "avr-interrupt" {}
 //~^ ERROR is not a supported ABI
 
-extern "riscv-interrupt-m" fn riscv() {}
-//[x64,x64_win,i686,arm,aarch64]~^ ERROR is not a supported ABI
-fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-    //[x64,x64_win,i686,arm,aarch64]~^ WARN unsupported_fn_ptr_calling_conventions
-    //[x64,x64_win,i686,arm,aarch64]~^^ WARN this was previously accepted
-    f()
-}
 extern "riscv-interrupt-m" {}
 //[x64,x64_win,i686,arm,aarch64]~^ ERROR is not a supported ABI
 
-extern "x86-interrupt" fn x86() {}
-//[aarch64,arm,riscv32,riscv64]~^ ERROR is not a supported ABI
-fn x86_ptr(f: extern "x86-interrupt" fn()) {
-    //[aarch64,arm,riscv32,riscv64]~^ WARN unsupported_fn_ptr_calling_conventions
-    //[aarch64,arm,riscv32,riscv64]~^^ WARN this was previously accepted
-    f()
-}
 extern "x86-interrupt" {}
 //[aarch64,arm,riscv32,riscv64]~^ ERROR is not a supported ABI
 
 extern "thiscall" fn thiscall() {}
 //[x64,x64_win,arm,aarch64,riscv32,riscv64]~^ ERROR is not a supported ABI
 fn thiscall_ptr(f: extern "thiscall" fn()) {
-    //[x64,x64_win,arm,aarch64,riscv32,riscv64]~^ WARN unsupported_fn_ptr_calling_conventions
-    //[x64,x64_win,arm,aarch64,riscv32,riscv64]~^^ WARN this was previously accepted
+    //[x64,x64_win,arm,aarch64,riscv32,riscv64]~^ ERROR is not a supported ABI
     f()
 }
 extern "thiscall" {}
@@ -110,10 +79,9 @@ extern "stdcall" fn stdcall() {}
 //[x64_win]~^^ WARN unsupported_calling_conventions
 //[x64_win]~^^^ WARN this was previously accepted
 fn stdcall_ptr(f: extern "stdcall" fn()) {
-    //[x64_win]~^ WARN unsupported_calling_conventions
-    //[x64_win]~| WARN this was previously accepted
-    //[x64,arm,aarch64,riscv32,riscv64]~^^^ WARN unsupported_fn_ptr_calling_conventions
-    //[x64,arm,aarch64,riscv32,riscv64]~| WARN this was previously accepted
+    //[x64,arm,aarch64,riscv32,riscv64]~^ ERROR is not a supported ABI
+    //[x64_win]~^^ WARN unsupported_calling_conventions
+    //[x64_win]~|  WARN this was previously accepted
     f()
 }
 extern "stdcall" {}
@@ -130,7 +98,7 @@ extern "cdecl" fn cdecl() {}
 //[x64,x64_win,arm,aarch64,riscv32,riscv64]~^^ WARN this was previously accepted
 fn cdecl_ptr(f: extern "cdecl" fn()) {
     //[x64,x64_win,arm,aarch64,riscv32,riscv64]~^ WARN unsupported_calling_conventions
-    //[x64,x64_win,arm,aarch64,riscv32,riscv64]~^^ WARN this was previously accepted
+    //[x64,x64_win,arm,aarch64,riscv32,riscv64]~| WARN this was previously accepted
     f()
 }
 extern "cdecl" {}
@@ -143,31 +111,28 @@ extern "cdecl-unwind" {}
 extern "vectorcall" fn vectorcall() {}
 //[arm,aarch64,riscv32,riscv64]~^ ERROR is not a supported ABI
 fn vectorcall_ptr(f: extern "vectorcall" fn()) {
-    //[arm,aarch64,riscv32,riscv64]~^ WARN unsupported_fn_ptr_calling_conventions
-    //[arm,aarch64,riscv32,riscv64]~^^ WARN this was previously accepted
+    //[arm,aarch64,riscv32,riscv64]~^ ERROR is not a supported ABI
     f()
 }
 extern "vectorcall" {}
 //[arm,aarch64,riscv32,riscv64]~^ ERROR is not a supported ABI
 
-fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-    //~^ WARN unsupported_fn_ptr_calling_conventions
-    //~^^ WARN this was previously accepted
+fn cmse_call_ptr(f: extern "cmse-nonsecure-call" fn()) {
+//~^ ERROR is not a supported ABI
     f()
 }
 
-extern "C-cmse-nonsecure-entry" fn cmse_entry() {}
+extern "cmse-nonsecure-entry" fn cmse_entry() {}
+//~^ ERROR is not a supported ABI
+fn cmse_entry_ptr(f: extern "cmse-nonsecure-entry" fn()) {
 //~^ ERROR is not a supported ABI
-fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-    //~^ WARN unsupported_fn_ptr_calling_conventions
-    //~^^ WARN this was previously accepted
     f()
 }
-extern "C-cmse-nonsecure-entry" {}
+extern "cmse-nonsecure-entry" {}
 //~^ ERROR is not a supported ABI
 
 #[cfg(windows)]
 #[link(name = "foo", kind = "raw-dylib")]
 extern "cdecl" {}
-//[x64_win]~^ WARN use of calling convention not supported on this target
+//[x64_win]~^ WARN unsupported_calling_conventions
 //[x64_win]~^^ WARN this was previously accepted
diff --git a/tests/ui/abi/unsupported.x64.stderr b/tests/ui/abi/unsupported.x64.stderr
index 5b55e5707fa..cf04680b587 100644
--- a/tests/ui/abi/unsupported.x64.stderr
+++ b/tests/ui/abi/unsupported.x64.stderr
@@ -1,121 +1,139 @@
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:8
    |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "ptx-kernel" fn ptx() {}
+   |        ^^^^^^^^^^^^
+
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:22
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
+   |                      ^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:43:1
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:42:8
    |
 LL | extern "ptx-kernel" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
-   |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:44:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "gpu-kernel" fn gpu() {}
+   |        ^^^^^^^^^^^^
 
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:55:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:47:8
    |
-LL | extern "aapcs" {}
-   | ^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" fn aapcs() {}
+   |        ^^^^^^^
 
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:49:24
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
+   |                        ^^^^^^^
 
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:65:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:53:8
    |
-LL | extern "msp430-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" {}
+   |        ^^^^^^^
 
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:56:8
    |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "msp430-interrupt" {}
+   |        ^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:75:1
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:59:8
    |
 LL | extern "avr-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^
 
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:62:8
    |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "riscv-interrupt-m" {}
+   |        ^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:85:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:68:8
    |
-LL | extern "riscv-interrupt-m" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "thiscall" fn thiscall() {}
+   |        ^^^^^^^^^^
 
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:70:27
    |
 LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   |                           ^^^^^^^^^^
 
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:105:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:74:8
    |
 LL | extern "thiscall" {}
-   | ^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^
 
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:77:8
+   |
+LL | extern "stdcall" fn stdcall() {}
+   |        ^^^^^^^^^
+   |
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
+
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:81:26
    |
 LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
+   |                          ^^^^^^^^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:119:1
+error[E0570]: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:87:8
    |
 LL | extern "stdcall" {}
-   | ^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^
    |
    = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-error[E0570]: `"stdcall-unwind"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:123:1
+error[E0570]: "stdcall-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:91:8
    |
 LL | extern "stdcall-unwind" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^
    |
    = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"`
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
+error[E0570]: "cmse-nonsecure-call" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:120:28
+   |
+LL | fn cmse_call_ptr(f: extern "cmse-nonsecure-call" fn()) {
+   |                            ^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:125:8
+   |
+LL | extern "cmse-nonsecure-entry" fn cmse_entry() {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:127:29
+   |
+LL | fn cmse_entry_ptr(f: extern "cmse-nonsecure-entry" fn()) {
+   |                             ^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:131:8
+   |
+LL | extern "cmse-nonsecure-entry" {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
+
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:99:17
    |
 LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
    |                 ^^^^^^^^^^^^^^^^^^^
@@ -125,8 +143,8 @@ LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
    = help: use `extern "C"` instead
    = note: `#[warn(unsupported_calling_conventions)]` on by default
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:104:1
    |
 LL | extern "cdecl" {}
    | ^^^^^^^^^^^^^^^^^
@@ -135,8 +153,8 @@ LL | extern "cdecl" {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
+warning: "cdecl-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:107:1
    |
 LL | extern "cdecl-unwind" {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -145,82 +163,8 @@ LL | extern "cdecl-unwind" {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C-unwind"` instead
 
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:166:1
-   |
-LL | extern "C-cmse-nonsecure-entry" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:36:1
-   |
-LL | extern "ptx-kernel" fn ptx() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:45:1
-   |
-LL | extern "gpu-kernel" fn gpu() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:48:1
-   |
-LL | extern "aapcs" fn aapcs() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:58:1
-   |
-LL | extern "msp430-interrupt" fn msp430() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:68:1
-   |
-LL | extern "avr-interrupt" fn avr() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:78:1
-   |
-LL | extern "riscv-interrupt-m" fn riscv() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:98:1
-   |
-LL | extern "thiscall" fn thiscall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"stdcall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:108:1
-   |
-LL | extern "stdcall" fn stdcall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:96:1
    |
 LL | extern "cdecl" fn cdecl() {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -229,111 +173,6 @@ LL | extern "cdecl" fn cdecl() {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
 
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:159:1
-   |
-LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 18 previous errors; 13 warnings emitted
+error: aborting due to 21 previous errors; 4 warnings emitted
 
 For more information about this error, try `rustc --explain E0570`.
-Future incompatibility report: Future breakage diagnostic:
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
-   |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
-   |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
-   |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
-   |
-LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "stdcall" is not supported on this target
-  --> $DIR/unsupported.rs:112:19
-   |
-LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
-   |                   ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
diff --git a/tests/ui/abi/unsupported.x64_win.stderr b/tests/ui/abi/unsupported.x64_win.stderr
index 93b5a272e92..d383a4df732 100644
--- a/tests/ui/abi/unsupported.x64_win.stderr
+++ b/tests/ui/abi/unsupported.x64_win.stderr
@@ -1,96 +1,107 @@
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:36:8
    |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "ptx-kernel" fn ptx() {}
+   |        ^^^^^^^^^^^^
+
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:38:22
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
+LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
+   |                      ^^^^^^^^^^^^
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:43:1
+error[E0570]: "ptx-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:42:8
    |
 LL | extern "ptx-kernel" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^
 
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:44:8
    |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "gpu-kernel" fn gpu() {}
+   |        ^^^^^^^^^^^^
 
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:55:1
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:47:8
    |
-LL | extern "aapcs" {}
-   | ^^^^^^^^^^^^^^^^^
+LL | extern "aapcs" fn aapcs() {}
+   |        ^^^^^^^
 
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:49:24
    |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
+   |                        ^^^^^^^
+
+error[E0570]: "aapcs" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:53:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "aapcs" {}
+   |        ^^^^^^^
 
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:65:1
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:56:8
    |
 LL | extern "msp430-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^
 
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
+error[E0570]: "avr-interrupt" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:59:8
    |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "avr-interrupt" {}
+   |        ^^^^^^^^^^^^^^^
+
+error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:62:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "riscv-interrupt-m" {}
+   |        ^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:75:1
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:68:8
    |
-LL | extern "avr-interrupt" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "thiscall" fn thiscall() {}
+   |        ^^^^^^^^^^
 
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:70:27
    |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
+   |                           ^^^^^^^^^^
+
+error[E0570]: "thiscall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:74:8
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | extern "thiscall" {}
+   |        ^^^^^^^^^^
 
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:85:1
+error[E0570]: "cmse-nonsecure-call" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:120:28
    |
-LL | extern "riscv-interrupt-m" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn cmse_call_ptr(f: extern "cmse-nonsecure-call" fn()) {
+   |                            ^^^^^^^^^^^^^^^^^^^^^
 
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:125:8
    |
-LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" fn cmse_entry() {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:127:29
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+LL | fn cmse_entry_ptr(f: extern "cmse-nonsecure-entry" fn()) {
+   |                             ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:105:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:131:8
    |
-LL | extern "thiscall" {}
-   | ^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:112:19
+warning: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:81:19
    |
 LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
    |                   ^^^^^^^^^^^^^^^^^^^^^
@@ -100,8 +111,8 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
    = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
    = note: `#[warn(unsupported_calling_conventions)]` on by default
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:119:1
+warning: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:87:1
    |
 LL | extern "stdcall" {}
    | ^^^^^^^^^^^^^^^^^^^
@@ -110,8 +121,8 @@ LL | extern "stdcall" {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:123:1
+warning: "stdcall-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:91:1
    |
 LL | extern "stdcall-unwind" {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -120,8 +131,8 @@ LL | extern "stdcall-unwind" {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"`
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:131:17
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:99:17
    |
 LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
    |                 ^^^^^^^^^^^^^^^^^^^
@@ -130,8 +141,8 @@ LL | fn cdecl_ptr(f: extern "cdecl" fn()) {
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:136:1
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:104:1
    |
 LL | extern "cdecl" {}
    | ^^^^^^^^^^^^^^^^^
@@ -140,8 +151,8 @@ LL | extern "cdecl" {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:139:1
+warning: "cdecl-unwind" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:107:1
    |
 LL | extern "cdecl-unwind" {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -150,32 +161,8 @@ LL | extern "cdecl-unwind" {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C-unwind"` instead
 
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:166:1
-   |
-LL | extern "C-cmse-nonsecure-entry" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:171:1
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:136:1
    |
 LL | extern "cdecl" {}
    | ^^^^^^^^^^^^^^^^^
@@ -184,50 +171,8 @@ LL | extern "cdecl" {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
 
-error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:36:1
-   |
-LL | extern "ptx-kernel" fn ptx() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:45:1
-   |
-LL | extern "gpu-kernel" fn gpu() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"aapcs"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:48:1
-   |
-LL | extern "aapcs" fn aapcs() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:58:1
-   |
-LL | extern "msp430-interrupt" fn msp430() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:68:1
-   |
-LL | extern "avr-interrupt" fn avr() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:78:1
-   |
-LL | extern "riscv-interrupt-m" fn riscv() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"thiscall"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:98:1
-   |
-LL | extern "thiscall" fn thiscall() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:108:1
+warning: "stdcall" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:77:1
    |
 LL | extern "stdcall" fn stdcall() {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -236,8 +181,8 @@ LL | extern "stdcall" fn stdcall() {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"`
 
-warning: use of calling convention not supported on this target
-  --> $DIR/unsupported.rs:128:1
+warning: "cdecl" is not a supported ABI for the current target
+  --> $DIR/unsupported.rs:96:1
    |
 LL | extern "cdecl" fn cdecl() {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -246,100 +191,6 @@ LL | extern "cdecl" fn cdecl() {}
    = note: for more information, see issue #137018 <https://github.com/rust-lang/rust/issues/137018>
    = help: use `extern "C"` instead
 
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/unsupported.rs:159:1
-   |
-LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 15 previous errors; 17 warnings emitted
+error: aborting due to 17 previous errors; 9 warnings emitted
 
 For more information about this error, try `rustc --explain E0570`.
-Future incompatibility report: Future breakage diagnostic:
-warning: the calling convention "ptx-kernel" is not supported on this target
-  --> $DIR/unsupported.rs:38:15
-   |
-LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "aapcs" is not supported on this target
-  --> $DIR/unsupported.rs:50:17
-   |
-LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "msp430-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:60:18
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "avr-interrupt" is not supported on this target
-  --> $DIR/unsupported.rs:70:15
-   |
-LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "riscv-interrupt-m" is not supported on this target
-  --> $DIR/unsupported.rs:80:17
-   |
-LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "thiscall" is not supported on this target
-  --> $DIR/unsupported.rs:100:20
-   |
-LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
-   |                    ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/unsupported.rs:153:21
-   |
-LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target
-  --> $DIR/unsupported.rs:161:22
-   |
-LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
diff --git a/tests/ui/allocator/no_std-alloc-error-handler-custom.rs b/tests/ui/allocator/no_std-alloc-error-handler-custom.rs
index 6bbfb72510d..1b0f0608fc6 100644
--- a/tests/ui/allocator/no_std-alloc-error-handler-custom.rs
+++ b/tests/ui/allocator/no_std-alloc-error-handler-custom.rs
@@ -6,13 +6,14 @@
 //@ compile-flags:-C panic=abort
 //@ aux-build:helper.rs
 
-#![feature(rustc_private, lang_items)]
+#![feature(rustc_private, lang_items, panic_unwind)]
 #![feature(alloc_error_handler)]
 #![no_std]
 #![no_main]
 
 extern crate alloc;
 extern crate libc;
+extern crate unwind; // For _Unwind_Resume
 
 // ARM targets need these symbols
 #[no_mangle]
@@ -70,7 +71,15 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
 // in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
 // unwind. So, for this test case we will define the symbol.
 #[lang = "eh_personality"]
-extern "C" fn rust_eh_personality() {}
+extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
 
 #[derive(Default, Debug)]
 struct Page(#[allow(dead_code)] [[u64; 32]; 16]);
diff --git a/tests/ui/allocator/no_std-alloc-error-handler-default.rs b/tests/ui/allocator/no_std-alloc-error-handler-default.rs
index 8bcf054ac85..51ecf1a6731 100644
--- a/tests/ui/allocator/no_std-alloc-error-handler-default.rs
+++ b/tests/ui/allocator/no_std-alloc-error-handler-default.rs
@@ -6,12 +6,13 @@
 //@ compile-flags:-C panic=abort
 //@ aux-build:helper.rs
 
-#![feature(rustc_private, lang_items)]
+#![feature(rustc_private, lang_items, panic_unwind)]
 #![no_std]
 #![no_main]
 
 extern crate alloc;
 extern crate libc;
+extern crate unwind; // For _Unwind_Resume
 
 // ARM targets need these symbols
 #[no_mangle]
@@ -57,7 +58,15 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
 // in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
 // unwind. So, for this test case we will define the symbol.
 #[lang = "eh_personality"]
-extern "C" fn rust_eh_personality() {}
+extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
 
 #[derive(Default, Debug)]
 struct Page(#[allow(dead_code)] [[u64; 32]; 16]);
diff --git a/tests/ui/argument-suggestions/issue-100154.stderr b/tests/ui/argument-suggestions/issue-100154.stderr
index 7eaebcafb59..9732beac449 100644
--- a/tests/ui/argument-suggestions/issue-100154.stderr
+++ b/tests/ui/argument-suggestions/issue-100154.stderr
@@ -17,10 +17,8 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
   --> $DIR/issue-100154.rs:4:11
    |
 LL |     foo::<()>(());
-   |           ^^ `()` cannot be formatted with the default formatter
+   |           ^^ the trait `std::fmt::Display` is not implemented for `()`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `()`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `foo`
   --> $DIR/issue-100154.rs:1:16
    |
diff --git a/tests/ui/argument-suggestions/issue-100478.rs b/tests/ui/argument-suggestions/issue-100478.rs
index b0a9703112e..219870f3b23 100644
--- a/tests/ui/argument-suggestions/issue-100478.rs
+++ b/tests/ui/argument-suggestions/issue-100478.rs
@@ -32,8 +32,8 @@ fn four_shuffle(_a: T1, _b: T2, _c: T3, _d: T4) {}
 
 fn main() {
     three_diff(T2::new(0)); //~ ERROR function takes
-    four_shuffle(T3::default(), T4::default(), T1::default(), T2::default()); //~ ERROR 35:5: 35:17: arguments to this function are incorrect [E0308]
-    four_shuffle(T3::default(), T2::default(), T1::default(), T3::default()); //~ ERROR 36:5: 36:17: arguments to this function are incorrect [E0308]
+    four_shuffle(T3::default(), T4::default(), T1::default(), T2::default()); //~ ERROR arguments to this function are incorrect [E0308]
+    four_shuffle(T3::default(), T2::default(), T1::default(), T3::default()); //~ ERROR arguments to this function are incorrect [E0308]
 
     let p1 = T1::new(0);
     let p2 = Arc::new(T2::new(0));
diff --git a/tests/ui/asm/naked-functions-inline.stderr b/tests/ui/asm/naked-functions-inline.stderr
index 07d5f3bc49a..91140a301ed 100644
--- a/tests/ui/asm/naked-functions-inline.stderr
+++ b/tests/ui/asm/naked-functions-inline.stderr
@@ -1,26 +1,26 @@
 error[E0736]: attribute incompatible with `#[unsafe(naked)]`
-  --> $DIR/naked-functions-inline.rs:12:1
+  --> $DIR/naked-functions-inline.rs:12:3
    |
 LL | #[unsafe(naked)]
    | ---------------- function marked with `#[unsafe(naked)]` here
 LL | #[inline]
-   | ^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
+   |   ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
 
 error[E0736]: attribute incompatible with `#[unsafe(naked)]`
-  --> $DIR/naked-functions-inline.rs:19:1
+  --> $DIR/naked-functions-inline.rs:19:3
    |
 LL | #[unsafe(naked)]
    | ---------------- function marked with `#[unsafe(naked)]` here
 LL | #[inline(always)]
-   | ^^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
+   |   ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
 
 error[E0736]: attribute incompatible with `#[unsafe(naked)]`
-  --> $DIR/naked-functions-inline.rs:26:1
+  --> $DIR/naked-functions-inline.rs:26:3
    |
 LL | #[unsafe(naked)]
    | ---------------- function marked with `#[unsafe(naked)]` here
 LL | #[inline(never)]
-   | ^^^^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
+   |   ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
 
 error[E0736]: attribute incompatible with `#[unsafe(naked)]`
   --> $DIR/naked-functions-inline.rs:33:19
@@ -28,7 +28,7 @@ error[E0736]: attribute incompatible with `#[unsafe(naked)]`
 LL | #[unsafe(naked)]
    | ---------------- function marked with `#[unsafe(naked)]` here
 LL | #[cfg_attr(all(), inline(never))]
-   |                   ^^^^^^^^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
+   |                   ^^^^^^ the `inline` attribute is incompatible with `#[unsafe(naked)]`
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/asm/naked-invalid-attr.stderr b/tests/ui/asm/naked-invalid-attr.stderr
index ef389e7d921..915b54b3fc2 100644
--- a/tests/ui/asm/naked-invalid-attr.stderr
+++ b/tests/ui/asm/naked-invalid-attr.stderr
@@ -4,6 +4,15 @@ error[E0433]: failed to resolve: use of unresolved module or unlinked crate `a`
 LL | #[::a]
    |     ^ use of unresolved module or unlinked crate `a`
 
+error[E0736]: attribute incompatible with `#[unsafe(naked)]`
+  --> $DIR/naked-invalid-attr.rs:56:3
+   |
+LL | #[::a]
+   |   ^^^ the `{{root}}::a` attribute is incompatible with `#[unsafe(naked)]`
+...
+LL | #[unsafe(naked)]
+   | ---------------- function marked with `#[unsafe(naked)]` here
+
 error: attribute should be applied to a function definition
   --> $DIR/naked-invalid-attr.rs:13:1
    |
@@ -33,15 +42,6 @@ LL |     #[unsafe(naked)]
 LL |     || {};
    |     ----- not a function definition
 
-error[E0736]: attribute incompatible with `#[unsafe(naked)]`
-  --> $DIR/naked-invalid-attr.rs:56:1
-   |
-LL | #[::a]
-   | ^^^^^^ the `{{root}}::a` attribute is incompatible with `#[unsafe(naked)]`
-...
-LL | #[unsafe(naked)]
-   | ---------------- function marked with `#[unsafe(naked)]` here
-
 error: attribute should be applied to a function definition
   --> $DIR/naked-invalid-attr.rs:22:5
    |
diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.rs b/tests/ui/asm/naked-with-invalid-repr-attr.rs
index 96eed70dc55..bfbbf29a69e 100644
--- a/tests/ui/asm/naked-with-invalid-repr-attr.rs
+++ b/tests/ui/asm/naked-with-invalid-repr-attr.rs
@@ -19,8 +19,9 @@ extern "C" fn example2() {
     naked_asm!("")
 }
 
-#[repr(align(16), C)]
+#[repr(C)]
 //~^ ERROR attribute should be applied to a struct, enum, or union [E0517]
+#[align(16)]
 #[unsafe(naked)]
 extern "C" fn example3() {
     //~^ NOTE not a struct, enum, or union
diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.stderr b/tests/ui/asm/naked-with-invalid-repr-attr.stderr
index f173a39e5bf..4eb4a4e5a04 100644
--- a/tests/ui/asm/naked-with-invalid-repr-attr.stderr
+++ b/tests/ui/asm/naked-with-invalid-repr-attr.stderr
@@ -23,10 +23,10 @@ LL | | }
    | |_- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/naked-with-invalid-repr-attr.rs:22:19
+  --> $DIR/naked-with-invalid-repr-attr.rs:22:8
    |
-LL |   #[repr(align(16), C)]
-   |                     ^
+LL |   #[repr(C)]
+   |          ^
 ...
 LL | / extern "C" fn example3() {
 LL | |
@@ -35,7 +35,7 @@ LL | | }
    | |_- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct, enum, or union
-  --> $DIR/naked-with-invalid-repr-attr.rs:31:8
+  --> $DIR/naked-with-invalid-repr-attr.rs:32:8
    |
 LL |   #[repr(C, packed)]
    |          ^
@@ -48,7 +48,7 @@ LL | | }
    | |_- not a struct, enum, or union
 
 error[E0517]: attribute should be applied to a struct or union
-  --> $DIR/naked-with-invalid-repr-attr.rs:31:11
+  --> $DIR/naked-with-invalid-repr-attr.rs:32:11
    |
 LL |   #[repr(C, packed)]
    |             ^^^^^^
@@ -61,7 +61,7 @@ LL | | }
    | |_- not a struct or union
 
 error[E0517]: attribute should be applied to an enum
-  --> $DIR/naked-with-invalid-repr-attr.rs:41:8
+  --> $DIR/naked-with-invalid-repr-attr.rs:42:8
    |
 LL |   #[repr(u8)]
    |          ^^
diff --git a/tests/ui/associated-inherent-types/bound_vars_in_args.rs b/tests/ui/associated-inherent-types/bound_vars_in_args.rs
new file mode 100644
index 00000000000..49a9ef31cd6
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bound_vars_in_args.rs
@@ -0,0 +1,22 @@
+#![feature(non_lifetime_binders, inherent_associated_types)]
+#![expect(incomplete_features)]
+
+// Test whether we can resolve to the right IAT when the self type
+// contains a bound type. This should ideally use the second impl.
+
+struct Foo<T: ?Sized>(T);
+
+impl Foo<[u8]> {
+    type IAT = u8;
+}
+
+impl<T: Sized> Foo<T> {
+    type IAT = u8;
+}
+
+struct Bar
+where
+    for<T> Foo<T>::IAT: Sized;
+    //~^ ERROR: multiple applicable items in scope
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/bound_vars_in_args.stderr b/tests/ui/associated-inherent-types/bound_vars_in_args.stderr
new file mode 100644
index 00000000000..9e880476f6a
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bound_vars_in_args.stderr
@@ -0,0 +1,20 @@
+error[E0034]: multiple applicable items in scope
+  --> $DIR/bound_vars_in_args.rs:19:20
+   |
+LL |     for<T> Foo<T>::IAT: Sized;
+   |                    ^^^ multiple `IAT` found
+   |
+note: candidate #1 is defined in an impl for the type `Foo<[u8]>`
+  --> $DIR/bound_vars_in_args.rs:10:5
+   |
+LL |     type IAT = u8;
+   |     ^^^^^^^^
+note: candidate #2 is defined in an impl for the type `Foo<T>`
+  --> $DIR/bound_vars_in_args.rs:14:5
+   |
+LL |     type IAT = u8;
+   |     ^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0034`.
diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr
deleted file mode 100644
index 7f8ed898525..00000000000
--- a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr
+++ /dev/null
@@ -1,33 +0,0 @@
-error[E0391]: cycle detected when computing predicates of `Foo`
-  --> $DIR/cycle-iat-inside-of-adt.rs:7:1
-   |
-LL | struct Foo {
-   | ^^^^^^^^^^
-   |
-note: ...which requires computing inferred outlives-predicates of `Foo`...
-  --> $DIR/cycle-iat-inside-of-adt.rs:7:1
-   |
-LL | struct Foo {
-   | ^^^^^^^^^^
-   = note: ...which requires computing the inferred outlives-predicates for items in this crate...
-note: ...which requires computing type of `Foo::bar`...
-  --> $DIR/cycle-iat-inside-of-adt.rs:8:5
-   |
-LL |     bar: Self::Bar,
-   |     ^^^^^^^^^^^^^^
-note: ...which requires computing normalized predicates of `Foo`...
-  --> $DIR/cycle-iat-inside-of-adt.rs:7:1
-   |
-LL | struct Foo {
-   | ^^^^^^^^^^
-   = note: ...which again requires computing predicates of `Foo`, completing the cycle
-note: cycle used when checking that `Foo` is well-formed
-  --> $DIR/cycle-iat-inside-of-adt.rs:7:1
-   |
-LL | struct Foo {
-   | ^^^^^^^^^^
-   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0391`.
diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs
deleted file mode 100644
index 902094b9862..00000000000
--- a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//@ known-bug: unknown
-
-#![feature(inherent_associated_types)]
-#![allow(incomplete_features)]
-
-// FIXME(inherent_associated_types): This shouldn't lead to a cycle error.
-
-fn user<T>() where S<T>::P: std::fmt::Debug {}
-
-struct S<T>;
-
-impl<T: Copy> S<T> {
-    type P = ();
-}
-
-fn main() {}
diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr
deleted file mode 100644
index e97a5df9d49..00000000000
--- a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr
+++ /dev/null
@@ -1,37 +0,0 @@
-error[E0391]: cycle detected when computing predicates of `user`
-  --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1
-   |
-LL | fn user<T>() where S<T>::P: std::fmt::Debug {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: ...which requires computing explicit predicates of `user`...
-  --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1
-   |
-LL | fn user<T>() where S<T>::P: std::fmt::Debug {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: ...which requires computing normalized predicates of `user`...
-  --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1
-   |
-LL | fn user<T>() where S<T>::P: std::fmt::Debug {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: ...which again requires computing predicates of `user`, completing the cycle
-note: cycle used when checking that `user` is well-formed
-  --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1
-   |
-LL | fn user<T>() where S<T>::P: std::fmt::Debug {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
-
-error[E0392]: type parameter `T` is never used
-  --> $DIR/cycle-iat-inside-of-where-predicate.rs:10:10
-   |
-LL | struct S<T>;
-   |          ^ unused type parameter
-   |
-   = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
-   = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
-
-error: aborting due to 2 previous errors
-
-Some errors have detailed explanations: E0391, E0392.
-For more information about an error, try `rustc --explain E0391`.
diff --git a/tests/ui/associated-inherent-types/candidate-with-alias-2.rs b/tests/ui/associated-inherent-types/candidate-with-alias-2.rs
new file mode 100644
index 00000000000..551d30a8786
--- /dev/null
+++ b/tests/ui/associated-inherent-types/candidate-with-alias-2.rs
@@ -0,0 +1,29 @@
+#![feature(inherent_associated_types)]
+#![expect(incomplete_features)]
+
+// A behaviour test showcasing that we do not normalize associated types in
+// the impl self ty when assembling IAT candidates
+
+trait Identity {
+    type Assoc;
+}
+impl<T> Identity for T {
+    type Assoc = T;
+}
+
+struct Foo<T>(T);
+impl Foo<<u8 as Identity>::Assoc> {
+    type Inherent = u8;
+}
+impl Foo<<u16 as Identity>::Assoc> {
+    type Inherent = u32;
+}
+
+struct Bar {
+    field: <Foo<u8>>::Inherent,
+    //~^ ERROR: multiple applicable items in scope
+}
+
+fn main() {
+    Bar { field: 10_u8 };
+}
diff --git a/tests/ui/associated-inherent-types/candidate-with-alias-2.stderr b/tests/ui/associated-inherent-types/candidate-with-alias-2.stderr
new file mode 100644
index 00000000000..2b79b65f22b
--- /dev/null
+++ b/tests/ui/associated-inherent-types/candidate-with-alias-2.stderr
@@ -0,0 +1,20 @@
+error[E0034]: multiple applicable items in scope
+  --> $DIR/candidate-with-alias-2.rs:23:23
+   |
+LL |     field: <Foo<u8>>::Inherent,
+   |                       ^^^^^^^^ multiple `Inherent` found
+   |
+note: candidate #1 is defined in an impl for the type `Foo<<u8 as Identity>::Assoc>`
+  --> $DIR/candidate-with-alias-2.rs:16:5
+   |
+LL |     type Inherent = u8;
+   |     ^^^^^^^^^^^^^
+note: candidate #2 is defined in an impl for the type `Foo<<u16 as Identity>::Assoc>`
+  --> $DIR/candidate-with-alias-2.rs:19:5
+   |
+LL |     type Inherent = u32;
+   |     ^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0034`.
diff --git a/tests/ui/associated-inherent-types/candidate-with-alias.rs b/tests/ui/associated-inherent-types/candidate-with-alias.rs
new file mode 100644
index 00000000000..a84da195c26
--- /dev/null
+++ b/tests/ui/associated-inherent-types/candidate-with-alias.rs
@@ -0,0 +1,30 @@
+//@ check-pass
+
+#![feature(inherent_associated_types)]
+#![expect(incomplete_features)]
+
+// A behaviour test showcasing that IAT resolution can pick the right
+// candidate even if it has an alias, if it's the only candidate.
+
+trait Identity {
+    type Assoc;
+}
+impl<T> Identity for T {
+    type Assoc = T;
+}
+
+struct Foo<T>(T);
+impl Foo<<u8 as Identity>::Assoc> {
+    type Inherent = u8;
+}
+impl Foo<u16> {
+    type Inherent = u32;
+}
+
+struct Bar {
+    field: <Foo<u8>>::Inherent,
+}
+
+fn main() {
+    Bar { field: 10_u8 };
+}
diff --git a/tests/ui/associated-inherent-types/iat-in-where-bound.rs b/tests/ui/associated-inherent-types/iat-in-where-bound.rs
new file mode 100644
index 00000000000..3b8b95eec9a
--- /dev/null
+++ b/tests/ui/associated-inherent-types/iat-in-where-bound.rs
@@ -0,0 +1,14 @@
+//@ check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+fn user<T: Copy>() where S<T>::P: std::fmt::Debug {}
+
+struct S<T>(T);
+
+impl<T: Copy> S<T> {
+    type P = ();
+}
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs b/tests/ui/associated-inherent-types/iat-inside-of-adt.rs
index 64168cb8c14..3d88016d0f8 100644
--- a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs
+++ b/tests/ui/associated-inherent-types/iat-inside-of-adt.rs
@@ -1,8 +1,7 @@
-//@ known-bug: #108491
+//@ check-pass
 
 #![feature(inherent_associated_types)]
 #![allow(incomplete_features)]
-// FIXME(inherent_associated_types): This should pass.
 
 struct Foo {
     bar: Self::Bar,
@@ -11,4 +10,8 @@ impl Foo {
     pub type Bar = usize;
 }
 
-fn main() {}
+fn main() {
+    Foo {
+        bar: 10_usize,
+    };
+}
diff --git a/tests/ui/associated-inherent-types/impl_params_are_infers.rs b/tests/ui/associated-inherent-types/impl_params_are_infers.rs
new file mode 100644
index 00000000000..55d29a35a23
--- /dev/null
+++ b/tests/ui/associated-inherent-types/impl_params_are_infers.rs
@@ -0,0 +1,34 @@
+#![feature(inherent_associated_types)]
+#![expect(incomplete_features)]
+
+// Test whether IAT resolution in item signatures will actually instantiate the
+// impl's params with infers before equating self types, or if we "cheat" and
+// use a heuristic (e.g. DeepRejectCtxt).
+
+struct Foo<T, U, V>(T, U, V);
+
+impl<T> Foo<T, T, u8> {
+    type IAT = u8;
+}
+
+impl<T, U> Foo<T, U, u16> {
+    type IAT = u16;
+}
+
+trait Identity {
+    type This;
+}
+impl<T> Identity for T {
+    type This = T;
+}
+
+struct Bar<T, U> {
+    // It would be illegal to resolve to `Foo<T, T, u8>::IAT`  as  `T` and `U` are
+    // different types. However, currently we treat all impl-side params sort of like
+    // they're infers and assume they can unify with anything, so we consider it a
+    // valid candidate.
+    field: Foo<T, U, <u16 as Identity>::This>::IAT,
+    //~^ ERROR: multiple applicable items in scope
+}
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/impl_params_are_infers.stderr b/tests/ui/associated-inherent-types/impl_params_are_infers.stderr
new file mode 100644
index 00000000000..fd31693cbed
--- /dev/null
+++ b/tests/ui/associated-inherent-types/impl_params_are_infers.stderr
@@ -0,0 +1,20 @@
+error[E0034]: multiple applicable items in scope
+  --> $DIR/impl_params_are_infers.rs:30:48
+   |
+LL |     field: Foo<T, U, <u16 as Identity>::This>::IAT,
+   |                                                ^^^ multiple `IAT` found
+   |
+note: candidate #1 is defined in an impl for the type `Foo<T, T, u8>`
+  --> $DIR/impl_params_are_infers.rs:11:5
+   |
+LL |     type IAT = u8;
+   |     ^^^^^^^^
+note: candidate #2 is defined in an impl for the type `Foo<T, U, u16>`
+  --> $DIR/impl_params_are_infers.rs:15:5
+   |
+LL |     type IAT = u16;
+   |     ^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0034`.
diff --git a/tests/crashes/125879.rs b/tests/ui/associated-inherent-types/inhabited-predicates.rs
index 4318842e455..2b041d4e1be 100644
--- a/tests/crashes/125879.rs
+++ b/tests/ui/associated-inherent-types/inhabited-predicates.rs
@@ -1,8 +1,10 @@
-//@ known-bug: rust-lang/rust#125879
+//@ check-pass
+
 #![feature(inherent_associated_types)]
-#![allow(incomplete_features)]
+#![expect(incomplete_features)]
 
 pub type PubAlias0 = PubTy::PrivAssocTy;
+//~^ WARN: associated type `PubTy::PrivAssocTy` is more private than the item `PubAlias0`
 
 pub struct PubTy;
 impl PubTy {
@@ -10,6 +12,7 @@ impl PubTy {
 }
 
 pub struct S(pub PubAlias0);
+//~^ WARN: associated type `PubTy::PrivAssocTy` is more private than the item `S::0`
 
 pub unsafe fn foo(a: S) -> S {
     a
diff --git a/tests/ui/associated-inherent-types/inhabited-predicates.stderr b/tests/ui/associated-inherent-types/inhabited-predicates.stderr
new file mode 100644
index 00000000000..e43cd034e67
--- /dev/null
+++ b/tests/ui/associated-inherent-types/inhabited-predicates.stderr
@@ -0,0 +1,27 @@
+warning: associated type `PubTy::PrivAssocTy` is more private than the item `PubAlias0`
+  --> $DIR/inhabited-predicates.rs:6:1
+   |
+LL | pub type PubAlias0 = PubTy::PrivAssocTy;
+   | ^^^^^^^^^^^^^^^^^^ type alias `PubAlias0` is reachable at visibility `pub`
+   |
+note: but associated type `PubTy::PrivAssocTy` is only usable at visibility `pub(crate)`
+  --> $DIR/inhabited-predicates.rs:11:5
+   |
+LL |     type PrivAssocTy = ();
+   |     ^^^^^^^^^^^^^^^^
+   = note: `#[warn(private_interfaces)]` on by default
+
+warning: associated type `PubTy::PrivAssocTy` is more private than the item `S::0`
+  --> $DIR/inhabited-predicates.rs:14:14
+   |
+LL | pub struct S(pub PubAlias0);
+   |              ^^^^^^^^^^^^^ field `S::0` is reachable at visibility `pub`
+   |
+note: but associated type `PubTy::PrivAssocTy` is only usable at visibility `pub(crate)`
+  --> $DIR/inhabited-predicates.rs:11:5
+   |
+LL |     type PrivAssocTy = ();
+   |     ^^^^^^^^^^^^^^^^
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/associated-inherent-types/issue-109299-1.rs b/tests/ui/associated-inherent-types/issue-109299-1.rs
index 4546785f0b1..3132d9fef69 100644
--- a/tests/ui/associated-inherent-types/issue-109299-1.rs
+++ b/tests/ui/associated-inherent-types/issue-109299-1.rs
@@ -8,8 +8,6 @@ impl Lexer<i32> {
 }
 
 type X = impl for<T> Fn() -> Lexer<T>::Cursor;
-//~^ ERROR associated type `Cursor` not found for `Lexer<T>` in the current scope
-//~| ERROR associated type `Cursor` not found for `Lexer<T>` in the current scope
-//~| ERROR: unconstrained opaque type
+//~^ ERROR: unconstrained opaque type
 
 fn main() {}
diff --git a/tests/ui/associated-inherent-types/issue-109299-1.stderr b/tests/ui/associated-inherent-types/issue-109299-1.stderr
index 6bc7a539680..bc8ea6acf28 100644
--- a/tests/ui/associated-inherent-types/issue-109299-1.stderr
+++ b/tests/ui/associated-inherent-types/issue-109299-1.stderr
@@ -6,31 +6,5 @@ LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor;
    |
    = note: `X` must be used in combination with a concrete type within the same crate
 
-error[E0220]: associated type `Cursor` not found for `Lexer<T>` in the current scope
-  --> $DIR/issue-109299-1.rs:10:40
-   |
-LL | struct Lexer<T>(T);
-   | --------------- associated type `Cursor` not found for this struct
-...
-LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor;
-   |                                        ^^^^^^ associated item not found in `Lexer<T>`
-   |
-   = note: the associated type was found for
-           - `Lexer<i32>`
-
-error[E0220]: associated type `Cursor` not found for `Lexer<T>` in the current scope
-  --> $DIR/issue-109299-1.rs:10:40
-   |
-LL | struct Lexer<T>(T);
-   | --------------- associated type `Cursor` not found for this struct
-...
-LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor;
-   |                                        ^^^^^^ associated item not found in `Lexer<T>`
-   |
-   = note: the associated type was found for
-           - `Lexer<i32>`
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: aborting due to 3 previous errors
+error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0220`.
diff --git a/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-1.rs b/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-1.rs
new file mode 100644
index 00000000000..7723ee9c58d
--- /dev/null
+++ b/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-1.rs
@@ -0,0 +1,23 @@
+//@ check-pass
+
+#![feature(inherent_associated_types)]
+#![expect(incomplete_features)]
+
+// Test that when resolving an IAT we select candidates based
+// off whether the self type matches not just the name of the IAT
+
+struct Foo<T>(T);
+impl Foo<u8> {
+    type Inherent = u8;
+}
+impl Foo<u16> {
+    type Inherent = u32;
+}
+
+struct Bar {
+    field: <Foo<u16>>::Inherent,
+}
+
+fn main() {
+    Bar { field: 10_u32 };
+}
diff --git a/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-2.rs b/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-2.rs
new file mode 100644
index 00000000000..8a6d1896f7d
--- /dev/null
+++ b/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-2.rs
@@ -0,0 +1,29 @@
+#![feature(inherent_associated_types)]
+#![expect(incomplete_features)]
+
+// Test that when we have an unnormalized projection in the IAT self ty
+// we don't normalize it to determine which IAT to resolve to.
+
+struct Foo<T>(T);
+impl Foo<u8> {
+    type Inherent = u16;
+}
+impl Foo<u16> {
+    type Inherent = u32;
+}
+
+struct Bar {
+    field: <Foo<<u8 as Identity>::This>>::Inherent,
+    //~^ ERROR: multiple applicable items in scope
+}
+
+trait Identity {
+    type This;
+}
+impl<T> Identity for T { type This = T; }
+
+fn main() {
+    Bar {
+        field: 1_u16,
+    };
+}
diff --git a/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-2.stderr b/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-2.stderr
new file mode 100644
index 00000000000..df8c124f77f
--- /dev/null
+++ b/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-2.stderr
@@ -0,0 +1,20 @@
+error[E0034]: multiple applicable items in scope
+  --> $DIR/multiple-candidates-in-adt-field-2.rs:16:43
+   |
+LL |     field: <Foo<<u8 as Identity>::This>>::Inherent,
+   |                                           ^^^^^^^^ multiple `Inherent` found
+   |
+note: candidate #1 is defined in an impl for the type `Foo<u8>`
+  --> $DIR/multiple-candidates-in-adt-field-2.rs:9:5
+   |
+LL |     type Inherent = u16;
+   |     ^^^^^^^^^^^^^
+note: candidate #2 is defined in an impl for the type `Foo<u16>`
+  --> $DIR/multiple-candidates-in-adt-field-2.rs:12:5
+   |
+LL |     type Inherent = u32;
+   |     ^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0034`.
diff --git a/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-3.rs b/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-3.rs
new file mode 100644
index 00000000000..4c5b382463d
--- /dev/null
+++ b/tests/ui/associated-inherent-types/multiple-candidates-in-adt-field-3.rs
@@ -0,0 +1,27 @@
+//@ check-pass
+
+#![feature(inherent_associated_types, lazy_type_alias)]
+#![expect(incomplete_features)]
+
+// Test that we *do* normalize free aliases in order to resolve
+// between multiple IAT candidates
+
+type Free = u8;
+
+struct Foo<T>(T);
+impl Foo<u8> {
+    type Assoc = u16;
+}
+impl Foo<u16> {
+    type Assoc = u32;
+}
+
+struct Bar {
+    field: <Foo<Free>>::Assoc,
+}
+
+fn main() {
+    Bar {
+        field: 1_u16,
+    };
+}
diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs
index c205cb800d2..337fd8fa00c 100644
--- a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs
+++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs
@@ -27,5 +27,5 @@ impl S<()> {
 fn main() {
     let _: S::<bool>::Pr = ();
     //[shadowed]~^ ERROR associated type `Pr` not found
-    //[uncovered]~^^ ERROR ambiguous associated type
+    //[uncovered]~^^ ERROR associated type `Pr` not found
 }
diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr
index 3e914e0538d..f35158c5b41 100644
--- a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr
+++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr
@@ -1,15 +1,15 @@
-error[E0223]: ambiguous associated type
-  --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:12
+error[E0220]: associated type `Pr` not found for `S<bool>` in the current scope
+  --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:23
    |
+LL | struct S<T>(T);
+   | ----------- associated type `Pr` not found for this struct
+...
 LL |     let _: S::<bool>::Pr = ();
-   |            ^^^^^^^^^^^^^
-   |
-help: use fully-qualified syntax
-   |
-LL -     let _: S::<bool>::Pr = ();
-LL +     let _: <S<bool> as Tr>::Pr = ();
+   |                       ^^ associated item not found in `S<bool>`
    |
+   = note: the associated type was found for
+           
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0223`.
+For more information about this error, try `rustc --explain E0220`.
diff --git a/tests/ui/associated-inherent-types/really_deep_self_ty_mismatch.rs b/tests/ui/associated-inherent-types/really_deep_self_ty_mismatch.rs
new file mode 100644
index 00000000000..eac33f631bb
--- /dev/null
+++ b/tests/ui/associated-inherent-types/really_deep_self_ty_mismatch.rs
@@ -0,0 +1,26 @@
+//@ check-pass
+
+#![feature(inherent_associated_types)]
+#![expect(incomplete_features)]
+
+// Test that IAT resolution doesn't bail out when the self type is
+// very nested.
+
+struct Foo<T>(T);
+#[rustfmt::skip]
+impl Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<u8>>>>>>>>>>> {
+    type Inherent = u16;
+}
+#[rustfmt::skip]
+impl Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<u16>>>>>>>>>>> {
+    type Inherent = u32;
+}
+
+#[rustfmt::skip]
+struct Bar {
+    field: <Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<Foo<u8>>>>>>>>>>>>::Inherent,
+}
+
+fn main() {
+    Bar { field: 1_u16 };
+}
diff --git a/tests/ui/associated-types/defaults-unsound-62211-1.current.stderr b/tests/ui/associated-types/defaults-unsound-62211-1.current.stderr
index 8b6f0a47aed..b17e26b608d 100644
--- a/tests/ui/associated-types/defaults-unsound-62211-1.current.stderr
+++ b/tests/ui/associated-types/defaults-unsound-62211-1.current.stderr
@@ -2,9 +2,8 @@ error[E0277]: `Self` doesn't implement `std::fmt::Display`
   --> $DIR/defaults-unsound-62211-1.rs:24:96
    |
 LL |     type Output: Copy + Deref<Target = str> + AddAssign<&'static str> + From<Self> + Display = Self;
-   |                                                                                                ^^^^ `Self` cannot be formatted with the default formatter
+   |                                                                                                ^^^^ the trait `std::fmt::Display` is not implemented for `Self`
    |
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `UncheckedCopy::Output`
   --> $DIR/defaults-unsound-62211-1.rs:24:86
    |
diff --git a/tests/ui/associated-types/defaults-unsound-62211-1.next.stderr b/tests/ui/associated-types/defaults-unsound-62211-1.next.stderr
index 010f51df15a..a858c9c1ba0 100644
--- a/tests/ui/associated-types/defaults-unsound-62211-1.next.stderr
+++ b/tests/ui/associated-types/defaults-unsound-62211-1.next.stderr
@@ -2,9 +2,8 @@ error[E0277]: `Self` doesn't implement `std::fmt::Display`
   --> $DIR/defaults-unsound-62211-1.rs:24:96
    |
 LL |     type Output: Copy + Deref<Target = str> + AddAssign<&'static str> + From<Self> + Display = Self;
-   |                                                                                                ^^^^ `Self` cannot be formatted with the default formatter
+   |                                                                                                ^^^^ the trait `std::fmt::Display` is not implemented for `Self`
    |
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `UncheckedCopy::Output`
   --> $DIR/defaults-unsound-62211-1.rs:24:86
    |
diff --git a/tests/ui/associated-types/defaults-unsound-62211-2.current.stderr b/tests/ui/associated-types/defaults-unsound-62211-2.current.stderr
index 7552b089133..facfec85afe 100644
--- a/tests/ui/associated-types/defaults-unsound-62211-2.current.stderr
+++ b/tests/ui/associated-types/defaults-unsound-62211-2.current.stderr
@@ -2,9 +2,8 @@ error[E0277]: `Self` doesn't implement `std::fmt::Display`
   --> $DIR/defaults-unsound-62211-2.rs:24:96
    |
 LL |     type Output: Copy + Deref<Target = str> + AddAssign<&'static str> + From<Self> + Display = Self;
-   |                                                                                                ^^^^ `Self` cannot be formatted with the default formatter
+   |                                                                                                ^^^^ the trait `std::fmt::Display` is not implemented for `Self`
    |
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `UncheckedCopy::Output`
   --> $DIR/defaults-unsound-62211-2.rs:24:86
    |
diff --git a/tests/ui/associated-types/defaults-unsound-62211-2.next.stderr b/tests/ui/associated-types/defaults-unsound-62211-2.next.stderr
index 93478946570..1360843172f 100644
--- a/tests/ui/associated-types/defaults-unsound-62211-2.next.stderr
+++ b/tests/ui/associated-types/defaults-unsound-62211-2.next.stderr
@@ -2,9 +2,8 @@ error[E0277]: `Self` doesn't implement `std::fmt::Display`
   --> $DIR/defaults-unsound-62211-2.rs:24:96
    |
 LL |     type Output: Copy + Deref<Target = str> + AddAssign<&'static str> + From<Self> + Display = Self;
-   |                                                                                                ^^^^ `Self` cannot be formatted with the default formatter
+   |                                                                                                ^^^^ the trait `std::fmt::Display` is not implemented for `Self`
    |
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `UncheckedCopy::Output`
   --> $DIR/defaults-unsound-62211-2.rs:24:86
    |
diff --git a/tests/ui/async-await/async-drop/async-without-sync.rs b/tests/ui/async-await/async-drop/async-without-sync.rs
new file mode 100644
index 00000000000..8a748636cc7
--- /dev/null
+++ b/tests/ui/async-await/async-drop/async-without-sync.rs
@@ -0,0 +1,19 @@
+//@ edition: 2024
+#![feature(async_drop)]
+#![allow(incomplete_features)]
+#![crate_type = "lib"]
+
+use std::future::AsyncDrop;
+use std::pin::Pin;
+
+async fn foo() {
+    let _st = St;
+}
+
+struct St;
+
+impl AsyncDrop for St { //~ ERROR: `AsyncDrop` impl without `Drop` impl
+    async fn drop(self: Pin<&mut Self>) {
+        println!("123");
+    }
+}
diff --git a/tests/ui/async-await/async-drop/async-without-sync.stderr b/tests/ui/async-await/async-drop/async-without-sync.stderr
new file mode 100644
index 00000000000..0eaca322dc0
--- /dev/null
+++ b/tests/ui/async-await/async-drop/async-without-sync.stderr
@@ -0,0 +1,10 @@
+error: `AsyncDrop` impl without `Drop` impl
+  --> $DIR/async-without-sync.rs:15:1
+   |
+LL | impl AsyncDrop for St {
+   | ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: type implementing `AsyncDrop` trait must also implement `Drop` trait to be used in sync context and unwinds
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/incorrect-move-async-order-issue-79694.fixed b/tests/ui/async-await/incorrect-move-async-order-issue-79694.fixed
index c74a32e442f..9e5e889506c 100644
--- a/tests/ui/async-await/incorrect-move-async-order-issue-79694.fixed
+++ b/tests/ui/async-await/incorrect-move-async-order-issue-79694.fixed
@@ -4,5 +4,5 @@
 // Regression test for issue 79694
 
 fn main() {
-    let _ = async move { }; //~ ERROR 7:13: 7:23: the order of `move` and `async` is incorrect
+    let _ = async move { }; //~ ERROR the order of `move` and `async` is incorrect
 }
diff --git a/tests/ui/async-await/incorrect-move-async-order-issue-79694.rs b/tests/ui/async-await/incorrect-move-async-order-issue-79694.rs
index 81ffbacc327..9c36a6c96da 100644
--- a/tests/ui/async-await/incorrect-move-async-order-issue-79694.rs
+++ b/tests/ui/async-await/incorrect-move-async-order-issue-79694.rs
@@ -4,5 +4,5 @@
 // Regression test for issue 79694
 
 fn main() {
-    let _ = move async { }; //~ ERROR 7:13: 7:23: the order of `move` and `async` is incorrect
+    let _ = move async { }; //~ ERROR the order of `move` and `async` is incorrect
 }
diff --git a/tests/ui/attributes/arg-error-issue-121425.stderr b/tests/ui/attributes/arg-error-issue-121425.stderr
index 6e71f15fdc8..1beb99b1703 100644
--- a/tests/ui/attributes/arg-error-issue-121425.stderr
+++ b/tests/ui/attributes/arg-error-issue-121425.stderr
@@ -1,9 +1,3 @@
-error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
-  --> $DIR/arg-error-issue-121425.rs:16:8
-   |
-LL | #[repr(align())]
-   |        ^^^^^^^
-
 error[E0693]: incorrect `repr(align)` attribute format: `align` expects a literal integer as argument
   --> $DIR/arg-error-issue-121425.rs:4:14
    |
@@ -22,6 +16,12 @@ error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
 LL | #[repr(align("str"))]
    |              ^^^^^
 
+error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
+  --> $DIR/arg-error-issue-121425.rs:16:8
+   |
+LL | #[repr(align())]
+   |        ^^^^^^^
+
 error[E0552]: incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument
   --> $DIR/arg-error-issue-121425.rs:21:15
    |
diff --git a/tests/ui/attributes/expected-word.rs b/tests/ui/attributes/expected-word.rs
new file mode 100644
index 00000000000..246aa78db82
--- /dev/null
+++ b/tests/ui/attributes/expected-word.rs
@@ -0,0 +1,3 @@
+#[cold = true]
+//~^ ERROR malformed `cold` attribute input [E0565]
+fn main() {}
diff --git a/tests/ui/attributes/expected-word.stderr b/tests/ui/attributes/expected-word.stderr
new file mode 100644
index 00000000000..dcb10e7aee8
--- /dev/null
+++ b/tests/ui/attributes/expected-word.stderr
@@ -0,0 +1,12 @@
+error[E0565]: malformed `cold` attribute input
+  --> $DIR/expected-word.rs:1:1
+   |
+LL | #[cold = true]
+   | ^^^^^^^------^
+   | |      |
+   | |      didn't expect any arguments here
+   | help: must be of the form: `#[cold]`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0565`.
diff --git a/tests/ui/attributes/invalid-repr.rs b/tests/ui/attributes/invalid-repr.rs
index 10a487c127e..d7933533405 100644
--- a/tests/ui/attributes/invalid-repr.rs
+++ b/tests/ui/attributes/invalid-repr.rs
@@ -1,5 +1,5 @@
 #[repr(align(16))]
-//~^ ERROR attribute should be applied to a struct, enum, function, associated function, or union
+//~^ ERROR attribute should be applied to a struct, enum, or union
 pub type Foo = i32;
 
 fn main() {}
diff --git a/tests/ui/attributes/invalid-repr.stderr b/tests/ui/attributes/invalid-repr.stderr
index 681460ad081..3f5a305c6b7 100644
--- a/tests/ui/attributes/invalid-repr.stderr
+++ b/tests/ui/attributes/invalid-repr.stderr
@@ -1,11 +1,11 @@
-error[E0517]: attribute should be applied to a struct, enum, function, associated function, or union
+error[E0517]: attribute should be applied to a struct, enum, or union
   --> $DIR/invalid-repr.rs:1:8
    |
 LL | #[repr(align(16))]
    |        ^^^^^^^^^
 LL |
 LL | pub type Foo = i32;
-   | ------------------- not a struct, enum, function, associated function, or union
+   | ------------------- not a struct, enum, or union
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/attributes/lint_on_root.rs b/tests/ui/attributes/lint_on_root.rs
new file mode 100644
index 00000000000..93d47bf0d71
--- /dev/null
+++ b/tests/ui/attributes/lint_on_root.rs
@@ -0,0 +1,7 @@
+// NOTE: this used to panic in debug builds (by a sanity assertion)
+// and not emit any lint on release builds. See https://github.com/rust-lang/rust/issues/142891.
+#![inline = ""]
+//~^ ERROR valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
+fn main() {}
diff --git a/tests/ui/attributes/lint_on_root.stderr b/tests/ui/attributes/lint_on_root.stderr
new file mode 100644
index 00000000000..aaa46e6f54b
--- /dev/null
+++ b/tests/ui/attributes/lint_on_root.stderr
@@ -0,0 +1,12 @@
+error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+  --> $DIR/lint_on_root.rs:3:1
+   |
+LL | #![inline = ""]
+   | ^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: `#[deny(ill_formed_attribute_input)]` on by default
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/attributes/malformed-fn-align.rs b/tests/ui/attributes/malformed-fn-align.rs
index 4aaad01b723..f5ab9555e56 100644
--- a/tests/ui/attributes/malformed-fn-align.rs
+++ b/tests/ui/attributes/malformed-fn-align.rs
@@ -2,6 +2,24 @@
 #![crate_type = "lib"]
 
 trait MyTrait {
-    #[repr(align)] //~ ERROR invalid `repr(align)` attribute: `align` needs an argument
-    fn myfun();
+    #[align] //~ ERROR malformed `align` attribute input
+    fn myfun1();
+
+    #[align(1, 2)] //~ ERROR malformed `align` attribute input
+    fn myfun2();
 }
+
+#[align = 16] //~ ERROR malformed `align` attribute input
+fn f1() {}
+
+#[align("hello")] //~ ERROR invalid alignment value: not an unsuffixed integer
+fn f2() {}
+
+#[align(0)] //~ ERROR invalid alignment value: not a power of two
+fn f3() {}
+
+#[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on function items
+fn f4() {}
+
+#[align(16)] //~ ERROR `#[align(...)]` is not supported on struct items
+struct S1;
diff --git a/tests/ui/attributes/malformed-fn-align.stderr b/tests/ui/attributes/malformed-fn-align.stderr
index 57913c48ef7..b769d0b457d 100644
--- a/tests/ui/attributes/malformed-fn-align.stderr
+++ b/tests/ui/attributes/malformed-fn-align.stderr
@@ -1,9 +1,67 @@
-error[E0589]: invalid `repr(align)` attribute: `align` needs an argument
-  --> $DIR/malformed-fn-align.rs:5:12
+error[E0539]: malformed `align` attribute input
+  --> $DIR/malformed-fn-align.rs:5:5
    |
-LL |     #[repr(align)]
-   |            ^^^^^ help: supply an argument here: `align(...)`
+LL |     #[align]
+   |     ^^^^^^^^
+   |     |
+   |     expected this to be a list
+   |     help: must be of the form: `#[align(<alignment in bytes>)]`
 
-error: aborting due to 1 previous error
+error[E0805]: malformed `align` attribute input
+  --> $DIR/malformed-fn-align.rs:8:5
+   |
+LL |     #[align(1, 2)]
+   |     ^^^^^^^------^
+   |     |      |
+   |     |      expected a single argument here
+   |     help: must be of the form: `#[align(<alignment in bytes>)]`
+
+error[E0539]: malformed `align` attribute input
+  --> $DIR/malformed-fn-align.rs:12:1
+   |
+LL | #[align = 16]
+   | ^^^^^^^^^^^^^
+   | |
+   | expected this to be a list
+   | help: must be of the form: `#[align(<alignment in bytes>)]`
+
+error[E0589]: invalid alignment value: not an unsuffixed integer
+  --> $DIR/malformed-fn-align.rs:15:9
+   |
+LL | #[align("hello")]
+   |         ^^^^^^^
+
+error[E0589]: invalid alignment value: not a power of two
+  --> $DIR/malformed-fn-align.rs:18:9
+   |
+LL | #[align(0)]
+   |         ^
+
+error: `#[repr(align(...))]` is not supported on function items
+  --> $DIR/malformed-fn-align.rs:21:8
+   |
+LL | #[repr(align(16))]
+   |        ^^^^^^^^^
+   |
+help: use `#[align(...)]` instead
+  --> $DIR/malformed-fn-align.rs:21:8
+   |
+LL | #[repr(align(16))]
+   |        ^^^^^^^^^
+
+error: `#[align(...)]` is not supported on struct items
+  --> $DIR/malformed-fn-align.rs:24:1
+   |
+LL | #[align(16)]
+   | ^^^^^^^^^^^^
+   |
+help: use `#[repr(align(...))]` instead
+   |
+LL - #[align(16)]
+LL + #[repr(align(16))]
+   |
+
+error: aborting due to 7 previous errors
 
-For more information about this error, try `rustc --explain E0589`.
+Some errors have detailed explanations: E0539, E0589, E0805.
+For more information about an error, try `rustc --explain E0539`.
diff --git a/tests/ui/attributes/malformed-must_use.rs b/tests/ui/attributes/malformed-must_use.rs
new file mode 100644
index 00000000000..4b98affa8ab
--- /dev/null
+++ b/tests/ui/attributes/malformed-must_use.rs
@@ -0,0 +1,4 @@
+#[must_use()] //~ ERROR valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]`
+struct Test;
+
+fn main() {}
diff --git a/tests/ui/attributes/malformed-must_use.stderr b/tests/ui/attributes/malformed-must_use.stderr
new file mode 100644
index 00000000000..c948ba67744
--- /dev/null
+++ b/tests/ui/attributes/malformed-must_use.stderr
@@ -0,0 +1,8 @@
+error: valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]`
+  --> $DIR/malformed-must_use.rs:1:1
+   |
+LL | #[must_use()]
+   | ^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/attributes/rustc_skip_during_method_dispatch.rs b/tests/ui/attributes/rustc_skip_during_method_dispatch.rs
new file mode 100644
index 00000000000..25b473d5a58
--- /dev/null
+++ b/tests/ui/attributes/rustc_skip_during_method_dispatch.rs
@@ -0,0 +1,38 @@
+#![feature(rustc_attrs)]
+
+#[rustc_skip_during_method_dispatch]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input [E0539]
+trait NotAList {}
+
+#[rustc_skip_during_method_dispatch = "array"]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input [E0539]
+trait AlsoNotAList {}
+
+#[rustc_skip_during_method_dispatch()]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait Argless {}
+
+#[rustc_skip_during_method_dispatch(array, boxed_slice, array)]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait Duplicate {}
+
+#[rustc_skip_during_method_dispatch(slice)]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait Unexpected {}
+
+#[rustc_skip_during_method_dispatch(array = true)]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait KeyValue {}
+
+#[rustc_skip_during_method_dispatch("array")]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait String {}
+
+#[rustc_skip_during_method_dispatch(array, boxed_slice)]
+trait OK {}
+
+#[rustc_skip_during_method_dispatch(array)]
+//~^ ERROR: attribute should be applied to a trait
+impl OK for () {}
+
+fn main() {}
diff --git a/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr b/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr
new file mode 100644
index 00000000000..2f5d7968489
--- /dev/null
+++ b/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr
@@ -0,0 +1,76 @@
+error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:3:1
+   |
+LL | #[rustc_skip_during_method_dispatch]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | expected this to be a list
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:7:1
+   |
+LL | #[rustc_skip_during_method_dispatch = "array"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | expected this to be a list
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:11:1
+   |
+LL | #[rustc_skip_during_method_dispatch()]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--^
+   | |                                  |
+   | |                                  expected at least 1 argument here
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0538]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:15:1
+   |
+LL | #[rustc_skip_during_method_dispatch(array, boxed_slice, array)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----^^
+   | |                                                       |
+   | |                                                       found `array` used as a key more than once
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:19:1
+   |
+LL | #[rustc_skip_during_method_dispatch(slice)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----^^
+   | |                                   |
+   | |                                   valid arguments are `array` or `boxed_slice`
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0565]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:23:1
+   |
+LL | #[rustc_skip_during_method_dispatch(array = true)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^^
+   | |                                         |
+   | |                                         didn't expect any arguments here
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0565]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:27:1
+   |
+LL | #[rustc_skip_during_method_dispatch("array")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^
+   | |                                   |
+   | |                                   didn't expect a literal here
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error: attribute should be applied to a trait
+  --> $DIR/rustc_skip_during_method_dispatch.rs:34:1
+   |
+LL | #[rustc_skip_during_method_dispatch(array)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | impl OK for () {}
+   | ----------------- not a trait
+
+error: aborting due to 8 previous errors
+
+Some errors have detailed explanations: E0538, E0539, E0565.
+For more information about an error, try `rustc --explain E0538`.
diff --git a/tests/ui/binop/issue-77910-1.stderr b/tests/ui/binop/issue-77910-1.stderr
index 74deac900d4..80c384f39bd 100644
--- a/tests/ui/binop/issue-77910-1.stderr
+++ b/tests/ui/binop/issue-77910-1.stderr
@@ -16,9 +16,8 @@ LL | fn foo(s: &i32) -> &i32 {
    |    --- consider calling this function
 ...
 LL |     assert_eq!(foo, y);
-   |     ^^^^^^^^^^^^^^^^^^ `for<'a> fn(&'a i32) -> &'a i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `for<'a> fn(&'a i32) -> &'a i32 {foo}`
    |
-   = help: the trait `Debug` is not implemented for fn item `for<'a> fn(&'a i32) -> &'a i32 {foo}`
    = help: use parentheses to call this function: `foo(/* &i32 */)`
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr
index 911c136086c..72580e7464b 100644
--- a/tests/ui/borrowck/clone-on-ref.stderr
+++ b/tests/ui/borrowck/clone-on-ref.stderr
@@ -30,7 +30,7 @@ LL |     drop(x);
    |          ^ move out of `x` occurs here
 LL |
 LL |     println!("{b}");
-   |               --- borrow later used here
+   |                - borrow later used here
    |
 help: if `T` implemented `Clone`, you could clone the value
   --> $DIR/clone-on-ref.rs:11:8
@@ -57,7 +57,7 @@ LL |     drop(x);
    |          ^ move out of `x` occurs here
 LL |
 LL |     println!("{b:?}");
-   |               ----- borrow later used here
+   |                - borrow later used here
    |
 note: if `A` implemented `Clone`, you could clone the value
   --> $DIR/clone-on-ref.rs:19:1
diff --git a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs
deleted file mode 100644
index 4a6c2f9ed06..00000000000
--- a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#![allow(dead_code)]
-
-fn bar<'a>(_: std::fmt::Arguments<'a>) {}
-fn main() {
-    let x = format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3);
-    //~^ ERROR temporary value dropped while borrowed
-
-    bar(x);
-
-    let foo = format_args!("{}", "hi");
-    //~^ ERROR temporary value dropped while borrowed
-    bar(foo);
-
-    let foo = format_args!("hi"); // no placeholder in arguments, so no error
-    bar(foo);
-}
diff --git a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr b/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr
deleted file mode 100644
index 30f292f71a2..00000000000
--- a/tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr
+++ /dev/null
@@ -1,31 +0,0 @@
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/issue-114374-invalid-help-fmt-args.rs:5:13
-   |
-LL |     let x = format_args!("a {} {} {}.", 1, format_args!("b{}!", 2), 3);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
-   |             |
-   |             creates a temporary value which is freed while still in use
-...
-LL |     bar(x);
-   |         - borrow later used here
-   |
-   = note: the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used
-   = note: to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>
-
-error[E0716]: temporary value dropped while borrowed
-  --> $DIR/issue-114374-invalid-help-fmt-args.rs:10:15
-   |
-LL |     let foo = format_args!("{}", "hi");
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
-   |               |
-   |               creates a temporary value which is freed while still in use
-LL |
-LL |     bar(foo);
-   |         --- borrow later used here
-   |
-   = note: the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used
-   = note: to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr
index f29a41d6a8e..f422919983b 100644
--- a/tests/ui/check-cfg/target_feature.stderr
+++ b/tests/ui/check-cfg/target_feature.stderr
@@ -211,10 +211,6 @@ LL |     cfg!(target_feature = "_UNEXPECTED_VALUE");
 `reference-types`
 `relax`
 `relaxed-simd`
-`reserve-x18`
-`retpoline-external-thunk`
-`retpoline-indirect-branches`
-`retpoline-indirect-calls`
 `rtm`
 `sb`
 `scq`
diff --git a/tests/ui/closures/issue-111932.stderr b/tests/ui/closures/issue-111932.stderr
index 93488ad2011..fc3b7b0c6e6 100644
--- a/tests/ui/closures/issue-111932.stderr
+++ b/tests/ui/closures/issue-111932.stderr
@@ -14,11 +14,9 @@ error[E0277]: the size for values of type `dyn Foo` cannot be known at compilati
 LL |         println!("{:?}", foo);
    |                   ----   ^^^ doesn't have a size known at compile-time
    |                   |
-   |                   required by a bound introduced by this call
+   |                   required by this formatting parameter
    |
    = help: the trait `Sized` is not implemented for `dyn Foo`
-note: required by an implicit `Sized` bound in `core::fmt::rt::Argument::<'_>::new_debug`
-  --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/callback-as-argument.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/callback-as-argument.rs
index b25a81b858b..796c2634b62 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/callback-as-argument.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/callback-as-argument.rs
@@ -2,15 +2,15 @@
 //@ build-pass
 //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
 //@ needs-llvm-components: arm
-#![feature(abi_c_cmse_nonsecure_call, cmse_nonsecure_entry, no_core, lang_items, intrinsics)]
+#![feature(abi_cmse_nonsecure_call, cmse_nonsecure_entry, no_core, lang_items, intrinsics)]
 #![no_core]
 
 extern crate minicore;
 use minicore::*;
 
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn test(
-    f: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u32,
+pub extern "cmse-nonsecure-entry" fn test(
+    f: extern "cmse-nonsecure-call" fn(u32, u32, u32, u32) -> u32,
     a: u32,
     b: u32,
     c: u32,
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs
index 2d0ed5d2a30..cb805309a02 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.rs
@@ -1,9 +1,9 @@
-// gate-test-abi_c_cmse_nonsecure_call
-#[allow(unsupported_fn_ptr_calling_conventions)]
+// gate-test-abi_cmse_nonsecure_call
 fn main() {
     let non_secure_function = unsafe {
-        core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
-            //~^ ERROR [E0658]
+        core::mem::transmute::<usize, extern "cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
+            //~^ ERROR: is not a supported ABI for the current target [E0570]
+            //~| ERROR: ABI is experimental and subject to change [E0658]
             0x10000004,
         )
     };
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr
index beb0ab70cc7..ecf70e890f4 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr
@@ -1,23 +1,20 @@
-error[E0658]: the extern "C-cmse-nonsecure-call" ABI is experimental and subject to change
-  --> $DIR/gate_test.rs:5:46
+error[E0570]: "cmse-nonsecure-call" is not a supported ABI for the current target
+  --> $DIR/gate_test.rs:4:46
    |
-LL |         core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
-   |                                              ^^^^^^^^^^^^^^^^^^^^^^^
+LL |         core::mem::transmute::<usize, extern "cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
+   |                                              ^^^^^^^^^^^^^^^^^^^^^
+
+error[E0658]: the extern "cmse-nonsecure-call" ABI is experimental and subject to change
+  --> $DIR/gate_test.rs:4:46
+   |
+LL |         core::mem::transmute::<usize, extern "cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
+   |                                              ^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #81391 <https://github.com/rust-lang/rust/issues/81391> for more information
-   = help: add `#![feature(abi_c_cmse_nonsecure_call)]` to the crate attributes to enable
+   = help: add `#![feature(abi_cmse_nonsecure_call)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0658`.
-Future incompatibility report: Future breakage diagnostic:
-warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target
-  --> $DIR/gate_test.rs:5:39
-   |
-LL |         core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn(i32, i32, i32, i32) -> i32>(
-   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
+error: aborting due to 2 previous errors
 
+Some errors have detailed explanations: E0570, E0658.
+For more information about an error, try `rustc --explain E0570`.
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
index 84080890e08..4ce5890a2da 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs
@@ -1,7 +1,7 @@
 //@ add-core-stubs
 //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
 //@ needs-llvm-components: arm
-#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)]
+#![feature(abi_cmse_nonsecure_call, no_core, lang_items)]
 #![no_core]
 
 extern crate minicore;
@@ -11,31 +11,31 @@ use minicore::*;
 struct Wrapper<T>(T);
 
 struct Test<T: Copy> {
-    f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
+    f1: extern "cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
     //~^ ERROR cannot find type `U` in this scope
     //~| ERROR function pointer types may not have generic parameters
-    f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64,
+    f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64,
     //~^ ERROR `impl Trait` is not allowed in `fn` pointer parameters
-    f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798]
-    f4: extern "C-cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64, //~ ERROR [E0798]
+    f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, //~ ERROR [E0798]
+    f4: extern "cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64, //~ ERROR [E0798]
 }
 
-type WithReference = extern "C-cmse-nonsecure-call" fn(&usize);
+type WithReference = extern "cmse-nonsecure-call" fn(&usize);
 
 trait Trait {}
-type WithTraitObject = extern "C-cmse-nonsecure-call" fn(&dyn Trait) -> &dyn Trait;
-//~^ ERROR return value of `"C-cmse-nonsecure-call"` function too large to pass via registers [E0798]
+type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn Trait;
+//~^ ERROR return value of `"cmse-nonsecure-call"` function too large to pass via registers [E0798]
 
 type WithStaticTraitObject =
-    extern "C-cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Trait;
-//~^ ERROR return value of `"C-cmse-nonsecure-call"` function too large to pass via registers [E0798]
+    extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Trait;
+//~^ ERROR return value of `"cmse-nonsecure-call"` function too large to pass via registers [E0798]
 
 #[repr(transparent)]
 struct WrapperTransparent<'a>(&'a dyn Trait);
 
 type WithTransparentTraitObject =
-    extern "C-cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTransparent;
-//~^ ERROR return value of `"C-cmse-nonsecure-call"` function too large to pass via registers [E0798]
+    extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTransparent;
+//~^ ERROR return value of `"cmse-nonsecure-call"` function too large to pass via registers [E0798]
 
-type WithVarArgs = extern "C-cmse-nonsecure-call" fn(u32, ...);
-//~^ ERROR C-variadic functions with the "C-cmse-nonsecure-call" calling convention are not supported
+type WithVarArgs = extern "cmse-nonsecure-call" fn(u32, ...);
+//~^ ERROR C-variadic functions with the "cmse-nonsecure-call" calling convention are not supported
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
index 2b51f48915b..15656853576 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr
@@ -1,21 +1,21 @@
 error: function pointer types may not have generic parameters
-  --> $DIR/generics.rs:14:42
+  --> $DIR/generics.rs:14:40
    |
-LL |     f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
-   |                                          ^^^^^^^^^
+LL |     f1: extern "cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
+   |                                        ^^^^^^^^^
 
 error[E0412]: cannot find type `U` in this scope
-  --> $DIR/generics.rs:14:52
+  --> $DIR/generics.rs:14:50
    |
 LL | struct Test<T: Copy> {
    |             - similarly named type parameter `T` defined here
-LL |     f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
-   |                                                    ^
+LL |     f1: extern "cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
+   |                                                  ^
    |
 help: a type parameter with a similar name exists
    |
-LL -     f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
-LL +     f1: extern "C-cmse-nonsecure-call" fn<U: Copy>(T, u32, u32, u32) -> u64,
+LL -     f1: extern "cmse-nonsecure-call" fn<U: Copy>(U, u32, u32, u32) -> u64,
+LL +     f1: extern "cmse-nonsecure-call" fn<U: Copy>(T, u32, u32, u32) -> u64,
    |
 help: you might be missing a type parameter
    |
@@ -23,57 +23,57 @@ LL | struct Test<T: Copy, U> {
    |                    +++
 
 error[E0562]: `impl Trait` is not allowed in `fn` pointer parameters
-  --> $DIR/generics.rs:17:43
+  --> $DIR/generics.rs:17:41
    |
-LL |     f2: extern "C-cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64,
-   |                                           ^^^^^^^^^
+LL |     f2: extern "cmse-nonsecure-call" fn(impl Copy, u32, u32, u32) -> u64,
+   |                                         ^^^^^^^^^
    |
    = note: `impl Trait` is only allowed in arguments and return types of functions and methods
 
-error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
+error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type
   --> $DIR/generics.rs:19:9
    |
-LL |     f3: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64,
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     f3: extern "cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0798]: function pointers with the `"C-cmse-nonsecure-call"` ABI cannot contain generics in their type
+error[E0798]: function pointers with the `"cmse-nonsecure-call"` ABI cannot contain generics in their type
   --> $DIR/generics.rs:20:9
    |
-LL |     f4: extern "C-cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64,
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     f4: extern "cmse-nonsecure-call" fn(Wrapper<T>, u32, u32, u32) -> u64,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/generics.rs:26:73
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/generics.rs:26:71
    |
-LL | type WithTraitObject = extern "C-cmse-nonsecure-call" fn(&dyn Trait) -> &dyn Trait;
-   |                                                                         ^^^^^^^^^^ this type doesn't fit in the available registers
+LL | type WithTraitObject = extern "cmse-nonsecure-call" fn(&dyn Trait) -> &dyn Trait;
+   |                                                                       ^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/generics.rs:30:62
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/generics.rs:30:60
    |
-LL |     extern "C-cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Trait;
-   |                                                              ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
+LL |     extern "cmse-nonsecure-call" fn(&'static dyn Trait) -> &'static dyn Trait;
+   |                                                            ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/generics.rs:37:62
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/generics.rs:37:60
    |
-LL |     extern "C-cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTransparent;
-   |                                                              ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
+LL |     extern "cmse-nonsecure-call" fn(WrapperTransparent) -> WrapperTransparent;
+   |                                                            ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0045]: C-variadic functions with the "C-cmse-nonsecure-call" calling convention are not supported
+error[E0045]: C-variadic functions with the "cmse-nonsecure-call" calling convention are not supported
   --> $DIR/generics.rs:40:20
    |
-LL | type WithVarArgs = extern "C-cmse-nonsecure-call" fn(u32, ...);
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
+LL | type WithVarArgs = extern "cmse-nonsecure-call" fn(u32, ...);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
 
 error: aborting due to 9 previous errors
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs
index 8328f9b6dd5..7036cd367e4 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.rs
@@ -1,7 +1,7 @@
 //@ add-core-stubs
 //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
 //@ needs-llvm-components: arm
-#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)]
+#![feature(abi_cmse_nonsecure_call, no_core, lang_items)]
 #![no_core]
 
 extern crate minicore;
@@ -13,10 +13,10 @@ pub struct AlignRelevant(u32);
 
 #[no_mangle]
 pub fn test(
-    f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), //~ ERROR [E0798]
-    f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16),            //~ ERROR [E0798]
-    f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32),                      //~ ERROR [E0798]
-    f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32),                 //~ ERROR [E0798]
-    f5: extern "C-cmse-nonsecure-call" fn([u32; 5]),                           //~ ERROR [E0798]
+    f1: extern "cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32), //~ ERROR [E0798]
+    f2: extern "cmse-nonsecure-call" fn(u32, u32, u32, u16, u16),            //~ ERROR [E0798]
+    f3: extern "cmse-nonsecure-call" fn(u32, u64, u32),                      //~ ERROR [E0798]
+    f4: extern "cmse-nonsecure-call" fn(AlignRelevant, u32),                 //~ ERROR [E0798]
+    f5: extern "cmse-nonsecure-call" fn([u32; 5]),                           //~ ERROR [E0798]
 ) {
 }
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr
index 10a5e856107..5d59405fbd1 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/params-via-stack.stderr
@@ -1,42 +1,42 @@
-error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:16:63
+error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:16:61
    |
-LL |     f1: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32),
-   |                                                               ^^^^^^^^^^^^^^ these arguments don't fit in the available registers
+LL |     f1: extern "cmse-nonsecure-call" fn(u32, u32, u32, u32, x: u32, y: u32),
+   |                                                             ^^^^^^^^^^^^^^ these arguments don't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:17:63
+error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:17:61
    |
-LL |     f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u16, u16),
-   |                                                               ^^^ this argument doesn't fit in the available registers
+LL |     f2: extern "cmse-nonsecure-call" fn(u32, u32, u32, u16, u16),
+   |                                                             ^^^ this argument doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:18:53
+error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:18:51
    |
-LL |     f3: extern "C-cmse-nonsecure-call" fn(u32, u64, u32),
-   |                                                     ^^^ this argument doesn't fit in the available registers
+LL |     f3: extern "cmse-nonsecure-call" fn(u32, u64, u32),
+   |                                                   ^^^ this argument doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:19:58
+error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:19:56
    |
-LL |     f4: extern "C-cmse-nonsecure-call" fn(AlignRelevant, u32),
-   |                                                          ^^^ this argument doesn't fit in the available registers
+LL |     f4: extern "cmse-nonsecure-call" fn(AlignRelevant, u32),
+   |                                                        ^^^ this argument doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-error[E0798]: arguments for `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:20:43
+error[E0798]: arguments for `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:20:41
    |
-LL |     f5: extern "C-cmse-nonsecure-call" fn([u32; 5]),
-   |                                           ^^^^^^^^ this argument doesn't fit in the available registers
+LL |     f5: extern "cmse-nonsecure-call" fn([u32; 5]),
+   |                                         ^^^^^^^^ this argument doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs
index 890ec4b00f6..77347b04ede 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs
@@ -3,7 +3,7 @@
 //@ needs-llvm-components: arm
 //@ add-core-stubs
 
-#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)]
+#![feature(abi_cmse_nonsecure_call, no_core, lang_items)]
 #![no_core]
 
 extern crate minicore;
@@ -23,18 +23,18 @@ pub struct ReprCAlign16(u16);
 
 #[no_mangle]
 pub fn test(
-    f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64, //~ ERROR [E0798]
-    f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes, //~ ERROR [E0798]
-    f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound, //~ ERROR [E0798]
-    f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16, //~ ERROR [E0798]
-    f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5],  //~ ERROR [E0798]
+    f1: extern "cmse-nonsecure-call" fn() -> ReprCU64, //~ ERROR [E0798]
+    f2: extern "cmse-nonsecure-call" fn() -> ReprCBytes, //~ ERROR [E0798]
+    f3: extern "cmse-nonsecure-call" fn() -> U64Compound, //~ ERROR [E0798]
+    f4: extern "cmse-nonsecure-call" fn() -> ReprCAlign16, //~ ERROR [E0798]
+    f5: extern "cmse-nonsecure-call" fn() -> [u8; 5],  //~ ERROR [E0798]
 ) {
 }
 
 #[allow(improper_ctypes_definitions)]
 struct Test {
-    u128: extern "C-cmse-nonsecure-call" fn() -> u128, //~ ERROR [E0798]
-    i128: extern "C-cmse-nonsecure-call" fn() -> i128, //~ ERROR [E0798]
+    u128: extern "cmse-nonsecure-call" fn() -> u128, //~ ERROR [E0798]
+    i128: extern "cmse-nonsecure-call" fn() -> i128, //~ ERROR [E0798]
 }
 
 #[repr(C)]
@@ -49,7 +49,7 @@ pub union ReprRustUnionU64 {
 
 #[no_mangle]
 pub fn test_union(
-    f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ ERROR [E0798]
-    f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64,    //~ ERROR [E0798]
+    f1: extern "cmse-nonsecure-call" fn() -> ReprRustUnionU64, //~ ERROR [E0798]
+    f2: extern "cmse-nonsecure-call" fn() -> ReprCUnionU64,    //~ ERROR [E0798]
 ) {
 }
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr
index d2077352900..ddf969c1bce 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.stderr
@@ -1,82 +1,82 @@
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:36:50
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:36:48
    |
-LL |     u128: extern "C-cmse-nonsecure-call" fn() -> u128,
-   |                                                  ^^^^ this type doesn't fit in the available registers
+LL |     u128: extern "cmse-nonsecure-call" fn() -> u128,
+   |                                                ^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:37:50
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:37:48
    |
-LL |     i128: extern "C-cmse-nonsecure-call" fn() -> i128,
-   |                                                  ^^^^ this type doesn't fit in the available registers
+LL |     i128: extern "cmse-nonsecure-call" fn() -> i128,
+   |                                                ^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:26:48
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:26:46
    |
-LL |     f1: extern "C-cmse-nonsecure-call" fn() -> ReprCU64,
-   |                                                ^^^^^^^^ this type doesn't fit in the available registers
+LL |     f1: extern "cmse-nonsecure-call" fn() -> ReprCU64,
+   |                                              ^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:27:48
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:27:46
    |
-LL |     f2: extern "C-cmse-nonsecure-call" fn() -> ReprCBytes,
-   |                                                ^^^^^^^^^^ this type doesn't fit in the available registers
+LL |     f2: extern "cmse-nonsecure-call" fn() -> ReprCBytes,
+   |                                              ^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:28:48
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:28:46
    |
-LL |     f3: extern "C-cmse-nonsecure-call" fn() -> U64Compound,
-   |                                                ^^^^^^^^^^^ this type doesn't fit in the available registers
+LL |     f3: extern "cmse-nonsecure-call" fn() -> U64Compound,
+   |                                              ^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:29:48
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:29:46
    |
-LL |     f4: extern "C-cmse-nonsecure-call" fn() -> ReprCAlign16,
-   |                                                ^^^^^^^^^^^^ this type doesn't fit in the available registers
+LL |     f4: extern "cmse-nonsecure-call" fn() -> ReprCAlign16,
+   |                                              ^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:30:48
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:30:46
    |
-LL |     f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 5],
-   |                                                ^^^^^^^ this type doesn't fit in the available registers
+LL |     f5: extern "cmse-nonsecure-call" fn() -> [u8; 5],
+   |                                              ^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:52:48
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:52:46
    |
-LL |     f1: extern "C-cmse-nonsecure-call" fn() -> ReprRustUnionU64,
-   |                                                ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
+LL |     f1: extern "cmse-nonsecure-call" fn() -> ReprRustUnionU64,
+   |                                              ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-call"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:53:48
+error[E0798]: return value of `"cmse-nonsecure-call"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:53:46
    |
-LL |     f2: extern "C-cmse-nonsecure-call" fn() -> ReprCUnionU64,
-   |                                                ^^^^^^^^^^^^^ this type doesn't fit in the available registers
+LL |     f2: extern "cmse-nonsecure-call" fn() -> ReprCUnionU64,
+   |                                              ^^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-call"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-call"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
 error: aborting due to 9 previous errors
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs
index 7dfe6cf9672..419d26875bc 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs
@@ -2,7 +2,7 @@
 //@ build-pass
 //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
 //@ needs-llvm-components: arm
-#![feature(abi_c_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
+#![feature(abi_cmse_nonsecure_call, no_core, lang_items, intrinsics)]
 #![no_core]
 
 extern crate minicore;
@@ -27,26 +27,26 @@ pub struct U32Compound(u16, u16);
 #[no_mangle]
 #[allow(improper_ctypes_definitions)]
 pub fn params(
-    f1: extern "C-cmse-nonsecure-call" fn(),
-    f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32),
-    f3: extern "C-cmse-nonsecure-call" fn(u64, u64),
-    f4: extern "C-cmse-nonsecure-call" fn(u128),
-    f5: extern "C-cmse-nonsecure-call" fn(f64, f32, f32),
-    f6: extern "C-cmse-nonsecure-call" fn(ReprTransparentStruct<u64>, U32Compound),
-    f7: extern "C-cmse-nonsecure-call" fn([u32; 4]),
+    f1: extern "cmse-nonsecure-call" fn(),
+    f2: extern "cmse-nonsecure-call" fn(u32, u32, u32, u32),
+    f3: extern "cmse-nonsecure-call" fn(u64, u64),
+    f4: extern "cmse-nonsecure-call" fn(u128),
+    f5: extern "cmse-nonsecure-call" fn(f64, f32, f32),
+    f6: extern "cmse-nonsecure-call" fn(ReprTransparentStruct<u64>, U32Compound),
+    f7: extern "cmse-nonsecure-call" fn([u32; 4]),
 ) {
 }
 
 #[no_mangle]
 pub fn returns(
-    f1: extern "C-cmse-nonsecure-call" fn() -> u32,
-    f2: extern "C-cmse-nonsecure-call" fn() -> u64,
-    f3: extern "C-cmse-nonsecure-call" fn() -> i64,
-    f4: extern "C-cmse-nonsecure-call" fn() -> f64,
-    f5: extern "C-cmse-nonsecure-call" fn() -> [u8; 4],
-    f6: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct<u64>,
-    f7: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentStruct<ReprTransparentStruct<u64>>,
-    f8: extern "C-cmse-nonsecure-call" fn() -> ReprTransparentEnumU64,
-    f9: extern "C-cmse-nonsecure-call" fn() -> U32Compound,
+    f1: extern "cmse-nonsecure-call" fn() -> u32,
+    f2: extern "cmse-nonsecure-call" fn() -> u64,
+    f3: extern "cmse-nonsecure-call" fn() -> i64,
+    f4: extern "cmse-nonsecure-call" fn() -> f64,
+    f5: extern "cmse-nonsecure-call" fn() -> [u8; 4],
+    f6: extern "cmse-nonsecure-call" fn() -> ReprTransparentStruct<u64>,
+    f7: extern "cmse-nonsecure-call" fn() -> ReprTransparentStruct<ReprTransparentStruct<u64>>,
+    f8: extern "cmse-nonsecure-call" fn() -> ReprTransparentEnumU64,
+    f9: extern "cmse-nonsecure-call" fn() -> U32Compound,
 ) {
 }
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.rs
index 5a2d2db19c5..44a1e7d69a8 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.rs
@@ -1,10 +1,10 @@
 //@ add-core-stubs
 //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
 //@ needs-llvm-components: arm
-#![feature(abi_c_cmse_nonsecure_call, lang_items, no_core)]
+#![feature(abi_cmse_nonsecure_call, lang_items, no_core)]
 #![no_core]
 
 extern crate minicore;
 use minicore::*;
 
-pub extern "C-cmse-nonsecure-call" fn test() {} //~ ERROR [E0781]
+pub extern "cmse-nonsecure-call" fn test() {} //~ ERROR [E0781]
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.stderr
index f49fab043a4..b9cccecc64b 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-1.stderr
@@ -1,8 +1,8 @@
-error[E0781]: the `"C-cmse-nonsecure-call"` ABI is only allowed on function pointers
+error[E0781]: the `"cmse-nonsecure-call"` ABI is only allowed on function pointers
   --> $DIR/wrong-abi-location-1.rs:10:1
    |
-LL | pub extern "C-cmse-nonsecure-call" fn test() {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | pub extern "cmse-nonsecure-call" fn test() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.rs
index e93b153949a..f23f45f786f 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.rs
@@ -1,12 +1,12 @@
 //@ add-core-stubs
 //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib
 //@ needs-llvm-components: arm
-#![feature(abi_c_cmse_nonsecure_call, lang_items, no_core)]
+#![feature(abi_cmse_nonsecure_call, lang_items, no_core)]
 #![no_core]
 
 extern crate minicore;
 use minicore::*;
 
-extern "C-cmse-nonsecure-call" { //~ ERROR [E0781]
+extern "cmse-nonsecure-call" { //~ ERROR [E0781]
     fn test();
 }
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.stderr
index bae8d20d81c..437d7b80b1f 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/wrong-abi-location-2.stderr
@@ -1,7 +1,7 @@
-error[E0781]: the `"C-cmse-nonsecure-call"` ABI is only allowed on function pointers
+error[E0781]: the `"cmse-nonsecure-call"` ABI is only allowed on function pointers
   --> $DIR/wrong-abi-location-2.rs:10:1
    |
-LL | / extern "C-cmse-nonsecure-call" {
+LL | / extern "cmse-nonsecure-call" {
 LL | |     fn test();
 LL | | }
    | |_^
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.rs
index 6061451b2e9..8ec22033a3d 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.rs
@@ -1,9 +1,9 @@
 // gate-test-cmse_nonsecure_entry
 
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
-    //~^ ERROR [E0570]
-    //~| ERROR [E0658]
+pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+    //~^ ERROR: is not a supported ABI for the current target [E0570]
+    //~| ERROR: ABI is experimental and subject to change [E0658]
     input + 6
 }
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.stderr
index 0afbbe647af..e40862e74ee 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.stderr
@@ -1,19 +1,19 @@
-error[E0658]: the extern "C-cmse-nonsecure-entry" ABI is experimental and subject to change
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
   --> $DIR/gate_test.rs:4:12
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+   |            ^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0658]: the extern "cmse-nonsecure-entry" ABI is experimental and subject to change
+  --> $DIR/gate_test.rs:4:12
+   |
+LL | pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+   |            ^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #75835 <https://github.com/rust-lang/rust/issues/75835> for more information
    = help: add `#![feature(cmse_nonsecure_entry)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/gate_test.rs:4:1
-   |
-LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: aborting due to 2 previous errors
 
 Some errors have detailed explanations: E0570, E0658.
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs
index 19b6179dde7..800dd580af2 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs
@@ -11,12 +11,12 @@ use minicore::*;
 struct Wrapper<T>(T);
 
 impl<T: Copy> Wrapper<T> {
-    extern "C-cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 {
+    extern "cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 {
         //~^ ERROR [E0798]
         0
     }
 
-    extern "C-cmse-nonsecure-entry" fn ambient_generic_nested(
+    extern "cmse-nonsecure-entry" fn ambient_generic_nested(
         //~^ ERROR [E0798]
         _: Wrapper<T>,
         _: u32,
@@ -27,7 +27,7 @@ impl<T: Copy> Wrapper<T> {
     }
 }
 
-extern "C-cmse-nonsecure-entry" fn introduced_generic<U: Copy>(
+extern "cmse-nonsecure-entry" fn introduced_generic<U: Copy>(
     //~^ ERROR [E0798]
     _: U,
     _: u32,
@@ -37,40 +37,40 @@ extern "C-cmse-nonsecure-entry" fn introduced_generic<U: Copy>(
     0
 }
 
-extern "C-cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 {
+extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 {
     //~^ ERROR [E0798]
     0
 }
 
-extern "C-cmse-nonsecure-entry" fn reference(x: &usize) -> usize {
+extern "cmse-nonsecure-entry" fn reference(x: &usize) -> usize {
     *x
 }
 
 trait Trait {}
 
-extern "C-cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait {
-    //~^ ERROR return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers [E0798]
+extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait {
+    //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798]
     x
 }
 
-extern "C-cmse-nonsecure-entry" fn static_trait_object(
+extern "cmse-nonsecure-entry" fn static_trait_object(
     x: &'static dyn Trait,
 ) -> &'static dyn Trait {
-    //~^ ERROR return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers [E0798]
+    //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798]
     x
 }
 
 #[repr(transparent)]
 struct WrapperTransparent<'a>(&'a dyn Trait);
 
-extern "C-cmse-nonsecure-entry" fn wrapped_trait_object(
+extern "cmse-nonsecure-entry" fn wrapped_trait_object(
     x: WrapperTransparent,
 ) -> WrapperTransparent {
-    //~^ ERROR return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers [E0798]
+    //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798]
     x
 }
 
-extern "C-cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
+extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
     //~^ ERROR only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
     //~| ERROR requires `va_list` lang_item
 }
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr
index c314671dc29..f0190671b5a 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr
@@ -1,13 +1,13 @@
 error: only foreign, `unsafe extern "C"`, or `unsafe extern "C-unwind"` functions may have a C-variadic arg
-  --> $DIR/generics.rs:73:55
+  --> $DIR/generics.rs:73:53
    |
-LL | extern "C-cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
-   |                                                       ^^^^^^
+LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
+   |                                                     ^^^^^^
 
-error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type
   --> $DIR/generics.rs:30:1
    |
-LL | / extern "C-cmse-nonsecure-entry" fn introduced_generic<U: Copy>(
+LL | / extern "cmse-nonsecure-entry" fn introduced_generic<U: Copy>(
 LL | |
 LL | |     _: U,
 LL | |     _: u32,
@@ -16,22 +16,22 @@ LL | |     _: u32,
 LL | | ) -> u64 {
    | |________^
 
-error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type
   --> $DIR/generics.rs:40:1
    |
-LL | extern "C-cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | extern "cmse-nonsecure-entry" fn impl_trait(_: impl Copy, _: u32, _: u32, _: u32) -> u64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type
   --> $DIR/generics.rs:14:5
    |
-LL |     extern "C-cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     extern "cmse-nonsecure-entry" fn ambient_generic(_: T, _: u32, _: u32, _: u32) -> u64 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0798]: functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type
+error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type
   --> $DIR/generics.rs:19:5
    |
-LL | /     extern "C-cmse-nonsecure-entry" fn ambient_generic_nested(
+LL | /     extern "cmse-nonsecure-entry" fn ambient_generic_nested(
 LL | |
 LL | |         _: Wrapper<T>,
 LL | |         _: u32,
@@ -40,38 +40,38 @@ LL | |         _: u32,
 LL | |     ) -> u64 {
    | |____________^
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/generics.rs:51:67
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/generics.rs:51:65
    |
-LL | extern "C-cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait {
-   |                                                                   ^^^^^^^^^^ this type doesn't fit in the available registers
+LL | extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait {
+   |                                                                 ^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
   --> $DIR/generics.rs:58:6
    |
 LL | ) -> &'static dyn Trait {
    |      ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
   --> $DIR/generics.rs:68:6
    |
 LL | ) -> WrapperTransparent {
    |      ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
 error: requires `va_list` lang_item
-  --> $DIR/generics.rs:73:55
+  --> $DIR/generics.rs:73:53
    |
-LL | extern "C-cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
-   |                                                       ^^^^^^
+LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) {
+   |                                                     ^^^^^^
 
 error: aborting due to 9 previous errors
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs
index 4c53f9422da..d4f722fa193 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs
@@ -12,14 +12,14 @@ use minicore::*;
 pub struct AlignRelevant(u32);
 
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {} //~ ERROR [E0798]
+pub extern "cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {} //~ ERROR [E0798]
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn f2(_: u32, _: u32, _: u32, _: u16, _: u16) {} //~ ERROR [E0798]
+pub extern "cmse-nonsecure-entry" fn f2(_: u32, _: u32, _: u32, _: u16, _: u16) {} //~ ERROR [E0798]
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {} //~ ERROR [E0798]
+pub extern "cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {} //~ ERROR [E0798]
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {} //~ ERROR [E0798]
+pub extern "cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {} //~ ERROR [E0798]
 
 #[no_mangle]
 #[allow(improper_ctypes_definitions)]
-pub extern "C-cmse-nonsecure-entry" fn f5(_: [u32; 5]) {} //~ ERROR [E0798]
+pub extern "cmse-nonsecure-entry" fn f5(_: [u32; 5]) {} //~ ERROR [E0798]
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr
index 24e9ddf32fe..f8b96bddc94 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.stderr
@@ -1,42 +1,42 @@
-error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:15:78
+error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:15:76
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {}
-   |                                                                              ^^^^^^^^^^^ these arguments don't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f1(_: u32, _: u32, _: u32, _: u32, _: u32, _: u32) {}
+   |                                                                            ^^^^^^^^^^^ these arguments don't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:17:78
+error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:17:76
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f2(_: u32, _: u32, _: u32, _: u16, _: u16) {}
-   |                                                                              ^^^ this argument doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f2(_: u32, _: u32, _: u32, _: u16, _: u16) {}
+   |                                                                            ^^^ this argument doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:19:62
+error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:19:60
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {}
-   |                                                              ^^^ this argument doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {}
+   |                                                            ^^^ this argument doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:21:64
+error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:21:62
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {}
-   |                                                                ^^^ this argument doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {}
+   |                                                              ^^^ this argument doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
-error[E0798]: arguments for `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/params-via-stack.rs:25:46
+error[E0798]: arguments for `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/params-via-stack.rs:25:44
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f5(_: [u32; 5]) {}
-   |                                              ^^^^^^^^ this argument doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f5(_: [u32; 5]) {}
+   |                                            ^^^^^^^^ this argument doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass all their arguments via the 4 32-bit available argument registers
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs
index 735eab10fa1..0052a0977ed 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs
@@ -22,41 +22,41 @@ pub struct U64Compound(u32, u32);
 pub struct ReprCAlign16(u16);
 
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 {
+pub extern "cmse-nonsecure-entry" fn f1() -> ReprCU64 {
     //~^ ERROR [E0798]
     ReprCU64(0)
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes {
+pub extern "cmse-nonsecure-entry" fn f2() -> ReprCBytes {
     //~^ ERROR [E0798]
     ReprCBytes(0, 1, 2, 3, 4)
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound {
+pub extern "cmse-nonsecure-entry" fn f3() -> U64Compound {
     //~^ ERROR [E0798]
     U64Compound(2, 3)
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 {
+pub extern "cmse-nonsecure-entry" fn f4() -> ReprCAlign16 {
     //~^ ERROR [E0798]
     ReprCAlign16(4)
 }
 
 #[no_mangle]
 #[allow(improper_ctypes_definitions)]
-pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] {
+pub extern "cmse-nonsecure-entry" fn f5() -> [u8; 5] {
     //~^ ERROR [E0798]
     [0xAA; 5]
 }
 #[no_mangle]
 #[allow(improper_ctypes_definitions)]
-pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 {
+pub extern "cmse-nonsecure-entry" fn u128() -> u128 {
     //~^ ERROR [E0798]
     123
 }
 #[no_mangle]
 #[allow(improper_ctypes_definitions)]
-pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 {
+pub extern "cmse-nonsecure-entry" fn i128() -> i128 {
     //~^ ERROR [E0798]
     456
 }
@@ -73,12 +73,12 @@ pub union ReprCUnionU64 {
 
 #[no_mangle]
 #[allow(improper_ctypes_definitions)]
-pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 {
+pub extern "cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 {
     //~^ ERROR [E0798]
     ReprRustUnionU64 { _unused: 1 }
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 {
+pub extern "cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 {
     //~^ ERROR [E0798]
     ReprCUnionU64 { _unused: 2 }
 }
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr
index 9c885d95318..c5effed92ae 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr
@@ -1,82 +1,82 @@
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:25:48
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:25:46
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 {
-   |                                                ^^^^^^^^ this type doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f1() -> ReprCU64 {
+   |                                              ^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:30:48
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:30:46
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes {
-   |                                                ^^^^^^^^^^ this type doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f2() -> ReprCBytes {
+   |                                              ^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:35:48
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:35:46
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound {
-   |                                                ^^^^^^^^^^^ this type doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f3() -> U64Compound {
+   |                                              ^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:40:48
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:40:46
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 {
-   |                                                ^^^^^^^^^^^^ this type doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f4() -> ReprCAlign16 {
+   |                                              ^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:47:48
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:47:46
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] {
-   |                                                ^^^^^^^ this type doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn f5() -> [u8; 5] {
+   |                                              ^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:53:50
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:53:48
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 {
-   |                                                  ^^^^ this type doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn u128() -> u128 {
+   |                                                ^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:59:50
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:59:48
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 {
-   |                                                  ^^^^ this type doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn i128() -> i128 {
+   |                                                ^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:76:56
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:76:54
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 {
-   |                                                        ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 {
+   |                                                      ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
-error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers
-  --> $DIR/return-via-stack.rs:81:53
+error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers
+  --> $DIR/return-via-stack.rs:81:51
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 {
-   |                                                     ^^^^^^^^^^^^^ this type doesn't fit in the available registers
+LL | pub extern "cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 {
+   |                                                   ^^^^^^^^^^^^^ this type doesn't fit in the available registers
    |
-   = note: functions with the `"C-cmse-nonsecure-entry"` ABI must pass their result via the available return registers
+   = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers
    = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
 error: aborting due to 9 previous errors
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.aarch64.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.aarch64.stderr
index 6a90dc8d635..3949eac1542 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.aarch64.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.aarch64.stderr
@@ -1,8 +1,8 @@
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/trustzone-only.rs:17:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/trustzone-only.rs:17:12
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+   |            ^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.rs
index 6d84dab2166..ff5d2ec0ab6 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.rs
@@ -14,7 +14,7 @@ extern crate minicore;
 use minicore::*;
 
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
     //~^ ERROR [E0570]
     input
 }
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.thumb7.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.thumb7.stderr
index 6a90dc8d635..3949eac1542 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.thumb7.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.thumb7.stderr
@@ -1,8 +1,8 @@
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/trustzone-only.rs:17:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/trustzone-only.rs:17:12
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+   |            ^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.x86.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.x86.stderr
index 6a90dc8d635..3949eac1542 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.x86.stderr
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.x86.stderr
@@ -1,8 +1,8 @@
-error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target
-  --> $DIR/trustzone-only.rs:17:1
+error[E0570]: "cmse-nonsecure-entry" is not a supported ABI for the current target
+  --> $DIR/trustzone-only.rs:17:12
    |
-LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+   |            ^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs
index 912fc8b85eb..34373288125 100644
--- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs
+++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs
@@ -26,49 +26,49 @@ pub enum ReprTransparentEnumU64 {
 pub struct U32Compound(u16, u16);
 
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn inputs1() {}
+pub extern "cmse-nonsecure-entry" fn inputs1() {}
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn inputs2(_: u32, _: u32, _: u32, _: u32) {}
+pub extern "cmse-nonsecure-entry" fn inputs2(_: u32, _: u32, _: u32, _: u32) {}
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn inputs3(_: u64, _: u64) {}
+pub extern "cmse-nonsecure-entry" fn inputs3(_: u64, _: u64) {}
 #[no_mangle]
 #[allow(improper_ctypes_definitions)]
-pub extern "C-cmse-nonsecure-entry" fn inputs4(_: u128) {}
+pub extern "cmse-nonsecure-entry" fn inputs4(_: u128) {}
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn inputs5(_: f64, _: f32, _: f32) {}
+pub extern "cmse-nonsecure-entry" fn inputs5(_: f64, _: f32, _: f32) {}
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn inputs6(_: ReprTransparentStruct<u64>, _: U32Compound) {}
+pub extern "cmse-nonsecure-entry" fn inputs6(_: ReprTransparentStruct<u64>, _: U32Compound) {}
 #[no_mangle]
 #[allow(improper_ctypes_definitions)]
-pub extern "C-cmse-nonsecure-entry" fn inputs7(_: [u32; 4]) {}
+pub extern "cmse-nonsecure-entry" fn inputs7(_: [u32; 4]) {}
 
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn outputs1() -> u32 {
+pub extern "cmse-nonsecure-entry" fn outputs1() -> u32 {
     0
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn outputs2() -> u64 {
+pub extern "cmse-nonsecure-entry" fn outputs2() -> u64 {
     0
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn outputs3() -> i64 {
+pub extern "cmse-nonsecure-entry" fn outputs3() -> i64 {
     0
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn outputs4() -> f64 {
+pub extern "cmse-nonsecure-entry" fn outputs4() -> f64 {
     0.0
 }
 #[no_mangle]
 #[allow(improper_ctypes_definitions)]
-pub extern "C-cmse-nonsecure-entry" fn outputs5() -> [u8; 4] {
+pub extern "cmse-nonsecure-entry" fn outputs5() -> [u8; 4] {
     [0xAA; 4]
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn outputs6() -> ReprTransparentStruct<u64> {
+pub extern "cmse-nonsecure-entry" fn outputs6() -> ReprTransparentStruct<u64> {
     ReprTransparentStruct { _marker1: (), _marker2: (), field: 0xAA, _marker3: () }
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn outputs7(
+pub extern "cmse-nonsecure-entry" fn outputs7(
 ) -> ReprTransparentStruct<ReprTransparentStruct<u64>> {
     ReprTransparentStruct {
         _marker1: (),
@@ -78,10 +78,10 @@ pub extern "C-cmse-nonsecure-entry" fn outputs7(
     }
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn outputs8() -> ReprTransparentEnumU64 {
+pub extern "cmse-nonsecure-entry" fn outputs8() -> ReprTransparentEnumU64 {
     ReprTransparentEnumU64::A(0)
 }
 #[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn outputs9() -> U32Compound {
+pub extern "cmse-nonsecure-entry" fn outputs9() -> U32Compound {
     U32Compound(1, 2)
 }
diff --git a/tests/ui/coercion/invalid-blanket-coerce-unsized-impl.rs b/tests/ui/coercion/invalid-blanket-coerce-unsized-impl.rs
new file mode 100644
index 00000000000..a4fd7710718
--- /dev/null
+++ b/tests/ui/coercion/invalid-blanket-coerce-unsized-impl.rs
@@ -0,0 +1,13 @@
+// Regression test minimized from #126982.
+// We used to apply a coerce_unsized coercion to literally every argument since
+// the blanket applied in literally all cases, even though it was incoherent.
+
+#![feature(coerce_unsized)]
+
+impl<A> std::ops::CoerceUnsized<A> for A {}
+//~^ ERROR type parameter `A` must be used as the type parameter for some local type
+//~| ERROR the trait `CoerceUnsized` may only be implemented for a coercion between structures
+
+const C: usize = 1;
+
+fn main() {}
diff --git a/tests/ui/coercion/invalid-blanket-coerce-unsized-impl.stderr b/tests/ui/coercion/invalid-blanket-coerce-unsized-impl.stderr
new file mode 100644
index 00000000000..377906ee334
--- /dev/null
+++ b/tests/ui/coercion/invalid-blanket-coerce-unsized-impl.stderr
@@ -0,0 +1,19 @@
+error[E0210]: type parameter `A` must be used as the type parameter for some local type (e.g., `MyStruct<A>`)
+  --> $DIR/invalid-blanket-coerce-unsized-impl.rs:7:6
+   |
+LL | impl<A> std::ops::CoerceUnsized<A> for A {}
+   |      ^ type parameter `A` must be used as the type parameter for some local type
+   |
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
+   = note: only traits defined in the current crate can be implemented for a type parameter
+
+error[E0377]: the trait `CoerceUnsized` may only be implemented for a coercion between structures
+  --> $DIR/invalid-blanket-coerce-unsized-impl.rs:7:1
+   |
+LL | impl<A> std::ops::CoerceUnsized<A> for A {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0210, E0377.
+For more information about an error, try `rustc --explain E0210`.
diff --git a/tests/ui/compiletest-self-test/line-annotation-mismatches.rs b/tests/ui/compiletest-self-test/line-annotation-mismatches.rs
new file mode 100644
index 00000000000..d2a14374ed4
--- /dev/null
+++ b/tests/ui/compiletest-self-test/line-annotation-mismatches.rs
@@ -0,0 +1,42 @@
+//@ should-fail
+
+// The warning is reported with unknown line
+//@ compile-flags: -D raw_pointer_derive
+//~? WARN kind and unknown line match the reported warning, but we do not suggest it
+
+// The error is expected but not reported at all.
+//~ ERROR this error does not exist
+
+// The error is reported but not expected at all.
+// "`main` function not found in crate" (the main function is intentionally not added)
+
+// An "unimportant" diagnostic is expected on a wrong line.
+//~ ERROR aborting due to
+
+// An "unimportant" diagnostic is expected with a wrong kind.
+//~? ERROR For more information about an error
+
+fn wrong_line_or_kind() {
+    // A diagnostic expected on a wrong line.
+    unresolved1;
+    //~ ERROR cannot find value `unresolved1` in this scope
+
+    // A diagnostic expected with a wrong kind.
+    unresolved2; //~ WARN cannot find value `unresolved2` in this scope
+
+    // A diagnostic expected with a missing kind (treated as a wrong kind).
+    unresolved3; //~ cannot find value `unresolved3` in this scope
+
+    // A diagnostic expected with a wrong line and kind.
+    unresolved4;
+    //~ WARN cannot find value `unresolved4` in this scope
+}
+
+fn wrong_message() {
+    // A diagnostic expected with a wrong message, but the line is known and right.
+    unresolvedA; //~ ERROR stub message 1
+
+    // A diagnostic expected with a wrong message, but the line is known and right,
+    // even if the kind doesn't match.
+    unresolvedB; //~ WARN stub message 2
+}
diff --git a/tests/ui/compiletest-self-test/line-annotation-mismatches.stderr b/tests/ui/compiletest-self-test/line-annotation-mismatches.stderr
new file mode 100644
index 00000000000..7ca3bfaf396
--- /dev/null
+++ b/tests/ui/compiletest-self-test/line-annotation-mismatches.stderr
@@ -0,0 +1,61 @@
+warning: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
+   |
+   = note: requested on the command line with `-D raw_pointer_derive`
+   = note: `#[warn(renamed_and_removed_lints)]` on by default
+
+error[E0425]: cannot find value `unresolved1` in this scope
+  --> $DIR/line-annotation-mismatches.rs:21:5
+   |
+LL |     unresolved1;
+   |     ^^^^^^^^^^^ not found in this scope
+
+error[E0425]: cannot find value `unresolved2` in this scope
+  --> $DIR/line-annotation-mismatches.rs:25:5
+   |
+LL |     unresolved2;
+   |     ^^^^^^^^^^^ not found in this scope
+
+error[E0425]: cannot find value `unresolved3` in this scope
+  --> $DIR/line-annotation-mismatches.rs:28:5
+   |
+LL |     unresolved3;
+   |     ^^^^^^^^^^^ not found in this scope
+
+error[E0425]: cannot find value `unresolved4` in this scope
+  --> $DIR/line-annotation-mismatches.rs:31:5
+   |
+LL |     unresolved4;
+   |     ^^^^^^^^^^^ not found in this scope
+
+error[E0425]: cannot find value `unresolvedA` in this scope
+  --> $DIR/line-annotation-mismatches.rs:37:5
+   |
+LL |     unresolvedA;
+   |     ^^^^^^^^^^^ not found in this scope
+
+error[E0425]: cannot find value `unresolvedB` in this scope
+  --> $DIR/line-annotation-mismatches.rs:41:5
+   |
+LL |     unresolvedB;
+   |     ^^^^^^^^^^^ not found in this scope
+
+warning: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
+   |
+   = note: requested on the command line with `-D raw_pointer_derive`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0601]: `main` function not found in crate `line_annotation_mismatches`
+  --> $DIR/line-annotation-mismatches.rs:42:2
+   |
+LL | }
+   |  ^ consider adding a `main` function to `$DIR/line-annotation-mismatches.rs`
+
+warning: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
+   |
+   = note: requested on the command line with `-D raw_pointer_derive`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: aborting due to 7 previous errors; 3 warnings emitted
+
+Some errors have detailed explanations: E0425, E0601.
+For more information about an error, try `rustc --explain E0425`.
diff --git a/tests/ui/const-generics/issues/issue-86535-2.rs b/tests/ui/const-generics/issues/issue-86535-2.rs
index 8d064f3eeb1..5c9132fe54d 100644
--- a/tests/ui/const-generics/issues/issue-86535-2.rs
+++ b/tests/ui/const-generics/issues/issue-86535-2.rs
@@ -9,7 +9,7 @@ pub trait Foo {
         [(); Self::ASSOC_C]:;
 }
 
-struct Bar<const N: &'static ()>;
+struct Bar<const N: &'static ()>; //~ WARN struct `Bar` is never constructed
 impl<const N: &'static ()> Foo for Bar<N> {
     const ASSOC_C: usize = 3;
 
diff --git a/tests/ui/const-generics/issues/issue-86535-2.stderr b/tests/ui/const-generics/issues/issue-86535-2.stderr
new file mode 100644
index 00000000000..0ba74836575
--- /dev/null
+++ b/tests/ui/const-generics/issues/issue-86535-2.stderr
@@ -0,0 +1,10 @@
+warning: struct `Bar` is never constructed
+  --> $DIR/issue-86535-2.rs:12:8
+   |
+LL | struct Bar<const N: &'static ()>;
+   |        ^^^
+   |
+   = note: `#[warn(dead_code)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/const-generics/issues/issue-86535.rs b/tests/ui/const-generics/issues/issue-86535.rs
index 62454f4a388..2cdf801c156 100644
--- a/tests/ui/const-generics/issues/issue-86535.rs
+++ b/tests/ui/const-generics/issues/issue-86535.rs
@@ -2,7 +2,7 @@
 #![feature(adt_const_params, unsized_const_params, generic_const_exprs)]
 #![allow(incomplete_features, unused_variables)]
 
-struct F<const S: &'static str>;
+struct F<const S: &'static str>; //~ WARN struct `F` is never constructed
 impl<const S: &'static str> X for F<{ S }> {
     const W: usize = 3;
 
diff --git a/tests/ui/const-generics/issues/issue-86535.stderr b/tests/ui/const-generics/issues/issue-86535.stderr
new file mode 100644
index 00000000000..84d6c1c11ff
--- /dev/null
+++ b/tests/ui/const-generics/issues/issue-86535.stderr
@@ -0,0 +1,10 @@
+warning: struct `F` is never constructed
+  --> $DIR/issue-86535.rs:5:8
+   |
+LL | struct F<const S: &'static str>;
+   |        ^
+   |
+   = note: `#[warn(dead_code)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr
index 2f202705b7f..bd50ac0bf41 100644
--- a/tests/ui/consts/const-eval/format.stderr
+++ b/tests/ui/consts/const-eval/format.stderr
@@ -13,7 +13,7 @@ LL |     println!("{:?}", 0);
    |     ^^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const function `_print` in constant functions
   --> $DIR/format.rs:7:5
diff --git a/tests/ui/consts/const_cmp_type_id.rs b/tests/ui/consts/const_cmp_type_id.rs
index e89b8d37787..dca0615083a 100644
--- a/tests/ui/consts/const_cmp_type_id.rs
+++ b/tests/ui/consts/const_cmp_type_id.rs
@@ -6,11 +6,10 @@ use std::any::TypeId;
 fn main() {
     const {
         assert!(TypeId::of::<u8>() == TypeId::of::<u8>());
-        //~^ ERROR cannot call non-const operator in constants
+        //~^ ERROR the trait bound `TypeId: const PartialEq` is not satisfied
         assert!(TypeId::of::<()>() != TypeId::of::<u8>());
-        //~^ ERROR cannot call non-const operator in constants
+        //~^ ERROR the trait bound `TypeId: const PartialEq` is not satisfied
         let _a = TypeId::of::<u8>() < TypeId::of::<u16>();
-        //~^ ERROR cannot call non-const operator in constants
         // can't assert `_a` because it is not deterministic
         // FIXME(const_trait_impl) make it pass
     }
diff --git a/tests/ui/consts/const_cmp_type_id.stderr b/tests/ui/consts/const_cmp_type_id.stderr
index 62f8d42c0e6..a8242a200ef 100644
--- a/tests/ui/consts/const_cmp_type_id.stderr
+++ b/tests/ui/consts/const_cmp_type_id.stderr
@@ -1,33 +1,15 @@
-error[E0015]: cannot call non-const operator in constants
+error[E0277]: the trait bound `TypeId: const PartialEq` is not satisfied
   --> $DIR/const_cmp_type_id.rs:8:17
    |
 LL |         assert!(TypeId::of::<u8>() == TypeId::of::<u8>());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: impl defined here, but it is not `const`
-  --> $SRC_DIR/core/src/any.rs:LL:COL
-   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
 
-error[E0015]: cannot call non-const operator in constants
+error[E0277]: the trait bound `TypeId: const PartialEq` is not satisfied
   --> $DIR/const_cmp_type_id.rs:10:17
    |
 LL |         assert!(TypeId::of::<()>() != TypeId::of::<u8>());
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: impl defined here, but it is not `const`
-  --> $SRC_DIR/core/src/any.rs:LL:COL
-   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const operator in constants
-  --> $DIR/const_cmp_type_id.rs:12:18
-   |
-LL |         let _a = TypeId::of::<u8>() < TypeId::of::<u16>();
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: impl defined here, but it is not `const`
-  --> $SRC_DIR/core/src/any.rs:LL:COL
-   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr
index d688bfbde2b..7dc08049889 100644
--- a/tests/ui/consts/fn_trait_refs.stderr
+++ b/tests/ui/consts/fn_trait_refs.stderr
@@ -4,12 +4,6 @@ error[E0635]: unknown feature `const_fn_trait_ref_impls`
 LL | #![feature(const_fn_trait_ref_impls)]
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0635]: unknown feature `const_cmp`
-  --> $DIR/fn_trait_refs.rs:7:12
-   |
-LL | #![feature(const_cmp)]
-   |            ^^^^^^^^^
-
 error: `~const` can only be applied to `#[const_trait]` traits
   --> $DIR/fn_trait_refs.rs:14:8
    |
@@ -155,21 +149,17 @@ note: `FnMut` can't be used with `~const` because it isn't annotated with `#[con
   --> $SRC_DIR/core/src/ops/function.rs:LL:COL
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error[E0015]: cannot call non-const operator in constants
+error[E0277]: the trait bound `(i32, i32, i32): const PartialEq` is not satisfied
   --> $DIR/fn_trait_refs.rs:71:17
    |
 LL |         assert!(test_one == (1, 1, 1));
    |                 ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
 
-error[E0015]: cannot call non-const operator in constants
+error[E0277]: the trait bound `(i32, i32): const PartialEq` is not satisfied
   --> $DIR/fn_trait_refs.rs:74:17
    |
 LL |         assert!(test_two == (2, 2));
    |                 ^^^^^^^^^^^^^^^^^^
-   |
-   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
 
 error[E0015]: cannot call non-const closure in constant functions
   --> $DIR/fn_trait_refs.rs:16:5
@@ -195,7 +185,7 @@ LL |     f()
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
 
-error: aborting due to 22 previous errors
+error: aborting due to 21 previous errors
 
-Some errors have detailed explanations: E0015, E0635.
+Some errors have detailed explanations: E0015, E0277, E0635.
 For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/consts/issue-73976-monomorphic.stderr b/tests/ui/consts/issue-73976-monomorphic.stderr
index ef754b23ff0..e5b32e0c4ad 100644
--- a/tests/ui/consts/issue-73976-monomorphic.stderr
+++ b/tests/ui/consts/issue-73976-monomorphic.stderr
@@ -1,13 +1,9 @@
-error[E0015]: cannot call non-const operator in constant functions
+error[E0277]: the trait bound `TypeId: ~const PartialEq` is not satisfied
   --> $DIR/issue-73976-monomorphic.rs:21:5
    |
 LL |     GetTypeId::<T>::VALUE == GetTypeId::<usize>::VALUE
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: impl defined here, but it is not `const`
-  --> $SRC_DIR/core/src/any.rs:LL:COL
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/consts/issue-90870.rs b/tests/ui/consts/issue-90870.rs
index b62769a33f8..f807ae75ee5 100644
--- a/tests/ui/consts/issue-90870.rs
+++ b/tests/ui/consts/issue-90870.rs
@@ -3,22 +3,31 @@
 #![allow(dead_code)]
 
 const fn f(a: &u8, b: &u8) -> bool {
+    //~^ HELP: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+    //~| HELP: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+    //~| HELP: add `#![feature(const_trait_impl)]` to the crate attributes to enable
     a == b
-    //~^ ERROR: cannot call non-const operator in constant functions [E0015]
+    //~^ ERROR: cannot call conditionally-const operator in constant functions
+    //~| ERROR: `PartialEq` is not yet stable as a const trait
     //~| HELP: consider dereferencing here
+    //~| HELP: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 }
 
 const fn g(a: &&&&i64, b: &&&&i64) -> bool {
     a == b
-    //~^ ERROR: cannot call non-const operator in constant functions [E0015]
+    //~^ ERROR: cannot call conditionally-const operator in constant functions
+    //~| ERROR: `PartialEq` is not yet stable as a const trait
     //~| HELP: consider dereferencing here
+    //~| HELP: add `#![feature(const_trait_impl)]` to the crate attributes to enable
 }
 
 const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
     while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
         if l == r {
-        //~^ ERROR: cannot call non-const operator in constant functions [E0015]
+        //~^ ERROR: cannot call conditionally-const operator in constant functions
+        //~| ERROR: `PartialEq` is not yet stable as a const trait
         //~| HELP: consider dereferencing here
+        //~| HELP: add `#![feature(const_trait_impl)]` to the crate attributes to enable
             a = at;
             b = bt;
         } else {
diff --git a/tests/ui/consts/issue-90870.stderr b/tests/ui/consts/issue-90870.stderr
index ea987920d7d..8d6f21fd82f 100644
--- a/tests/ui/consts/issue-90870.stderr
+++ b/tests/ui/consts/issue-90870.stderr
@@ -1,39 +1,81 @@
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/issue-90870.rs:6:5
+error[E0658]: cannot call conditionally-const operator in constant functions
+  --> $DIR/issue-90870.rs:9:5
    |
 LL |     a == b
    |     ^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+   = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
+   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 help: consider dereferencing here
    |
 LL |     *a == *b
    |     +     +
 
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/issue-90870.rs:12:5
+error: `PartialEq` is not yet stable as a const trait
+  --> $DIR/issue-90870.rs:9:5
+   |
+LL |     a == b
+   |     ^^^^^^
+   |
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
+
+error[E0658]: cannot call conditionally-const operator in constant functions
+  --> $DIR/issue-90870.rs:17:5
    |
 LL |     a == b
    |     ^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+   = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
+   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 help: consider dereferencing here
    |
 LL |     ****a == ****b
    |     ++++     ++++
 
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/issue-90870.rs:19:12
+error: `PartialEq` is not yet stable as a const trait
+  --> $DIR/issue-90870.rs:17:5
+   |
+LL |     a == b
+   |     ^^^^^^
+   |
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
+
+error[E0658]: cannot call conditionally-const operator in constant functions
+  --> $DIR/issue-90870.rs:26:12
    |
 LL |         if l == r {
    |            ^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+   = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
+   = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 help: consider dereferencing here
    |
 LL |         if *l == *r {
    |            +     +
 
-error: aborting due to 3 previous errors
+error: `PartialEq` is not yet stable as a const trait
+  --> $DIR/issue-90870.rs:26:12
+   |
+LL |         if l == r {
+   |            ^^^^^^
+   |
+help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
+   |
+LL + #![feature(const_trait_impl)]
+   |
+
+error: aborting due to 6 previous errors
 
-For more information about this error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/consts/recursive-const-in-impl.stderr b/tests/ui/consts/recursive-const-in-impl.stderr
index 6175112c8cc..035d9c2f21c 100644
--- a/tests/ui/consts/recursive-const-in-impl.stderr
+++ b/tests/ui/consts/recursive-const-in-impl.stderr
@@ -1,11 +1,12 @@
 error: queries overflow the depth limit!
-  --> $DIR/recursive-const-in-impl.rs:11:14
+  --> $DIR/recursive-const-in-impl.rs:11:20
    |
 LL |     println!("{}", Thing::<i32>::X);
-   |              ^^^^
+   |                    ^^^^^^^^^^^^^^^
    |
    = help: consider increasing the recursion limit by adding a `#![recursion_limit = "14"]` attribute to your crate (`recursive_const_in_impl`)
    = note: query depth increased by 9 when simplifying constant for the type system `main::promoted[1]`
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/defaults-well-formedness.rs b/tests/ui/defaults-well-formedness.rs
deleted file mode 100644
index e5e48edad88..00000000000
--- a/tests/ui/defaults-well-formedness.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-//@ run-pass
-
-#![allow(dead_code)]
-trait Trait<T> {}
-struct Foo<U, V=i32>(U, V) where U: Trait<V>;
-
-trait Marker {}
-struct TwoParams<T, U>(T, U);
-impl Marker for TwoParams<i32, i32> {}
-
-// Clauses with more than 1 param are not checked.
-struct IndividuallyBogus<T = i32, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Marker;
-struct BogusTogether<T = u32, U = i32>(T, U) where TwoParams<T, U>: Marker;
-// Clauses with non-defaulted params are not checked.
-struct NonDefaultedInClause<T, U = i32>(TwoParams<T, U>) where TwoParams<T, U>: Marker;
-struct DefaultedLhs<U, V=i32>(U, V) where V: Trait<U>;
-// Dependent defaults are not checked.
-struct Dependent<T, U = T>(T, U) where U: Copy;
-trait SelfBound<T: Copy=Self> {}
-// Not even for well-formedness.
-struct WellFormedProjection<A, T=<A as Iterator>::Item>(A, T);
-
-// Issue #49344, predicates with lifetimes should not be checked.
-trait Scope<'a> {}
-struct Request<'a, S: Scope<'a> = i32>(S, &'a ());
-
-fn main() {}
diff --git a/tests/ui/deprecation/deprecated-expr-precedence.rs b/tests/ui/deprecation/deprecated-expr-precedence.rs
new file mode 100644
index 00000000000..9636b46df20
--- /dev/null
+++ b/tests/ui/deprecation/deprecated-expr-precedence.rs
@@ -0,0 +1,8 @@
+//@ check-fail
+//@ compile-flags: --crate-type=lib
+
+// Regression test for issue 142649
+pub fn public() {
+    #[deprecated] 0
+    //~^ ERROR mismatched types
+}
diff --git a/tests/ui/deprecation/deprecated-expr-precedence.stderr b/tests/ui/deprecation/deprecated-expr-precedence.stderr
new file mode 100644
index 00000000000..3275f2e790a
--- /dev/null
+++ b/tests/ui/deprecation/deprecated-expr-precedence.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+  --> $DIR/deprecated-expr-precedence.rs:6:19
+   |
+LL | pub fn public() {
+   |                - help: try adding a return type: `-> i32`
+LL |     #[deprecated] 0
+   |                   ^ expected `()`, found integer
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/deprecation-in-force-unstable.rs b/tests/ui/deprecation/deprecated_main_function.rs
index 6aaf29b069a..398046637d8 100644
--- a/tests/ui/deprecation-in-force-unstable.rs
+++ b/tests/ui/deprecation/deprecated_main_function.rs
@@ -2,4 +2,4 @@
 //@ compile-flags:-Zforce-unstable-if-unmarked
 
 #[deprecated] // should work even with -Zforce-unstable-if-unmarked
-fn main() { }
+fn main() {}
diff --git a/tests/ui/deprecation/deprecated_no_stack_check.rs b/tests/ui/deprecation/deprecated_no_stack_check.rs
index ef482098634..8e1f5bbf045 100644
--- a/tests/ui/deprecation/deprecated_no_stack_check.rs
+++ b/tests/ui/deprecation/deprecated_no_stack_check.rs
@@ -1,5 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![deny(warnings)]
 #![feature(no_stack_check)]
 //~^ ERROR: feature has been removed [E0557]
diff --git a/tests/ui/deprecation/deprecated_no_stack_check.stderr b/tests/ui/deprecation/deprecated_no_stack_check.stderr
index 2d08b1b8db5..33788661d73 100644
--- a/tests/ui/deprecation/deprecated_no_stack_check.stderr
+++ b/tests/ui/deprecation/deprecated_no_stack_check.stderr
@@ -1,10 +1,10 @@
 error[E0557]: feature has been removed
-  --> $DIR/deprecated_no_stack_check.rs:4:12
+  --> $DIR/deprecated_no_stack_check.rs:2:12
    |
 LL | #![feature(no_stack_check)]
    |            ^^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.0.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/40110> for more information
+   = note: removed in 1.0.0; see <https://github.com/rust-lang/rust/pull/40110> for more information
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/deref-rc.rs b/tests/ui/deref-rc.rs
deleted file mode 100644
index 92fdd900359..00000000000
--- a/tests/ui/deref-rc.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//@ run-pass
-
-use std::rc::Rc;
-
-fn main() {
-    let x = Rc::new([1, 2, 3, 4]);
-    assert_eq!(*x, [1, 2, 3, 4]);
-}
diff --git a/tests/ui/deref.rs b/tests/ui/deref.rs
deleted file mode 100644
index 0a6f3cc81f6..00000000000
--- a/tests/ui/deref.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-//@ run-pass
-
-pub fn main() {
-    let x: Box<isize> = Box::new(10);
-    let _y: isize = *x;
-}
diff --git a/tests/ui/derive-uninhabited-enum-38885.rs b/tests/ui/derive-uninhabited-enum-38885.rs
deleted file mode 100644
index 2259a542706..00000000000
--- a/tests/ui/derive-uninhabited-enum-38885.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-//@ check-pass
-//@ compile-flags: -Wunused
-
-// ensure there are no special warnings about uninhabited types
-// when deriving Debug on an empty enum
-
-#[derive(Debug)]
-enum Void {}
-
-#[derive(Debug)]
-enum Foo {
-    Bar(#[allow(dead_code)] u8),
-    Void(Void), //~ WARN variant `Void` is never constructed
-}
-
-fn main() {
-    let x = Foo::Bar(42);
-    println!("{:?}", x);
-}
diff --git a/tests/ui/derives/clone-debug-dead-code.stderr b/tests/ui/derives/clone-debug-dead-code.stderr
index 34b7f929ec5..38be486e332 100644
--- a/tests/ui/derives/clone-debug-dead-code.stderr
+++ b/tests/ui/derives/clone-debug-dead-code.stderr
@@ -40,7 +40,7 @@ LL | struct D { f: () }
    |        |
    |        field in this struct
    |
-   = note: `D` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis
+   = note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
 
 error: field `f` is never read
   --> $DIR/clone-debug-dead-code.rs:21:12
diff --git a/tests/ui/derives/derive-debug-uninhabited-enum.rs b/tests/ui/derives/derive-debug-uninhabited-enum.rs
new file mode 100644
index 00000000000..be7b3ab348d
--- /dev/null
+++ b/tests/ui/derives/derive-debug-uninhabited-enum.rs
@@ -0,0 +1,23 @@
+//! Regression test for `#[derive(Debug)]` on enums with uninhabited variants.
+//!
+//! Ensures there are no special warnings about uninhabited types when deriving
+//! Debug on an enum with uninhabited variants, only standard unused warnings.
+//!
+//! Issue: https://github.com/rust-lang/rust/issues/38885
+
+//@ check-pass
+//@ compile-flags: -Wunused
+
+#[derive(Debug)]
+enum Void {}
+
+#[derive(Debug)]
+enum Foo {
+    Bar(#[allow(dead_code)] u8),
+    Void(Void), //~ WARN variant `Void` is never constructed
+}
+
+fn main() {
+    let x = Foo::Bar(42);
+    println!("{:?}", x);
+}
diff --git a/tests/ui/derive-uninhabited-enum-38885.stderr b/tests/ui/derives/derive-debug-uninhabited-enum.stderr
index bcd8f6b7b53..4911b6b6cde 100644
--- a/tests/ui/derive-uninhabited-enum-38885.stderr
+++ b/tests/ui/derives/derive-debug-uninhabited-enum.stderr
@@ -1,5 +1,5 @@
 warning: variant `Void` is never constructed
-  --> $DIR/derive-uninhabited-enum-38885.rs:13:5
+  --> $DIR/derive-debug-uninhabited-enum.rs:17:5
    |
 LL | enum Foo {
    |      --- variant in this enum
diff --git a/tests/ui/derives/derives-span-Debug-enum-struct-variant.stderr b/tests/ui/derives/derives-span-Debug-enum-struct-variant.stderr
index a7f6d094681..147910b715f 100644
--- a/tests/ui/derives/derives-span-Debug-enum-struct-variant.stderr
+++ b/tests/ui/derives/derives-span-Debug-enum-struct-variant.stderr
@@ -5,9 +5,8 @@ LL | #[derive(Debug)]
    |          ----- in this derive macro expansion
 ...
 LL |      x: Error
-   |      ^^^^^^^^ `Error` cannot be formatted using `{:?}`
+   |      ^^^^^^^^ the trait `Debug` is not implemented for `Error`
    |
-   = help: the trait `Debug` is not implemented for `Error`
    = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error`
 help: consider annotating `Error` with `#[derive(Debug)]`
    |
diff --git a/tests/ui/derives/derives-span-Debug-enum.stderr b/tests/ui/derives/derives-span-Debug-enum.stderr
index b3a58478159..6f97ceb02d3 100644
--- a/tests/ui/derives/derives-span-Debug-enum.stderr
+++ b/tests/ui/derives/derives-span-Debug-enum.stderr
@@ -5,9 +5,8 @@ LL | #[derive(Debug)]
    |          ----- in this derive macro expansion
 ...
 LL |      Error
-   |      ^^^^^ `Error` cannot be formatted using `{:?}`
+   |      ^^^^^ the trait `Debug` is not implemented for `Error`
    |
-   = help: the trait `Debug` is not implemented for `Error`
    = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error`
 help: consider annotating `Error` with `#[derive(Debug)]`
    |
diff --git a/tests/ui/derives/derives-span-Debug-struct.stderr b/tests/ui/derives/derives-span-Debug-struct.stderr
index c8ad652716c..46d69a892f2 100644
--- a/tests/ui/derives/derives-span-Debug-struct.stderr
+++ b/tests/ui/derives/derives-span-Debug-struct.stderr
@@ -5,9 +5,8 @@ LL | #[derive(Debug)]
    |          ----- in this derive macro expansion
 LL | struct Struct {
 LL |     x: Error
-   |     ^^^^^^^^ `Error` cannot be formatted using `{:?}`
+   |     ^^^^^^^^ the trait `Debug` is not implemented for `Error`
    |
-   = help: the trait `Debug` is not implemented for `Error`
    = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error`
 help: consider annotating `Error` with `#[derive(Debug)]`
    |
diff --git a/tests/ui/derives/derives-span-Debug-tuple-struct.stderr b/tests/ui/derives/derives-span-Debug-tuple-struct.stderr
index dbece4d2091..a3feeff6df3 100644
--- a/tests/ui/derives/derives-span-Debug-tuple-struct.stderr
+++ b/tests/ui/derives/derives-span-Debug-tuple-struct.stderr
@@ -5,9 +5,8 @@ LL | #[derive(Debug)]
    |          ----- in this derive macro expansion
 LL | struct Struct(
 LL |     Error
-   |     ^^^^^ `Error` cannot be formatted using `{:?}`
+   |     ^^^^^ the trait `Debug` is not implemented for `Error`
    |
-   = help: the trait `Debug` is not implemented for `Error`
    = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error`
 help: consider annotating `Error` with `#[derive(Debug)]`
    |
diff --git a/tests/ui/derives/nonsense-input-to-debug.rs b/tests/ui/derives/nonsense-input-to-debug.rs
new file mode 100644
index 00000000000..7dfa3cd616a
--- /dev/null
+++ b/tests/ui/derives/nonsense-input-to-debug.rs
@@ -0,0 +1,12 @@
+// Issue: #32950
+// Ensure that using macros rather than a type doesn't break `derive`.
+
+#[derive(Debug)]
+struct Nonsense<T> {
+    //~^ ERROR type parameter `T` is never used
+    should_be_vec_t: vec![T],
+    //~^ ERROR `derive` cannot be used on items with type macros
+    //~| ERROR expected type, found `expr` metavariable
+}
+
+fn main() {}
diff --git a/tests/ui/derives/nonsense-input-to-debug.stderr b/tests/ui/derives/nonsense-input-to-debug.stderr
new file mode 100644
index 00000000000..7c97ca93cfc
--- /dev/null
+++ b/tests/ui/derives/nonsense-input-to-debug.stderr
@@ -0,0 +1,30 @@
+error: `derive` cannot be used on items with type macros
+  --> $DIR/nonsense-input-to-debug.rs:7:22
+   |
+LL |     should_be_vec_t: vec![T],
+   |                      ^^^^^^^
+
+error: expected type, found `expr` metavariable
+  --> $DIR/nonsense-input-to-debug.rs:7:22
+   |
+LL |     should_be_vec_t: vec![T],
+   |                      ^^^^^^^
+   |                      |
+   |                      expected type
+   |                      in this macro invocation
+   |                      this macro call doesn't expand to a type
+   |
+   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0392]: type parameter `T` is never used
+  --> $DIR/nonsense-input-to-debug.rs:5:17
+   |
+LL | struct Nonsense<T> {
+   |                 ^ unused type parameter
+   |
+   = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
+   = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0392`.
diff --git a/tests/ui/destructure-trait-ref.rs b/tests/ui/destructure-trait-ref.rs
deleted file mode 100644
index daa0ca30d68..00000000000
--- a/tests/ui/destructure-trait-ref.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-// The regression test for #15031 to make sure destructuring trait
-// reference work properly.
-
-//@ dont-require-annotations: NOTE
-
-#![feature(box_patterns)]
-
-trait T { fn foo(&self) {} }
-impl T for isize {}
-
-
-fn main() {
-    // For an expression of the form:
-    //
-    //      let &...&x = &..&SomeTrait;
-    //
-    // Say we have n `&` at the left hand and m `&` right hand, then:
-    // if n < m, we are golden;
-    // if n == m, it's a derefing non-derefable type error;
-    // if n > m, it's a type mismatch error.
-
-    // n < m
-    let &x = &(&1isize as &dyn T);
-    let &x = &&(&1isize as &dyn T);
-    let &&x = &&(&1isize as &dyn T);
-
-    // n == m
-    let &x = &1isize as &dyn T;      //~ ERROR type `&dyn T` cannot be dereferenced
-    let &&x = &(&1isize as &dyn T);  //~ ERROR type `&dyn T` cannot be dereferenced
-    let box x = Box::new(1isize) as Box<dyn T>;
-    //~^ ERROR type `Box<dyn T>` cannot be dereferenced
-
-    // n > m
-    let &&x = &1isize as &dyn T;
-    //~^ ERROR mismatched types
-    //~| NOTE expected trait object `dyn T`
-    //~| NOTE found reference `&_`
-    let &&&x = &(&1isize as &dyn T);
-    //~^ ERROR mismatched types
-    //~| NOTE expected trait object `dyn T`
-    //~| NOTE found reference `&_`
-    let box box x = Box::new(1isize) as Box<dyn T>;
-    //~^ ERROR mismatched types
-    //~| NOTE expected trait object `dyn T`
-    //~| NOTE found struct `Box<_>`
-}
diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr
index 5408825d8cd..18d80810ab0 100644
--- a/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr
+++ b/tests/ui/diagnostic-width/non-whitespace-trimming-unicode.stderr
@@ -1,7 +1,7 @@
 error[E0308]: mismatched types
   --> $DIR/non-whitespace-trimming-unicode.rs:4:415
    |
-LL | ...♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42;  let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓  ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼...
+LL | ...♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42;  let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓  ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽       ...
    |                                                      --   ^^ expected `()`, found integer
    |                                                      |
    |                                                      expected due to this
diff --git a/tests/ui/diverging-fallback-method-chain.rs b/tests/ui/diverging-fallback-method-chain.rs
deleted file mode 100644
index aa8eba1191b..00000000000
--- a/tests/ui/diverging-fallback-method-chain.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-//@ run-pass
-
-#![allow(unused_imports)]
-// Test a regression found when building compiler. The `produce()`
-// error type `T` winds up getting unified with result of `x.parse()`;
-// the type of the closure given to `unwrap_or_else` needs to be
-// inferred to `usize`.
-
-use std::num::ParseIntError;
-
-fn produce<T>() -> Result<&'static str, T> {
-    Ok("22")
-}
-
-fn main() {
-    let x: usize = produce()
-        .and_then(|x| x.parse())
-        .unwrap_or_else(|_| panic!());
-    println!("{}", x);
-}
diff --git a/tests/ui/diverging-fallback-option.rs b/tests/ui/diverging-fallback-option.rs
deleted file mode 100644
index aa793ebd017..00000000000
--- a/tests/ui/diverging-fallback-option.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//@ run-pass
-
-#![allow(warnings)]
-
-// Here the type of `c` is `Option<?T>`, where `?T` is unconstrained.
-// Because there is data-flow from the `{ return; }` block, which
-// diverges and hence has type `!`, into `c`, we will default `?T` to
-// `!`, and hence this code compiles rather than failing and requiring
-// a type annotation.
-
-fn main() {
-    let c = Some({ return; });
-    c.unwrap();
-}
diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr
index d8935be5609..8b4f3f52ee9 100644
--- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr
+++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr
@@ -87,6 +87,11 @@ help: alternatively, consider constraining `g` so it does not apply to trait obj
    |
 LL | trait A { fn g(b: B) -> B where Self: Sized; }
    |                           +++++++++++++++++
+help: you might have meant to use `Self` to refer to the implementing type
+   |
+LL - trait B { fn f(a: A) -> A; }
+LL + trait B { fn f(a: Self) -> A; }
+   |
 
 warning: trait objects without an explicit `dyn` are deprecated
   --> $DIR/avoid-ice-on-warning-3.rs:14:19
@@ -124,6 +129,11 @@ help: alternatively, consider constraining `f` so it does not apply to trait obj
    |
 LL | trait B { fn f(a: A) -> A where Self: Sized; }
    |                           +++++++++++++++++
+help: you might have meant to use `Self` to refer to the implementing type
+   |
+LL - trait A { fn g(b: B) -> B; }
+LL + trait A { fn g(b: Self) -> B; }
+   |
 
 error: aborting due to 2 previous errors; 6 warnings emitted
 
diff --git a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.rs b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.rs
index 9e5c1bfe416..b866dab9dba 100644
--- a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.rs
+++ b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.rs
@@ -8,8 +8,7 @@ trait GatTrait {
 
 trait SuperTrait<T>: for<'a> GatTrait<Gat<'a> = T> {
     fn c(&self) -> dyn SuperTrait<T>;
-    //~^ ERROR associated item referring to unboxed trait object for its own trait
-    //~| ERROR the trait `SuperTrait` is not dyn compatible
+    //~^ ERROR the trait `SuperTrait` is not dyn compatible
 }
 
 fn main() {}
diff --git a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr
index 582cf1af054..ba4ce475399 100644
--- a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr
+++ b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr
@@ -7,20 +7,6 @@ LL |         Self: 'a;
    |               ^^
    = help: consider adding an explicit lifetime bound `Self: 'a`...
 
-error: associated item referring to unboxed trait object for its own trait
-  --> $DIR/supertrait-mentions-GAT.rs:10:20
-   |
-LL | trait SuperTrait<T>: for<'a> GatTrait<Gat<'a> = T> {
-   |       ---------- in this trait
-LL |     fn c(&self) -> dyn SuperTrait<T>;
-   |                    ^^^^^^^^^^^^^^^^^
-   |
-help: you might have meant to use `Self` to refer to the implementing type
-   |
-LL -     fn c(&self) -> dyn SuperTrait<T>;
-LL +     fn c(&self) -> Self;
-   |
-
 error[E0038]: the trait `SuperTrait` is not dyn compatible
   --> $DIR/supertrait-mentions-GAT.rs:10:20
    |
@@ -37,8 +23,13 @@ LL |     type Gat<'a>
 LL | trait SuperTrait<T>: for<'a> GatTrait<Gat<'a> = T> {
    |       ---------- this trait is not dyn compatible...
    = help: consider moving `Gat` to another trait
+help: you might have meant to use `Self` to refer to the implementing type
+   |
+LL -     fn c(&self) -> dyn SuperTrait<T>;
+LL +     fn c(&self) -> Self;
+   |
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
 Some errors have detailed explanations: E0038, E0311.
 For more information about an error, try `rustc --explain E0038`.
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr
index 3ddff11798d..229bfbe59e5 100644
--- a/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.not-diag-in-deps.stderr
@@ -2,10 +2,8 @@ error[E0277]: `A` doesn't implement `std::fmt::Display`
   --> remapped/errors/remap-path-prefix-diagnostics.rs:LL:COL
    |
 LL | impl r#trait::Trait for A {}
-   |                         ^ `A` cannot be formatted with the default formatter
+   |                         ^ the trait `std::fmt::Display` is not implemented for `A`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `A`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `Trait`
   --> $DIR/auxiliary/trait.rs:LL:COL
    |
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr
index 85c781425b1..a59af3b6a82 100644
--- a/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-debuginfo-in-deps.stderr
@@ -2,10 +2,8 @@ error[E0277]: `A` doesn't implement `std::fmt::Display`
   --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL
    |
 LL | impl r#trait::Trait for A {}
-   |                         ^ `A` cannot be formatted with the default formatter
+   |                         ^ the trait `std::fmt::Display` is not implemented for `A`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `A`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `Trait`
   --> $DIR/auxiliary/trait-debuginfo.rs:LL:COL
    |
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr
index 792ea7925ad..18fb9afcf39 100644
--- a/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr
@@ -2,10 +2,8 @@ error[E0277]: `A` doesn't implement `std::fmt::Display`
   --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL
    |
 LL | impl r#trait::Trait for A {}
-   |                         ^ `A` cannot be formatted with the default formatter
+   |                         ^ the trait `std::fmt::Display` is not implemented for `A`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `A`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `Trait`
   --> $DIR/auxiliary/trait-diag.rs:LL:COL
    |
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr
index d13333d2e48..9e770f07fba 100644
--- a/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-macro-in-deps.stderr
@@ -2,10 +2,8 @@ error[E0277]: `A` doesn't implement `std::fmt::Display`
   --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL
    |
 LL | impl r#trait::Trait for A {}
-   |                         ^ `A` cannot be formatted with the default formatter
+   |                         ^ the trait `std::fmt::Display` is not implemented for `A`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `A`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `Trait`
   --> $DIR/auxiliary/trait-macro.rs:LL:COL
    |
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr
index 85c781425b1..a59af3b6a82 100644
--- a/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-debuginfo-in-deps.stderr
@@ -2,10 +2,8 @@ error[E0277]: `A` doesn't implement `std::fmt::Display`
   --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL
    |
 LL | impl r#trait::Trait for A {}
-   |                         ^ `A` cannot be formatted with the default formatter
+   |                         ^ the trait `std::fmt::Display` is not implemented for `A`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `A`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `Trait`
   --> $DIR/auxiliary/trait-debuginfo.rs:LL:COL
    |
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr
index 08f7fb2c736..ca6f2b1697a 100644
--- a/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-diag-in-deps.stderr
@@ -2,10 +2,8 @@ error[E0277]: `A` doesn't implement `std::fmt::Display`
   --> remapped/errors/remap-path-prefix-diagnostics.rs:LL:COL
    |
 LL | impl r#trait::Trait for A {}
-   |                         ^ `A` cannot be formatted with the default formatter
+   |                         ^ the trait `std::fmt::Display` is not implemented for `A`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `A`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `Trait`
   --> remapped/errors/auxiliary/trait-diag.rs:LL:COL
    |
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr
index d13333d2e48..9e770f07fba 100644
--- a/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-macro-in-deps.stderr
@@ -2,10 +2,8 @@ error[E0277]: `A` doesn't implement `std::fmt::Display`
   --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL
    |
 LL | impl r#trait::Trait for A {}
-   |                         ^ `A` cannot be formatted with the default formatter
+   |                         ^ the trait `std::fmt::Display` is not implemented for `A`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `A`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `Trait`
   --> $DIR/auxiliary/trait-macro.rs:LL:COL
    |
diff --git a/tests/ui/extern-flag/auxiliary/panic_handler.rs b/tests/ui/extern-flag/auxiliary/panic_handler.rs
index 9140ceed229..9607f0ed013 100644
--- a/tests/ui/extern-flag/auxiliary/panic_handler.rs
+++ b/tests/ui/extern-flag/auxiliary/panic_handler.rs
@@ -12,4 +12,12 @@ pub fn begin_panic_handler(_info: &core::panic::PanicInfo<'_>) -> ! {
 }
 
 #[lang = "eh_personality"]
-extern "C" fn eh_personality() {}
+extern "C" fn eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
diff --git a/tests/ui/feature-gates/feature-gate-abi-custom.rs b/tests/ui/feature-gates/feature-gate-abi-custom.rs
index 3ddce974dd7..312b6230b74 100644
--- a/tests/ui/feature-gates/feature-gate-abi-custom.rs
+++ b/tests/ui/feature-gates/feature-gate-abi-custom.rs
@@ -15,11 +15,11 @@ unsafe extern "custom" fn f7() {
 trait Tr {
     extern "custom" fn m7();
     //~^ ERROR "custom" ABI is experimental
-    //~| ERROR functions with the `"custom"` ABI must be unsafe
+    //~| ERROR functions with the "custom" ABI must be unsafe
     #[unsafe(naked)]
     extern "custom" fn dm7() {
         //~^ ERROR "custom" ABI is experimental
-        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~| ERROR functions with the "custom" ABI must be unsafe
         naked_asm!("")
     }
 }
@@ -31,7 +31,7 @@ impl Tr for S {
     #[unsafe(naked)]
     extern "custom" fn m7() {
         //~^ ERROR "custom" ABI is experimental
-        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~| ERROR functions with the "custom" ABI must be unsafe
         naked_asm!("")
     }
 }
@@ -41,7 +41,7 @@ impl S {
     #[unsafe(naked)]
     extern "custom" fn im7() {
         //~^ ERROR "custom" ABI is experimental
-        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~| ERROR functions with the "custom" ABI must be unsafe
         naked_asm!("")
     }
 }
diff --git a/tests/ui/feature-gates/feature-gate-abi-custom.stderr b/tests/ui/feature-gates/feature-gate-abi-custom.stderr
index e6dce0126d6..e359dbb5ebe 100644
--- a/tests/ui/feature-gates/feature-gate-abi-custom.stderr
+++ b/tests/ui/feature-gates/feature-gate-abi-custom.stderr
@@ -1,4 +1,4 @@
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/feature-gate-abi-custom.rs:16:5
    |
 LL |     extern "custom" fn m7();
@@ -9,7 +9,7 @@ help: add the `unsafe` keyword to this definition
 LL |     unsafe extern "custom" fn m7();
    |     ++++++
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/feature-gate-abi-custom.rs:20:5
    |
 LL |     extern "custom" fn dm7() {
@@ -20,7 +20,7 @@ help: add the `unsafe` keyword to this definition
 LL |     unsafe extern "custom" fn dm7() {
    |     ++++++
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/feature-gate-abi-custom.rs:32:5
    |
 LL |     extern "custom" fn m7() {
@@ -31,7 +31,7 @@ help: add the `unsafe` keyword to this definition
 LL |     unsafe extern "custom" fn m7() {
    |     ++++++
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/feature-gate-abi-custom.rs:42:5
    |
 LL |     extern "custom" fn im7() {
diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.AMDGPU.stderr b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.AMDGPU.stderr
index fca32c5c1e6..4fa3fee942e 100644
--- a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.AMDGPU.stderr
+++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.AMDGPU.stderr
@@ -19,7 +19,7 @@ LL |     extern "gpu-kernel" fn m1(_: ());
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:23:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:24:12
    |
 LL |     extern "gpu-kernel" fn dm1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -29,7 +29,7 @@ LL |     extern "gpu-kernel" fn dm1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:31:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:32:12
    |
 LL |     extern "gpu-kernel" fn m1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -39,7 +39,7 @@ LL |     extern "gpu-kernel" fn m1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:38:12
    |
 LL |     extern "gpu-kernel" fn im1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL |     extern "gpu-kernel" fn im1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:18
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:43:18
    |
 LL | type A1 = extern "gpu-kernel" fn(_: ());
    |                  ^^^^^^^^^^^^
diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.HOST.stderr b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.HOST.stderr
index cc81289f6b7..88734bc9d22 100644
--- a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.HOST.stderr
+++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.HOST.stderr
@@ -1,3 +1,9 @@
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:16:8
+   |
+LL | extern "gpu-kernel" fn f1(_: ()) {}
+   |        ^^^^^^^^^^^^
+
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
   --> $DIR/feature-gate-abi_gpu_kernel.rs:16:8
    |
@@ -8,6 +14,12 @@ LL | extern "gpu-kernel" fn f1(_: ()) {}
    = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:21:12
+   |
+LL |     extern "gpu-kernel" fn m1(_: ());
+   |            ^^^^^^^^^^^^
+
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
   --> $DIR/feature-gate-abi_gpu_kernel.rs:21:12
    |
@@ -18,8 +30,14 @@ LL |     extern "gpu-kernel" fn m1(_: ());
    = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:24:12
+   |
+LL |     extern "gpu-kernel" fn dm1(_: ()) {}
+   |            ^^^^^^^^^^^^
+
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:23:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:24:12
    |
 LL |     extern "gpu-kernel" fn dm1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -28,8 +46,14 @@ LL |     extern "gpu-kernel" fn dm1(_: ()) {}
    = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:32:12
+   |
+LL |     extern "gpu-kernel" fn m1(_: ()) {}
+   |            ^^^^^^^^^^^^
+
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:31:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:32:12
    |
 LL |     extern "gpu-kernel" fn m1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -38,8 +62,14 @@ LL |     extern "gpu-kernel" fn m1(_: ()) {}
    = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:38:12
+   |
+LL |     extern "gpu-kernel" fn im1(_: ()) {}
+   |            ^^^^^^^^^^^^
+
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:38:12
    |
 LL |     extern "gpu-kernel" fn im1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -48,8 +78,14 @@ LL |     extern "gpu-kernel" fn im1(_: ()) {}
    = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:43:18
+   |
+LL | type A1 = extern "gpu-kernel" fn(_: ());
+   |                  ^^^^^^^^^^^^
+
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:18
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:43:18
    |
 LL | type A1 = extern "gpu-kernel" fn(_: ());
    |                  ^^^^^^^^^^^^
@@ -58,6 +94,12 @@ LL | type A1 = extern "gpu-kernel" fn(_: ());
    = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
+error[E0570]: "gpu-kernel" is not a supported ABI for the current target
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:47:8
+   |
+LL | extern "gpu-kernel" {}
+   |        ^^^^^^^^^^^^
+
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
   --> $DIR/feature-gate-abi_gpu_kernel.rs:47:8
    |
@@ -68,58 +110,7 @@ LL | extern "gpu-kernel" {}
    = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-warning: the calling convention "gpu-kernel" is not supported on this target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:11
-   |
-LL | type A1 = extern "gpu-kernel" fn(_: ());
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:47:1
-   |
-LL | extern "gpu-kernel" {}
-   | ^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:16:1
-   |
-LL | extern "gpu-kernel" fn f1(_: ()) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:23:5
-   |
-LL |     extern "gpu-kernel" fn dm1(_: ()) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:31:5
-   |
-LL |     extern "gpu-kernel" fn m1(_: ()) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:5
-   |
-LL |     extern "gpu-kernel" fn im1(_: ()) {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 12 previous errors; 1 warning emitted
+error: aborting due to 14 previous errors
 
 Some errors have detailed explanations: E0570, E0658.
 For more information about an error, try `rustc --explain E0570`.
-Future incompatibility report: Future breakage diagnostic:
-warning: the calling convention "gpu-kernel" is not supported on this target
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:11
-   |
-LL | type A1 = extern "gpu-kernel" fn(_: ());
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
-   = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
-
diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.NVPTX.stderr b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.NVPTX.stderr
index fca32c5c1e6..4fa3fee942e 100644
--- a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.NVPTX.stderr
+++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.NVPTX.stderr
@@ -19,7 +19,7 @@ LL |     extern "gpu-kernel" fn m1(_: ());
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:23:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:24:12
    |
 LL |     extern "gpu-kernel" fn dm1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -29,7 +29,7 @@ LL |     extern "gpu-kernel" fn dm1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:31:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:32:12
    |
 LL |     extern "gpu-kernel" fn m1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -39,7 +39,7 @@ LL |     extern "gpu-kernel" fn m1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:37:12
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:38:12
    |
 LL |     extern "gpu-kernel" fn im1(_: ()) {}
    |            ^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL |     extern "gpu-kernel" fn im1(_: ()) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change
-  --> $DIR/feature-gate-abi_gpu_kernel.rs:42:18
+  --> $DIR/feature-gate-abi_gpu_kernel.rs:43:18
    |
 LL | type A1 = extern "gpu-kernel" fn(_: ());
    |                  ^^^^^^^^^^^^
diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs
index 7b1ee681dd7..988fbd83afc 100644
--- a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs
+++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs
@@ -19,6 +19,7 @@ extern "gpu-kernel" fn f1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental a
 // Methods in trait definition
 trait Tr {
     extern "gpu-kernel" fn m1(_: ()); //~ ERROR "gpu-kernel" ABI is experimental and subject to change
+    //[HOST]~^ ERROR is not a supported ABI
 
     extern "gpu-kernel" fn dm1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change
     //[HOST]~^ ERROR is not a supported ABI
@@ -40,8 +41,7 @@ impl S {
 
 // Function pointer types
 type A1 = extern "gpu-kernel" fn(_: ()); //~ ERROR "gpu-kernel" ABI is experimental and subject to change
-//[HOST]~^ WARNING the calling convention "gpu-kernel" is not supported on this target [unsupported_fn_ptr_calling_conventions]
-//[HOST]~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+//[HOST]~^ ERROR is not a supported ABI
 
 // Foreign modules
 extern "gpu-kernel" {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change
diff --git a/tests/ui/feature-gates/feature-gate-cfi_encoding.rs b/tests/ui/feature-gates/feature-gate-cfi_encoding.rs
index 3cef8156014..b6312dd7817 100644
--- a/tests/ui/feature-gates/feature-gate-cfi_encoding.rs
+++ b/tests/ui/feature-gates/feature-gate-cfi_encoding.rs
@@ -1,4 +1,4 @@
 #![crate_type = "lib"]
 
-#[cfi_encoding = "3Bar"] //~ERROR 3:1: 3:25: the `#[cfi_encoding]` attribute is an experimental feature [E0658]
+#[cfi_encoding = "3Bar"] //~ ERROR the `#[cfi_encoding]` attribute is an experimental feature [E0658]
 pub struct Foo(i32);
diff --git a/tests/ui/feature-gates/feature-gate-concat_idents.rs b/tests/ui/feature-gates/feature-gate-concat_idents.rs
deleted file mode 100644
index 4fc3b691597..00000000000
--- a/tests/ui/feature-gates/feature-gate-concat_idents.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-#![expect(deprecated)] // concat_idents is deprecated
-
-const XY_1: i32 = 10;
-
-fn main() {
-    const XY_2: i32 = 20;
-    let a = concat_idents!(X, Y_1); //~ ERROR `concat_idents` is not stable
-    let b = concat_idents!(X, Y_2); //~ ERROR `concat_idents` is not stable
-    assert_eq!(a, 10);
-    assert_eq!(b, 20);
-}
diff --git a/tests/ui/feature-gates/feature-gate-concat_idents.stderr b/tests/ui/feature-gates/feature-gate-concat_idents.stderr
deleted file mode 100644
index 6399424eecd..00000000000
--- a/tests/ui/feature-gates/feature-gate-concat_idents.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change
-  --> $DIR/feature-gate-concat_idents.rs:7:13
-   |
-LL |     let a = concat_idents!(X, Y_1);
-   |             ^^^^^^^^^^^^^
-   |
-   = note: see issue #29599 <https://github.com/rust-lang/rust/issues/29599> for more information
-   = help: add `#![feature(concat_idents)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change
-  --> $DIR/feature-gate-concat_idents.rs:8:13
-   |
-LL |     let b = concat_idents!(X, Y_2);
-   |             ^^^^^^^^^^^^^
-   |
-   = note: see issue #29599 <https://github.com/rust-lang/rust/issues/29599> for more information
-   = help: add `#![feature(concat_idents)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-concat_idents2.rs b/tests/ui/feature-gates/feature-gate-concat_idents2.rs
deleted file mode 100644
index bc2b4f7cddf..00000000000
--- a/tests/ui/feature-gates/feature-gate-concat_idents2.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-#![expect(deprecated)] // concat_idents is deprecated
-
-fn main() {
-    concat_idents!(a, b); //~ ERROR `concat_idents` is not stable enough
-                          //~| ERROR cannot find value `ab` in this scope
-}
diff --git a/tests/ui/feature-gates/feature-gate-concat_idents2.stderr b/tests/ui/feature-gates/feature-gate-concat_idents2.stderr
deleted file mode 100644
index a770c1a348b..00000000000
--- a/tests/ui/feature-gates/feature-gate-concat_idents2.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change
-  --> $DIR/feature-gate-concat_idents2.rs:4:5
-   |
-LL |     concat_idents!(a, b);
-   |     ^^^^^^^^^^^^^
-   |
-   = note: see issue #29599 <https://github.com/rust-lang/rust/issues/29599> for more information
-   = help: add `#![feature(concat_idents)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0425]: cannot find value `ab` in this scope
-  --> $DIR/feature-gate-concat_idents2.rs:4:5
-   |
-LL |     concat_idents!(a, b);
-   |     ^^^^^^^^^^^^^^^^^^^^ not found in this scope
-
-error: aborting due to 2 previous errors
-
-Some errors have detailed explanations: E0425, E0658.
-For more information about an error, try `rustc --explain E0425`.
diff --git a/tests/ui/feature-gates/feature-gate-concat_idents3.rs b/tests/ui/feature-gates/feature-gate-concat_idents3.rs
deleted file mode 100644
index d4a0d2e6bb0..00000000000
--- a/tests/ui/feature-gates/feature-gate-concat_idents3.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-#![expect(deprecated)] // concat_idents is deprecated
-
-const XY_1: i32 = 10;
-
-fn main() {
-    const XY_2: i32 = 20;
-    assert_eq!(10, concat_idents!(X, Y_1)); //~ ERROR `concat_idents` is not stable
-    assert_eq!(20, concat_idents!(X, Y_2)); //~ ERROR `concat_idents` is not stable
-}
diff --git a/tests/ui/feature-gates/feature-gate-concat_idents3.stderr b/tests/ui/feature-gates/feature-gate-concat_idents3.stderr
deleted file mode 100644
index 7d929322bc0..00000000000
--- a/tests/ui/feature-gates/feature-gate-concat_idents3.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change
-  --> $DIR/feature-gate-concat_idents3.rs:7:20
-   |
-LL |     assert_eq!(10, concat_idents!(X, Y_1));
-   |                    ^^^^^^^^^^^^^
-   |
-   = note: see issue #29599 <https://github.com/rust-lang/rust/issues/29599> for more information
-   = help: add `#![feature(concat_idents)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: use of unstable library feature `concat_idents`: `concat_idents` is not stable enough for use and is subject to change
-  --> $DIR/feature-gate-concat_idents3.rs:8:20
-   |
-LL |     assert_eq!(20, concat_idents!(X, Y_2));
-   |                    ^^^^^^^^^^^^^
-   |
-   = note: see issue #29599 <https://github.com/rust-lang/rust/issues/29599> for more information
-   = help: add `#![feature(concat_idents)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-coverage-attribute.rs b/tests/ui/feature-gates/feature-gate-coverage-attribute.rs
index 2cf4b76180e..0a463755f13 100644
--- a/tests/ui/feature-gates/feature-gate-coverage-attribute.rs
+++ b/tests/ui/feature-gates/feature-gate-coverage-attribute.rs
@@ -1,5 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![crate_type = "lib"]
 #![feature(no_coverage)] //~ ERROR feature has been removed [E0557]
 
diff --git a/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr b/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr
index 8c23544698d..68d0d9bc3c3 100644
--- a/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr
+++ b/tests/ui/feature-gates/feature-gate-coverage-attribute.stderr
@@ -1,14 +1,14 @@
 error[E0557]: feature has been removed
-  --> $DIR/feature-gate-coverage-attribute.rs:4:12
+  --> $DIR/feature-gate-coverage-attribute.rs:2:12
    |
 LL | #![feature(no_coverage)]
    |            ^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.74.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/114656> for more information
+   = note: removed in 1.74.0; see <https://github.com/rust-lang/rust/pull/114656> for more information
    = note: renamed to `coverage_attribute`
 
 error[E0658]: the `#[coverage]` attribute is an experimental feature
-  --> $DIR/feature-gate-coverage-attribute.rs:12:1
+  --> $DIR/feature-gate-coverage-attribute.rs:10:1
    |
 LL | #[coverage(off)]
    | ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/feature-gates/feature-gate-fn_align.rs b/tests/ui/feature-gates/feature-gate-fn_align.rs
index 744877704dd..b6c300e5cbe 100644
--- a/tests/ui/feature-gates/feature-gate-fn_align.rs
+++ b/tests/ui/feature-gates/feature-gate-fn_align.rs
@@ -1,9 +1,12 @@
 #![crate_type = "lib"]
 
-#[repr(align(16))] //~ ERROR `repr(align)` attributes on functions are unstable
+#[align(16)]
+//~^ ERROR the `#[align]` attribute is an experimental feature
 fn requires_alignment() {}
 
 trait MyTrait {
-    #[repr(align)] //~ ERROR invalid `repr(align)` attribute: `align` needs an argument
+    #[align]
+    //~^ ERROR the `#[align]` attribute is an experimental feature
+    //~| ERROR malformed `align` attribute input
     fn myfun();
 }
diff --git a/tests/ui/feature-gates/feature-gate-fn_align.stderr b/tests/ui/feature-gates/feature-gate-fn_align.stderr
index ff17c29fe02..921cf08435c 100644
--- a/tests/ui/feature-gates/feature-gate-fn_align.stderr
+++ b/tests/ui/feature-gates/feature-gate-fn_align.stderr
@@ -1,20 +1,33 @@
-error[E0589]: invalid `repr(align)` attribute: `align` needs an argument
-  --> $DIR/feature-gate-fn_align.rs:7:12
+error[E0658]: the `#[align]` attribute is an experimental feature
+  --> $DIR/feature-gate-fn_align.rs:3:1
    |
-LL |     #[repr(align)]
-   |            ^^^^^ help: supply an argument here: `align(...)`
+LL | #[align(16)]
+   | ^^^^^^^^^^^^
+   |
+   = note: see issue #82232 <https://github.com/rust-lang/rust/issues/82232> for more information
+   = help: add `#![feature(fn_align)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0658]: `repr(align)` attributes on functions are unstable
-  --> $DIR/feature-gate-fn_align.rs:3:8
+error[E0658]: the `#[align]` attribute is an experimental feature
+  --> $DIR/feature-gate-fn_align.rs:8:5
    |
-LL | #[repr(align(16))]
-   |        ^^^^^^^^^
+LL |     #[align]
+   |     ^^^^^^^^
    |
    = note: see issue #82232 <https://github.com/rust-lang/rust/issues/82232> for more information
    = help: add `#![feature(fn_align)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: aborting due to 2 previous errors
+error[E0539]: malformed `align` attribute input
+  --> $DIR/feature-gate-fn_align.rs:8:5
+   |
+LL |     #[align]
+   |     ^^^^^^^^
+   |     |
+   |     expected this to be a list
+   |     help: must be of the form: `#[align(<alignment in bytes>)]`
+
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0589, E0658.
-For more information about an error, try `rustc --explain E0589`.
+Some errors have detailed explanations: E0539, E0658.
+For more information about an error, try `rustc --explain E0539`.
diff --git a/tests/ui/feature-gates/feature-gate-loop-match.rs b/tests/ui/feature-gates/feature-gate-loop-match.rs
new file mode 100644
index 00000000000..399b20234f3
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-loop-match.rs
@@ -0,0 +1,30 @@
+// Test that `#[loop_match]` and `#[const_continue]` cannot be used without
+// `#![feature(loop_match)]`.
+
+enum State {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    let mut state = State::A;
+    #[loop_match] //~ ERROR the `#[loop_match]` attribute is an experimental feature
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    //~^ ERROR the `#[const_continue]` attribute is an experimental feature
+                    break 'blk State::B;
+                }
+                State::B => {
+                    #[const_continue]
+                    //~^ ERROR the `#[const_continue]` attribute is an experimental feature
+                    break 'blk State::C;
+                }
+                State::C => break 'a,
+            }
+        };
+    }
+}
diff --git a/tests/ui/feature-gates/feature-gate-loop-match.stderr b/tests/ui/feature-gates/feature-gate-loop-match.stderr
new file mode 100644
index 00000000000..9b12047cf4d
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-loop-match.stderr
@@ -0,0 +1,33 @@
+error[E0658]: the `#[loop_match]` attribute is an experimental feature
+  --> $DIR/feature-gate-loop-match.rs:12:5
+   |
+LL |     #[loop_match]
+   |     ^^^^^^^^^^^^^
+   |
+   = note: see issue #132306 <https://github.com/rust-lang/rust/issues/132306> for more information
+   = help: add `#![feature(loop_match)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the `#[const_continue]` attribute is an experimental feature
+  --> $DIR/feature-gate-loop-match.rs:17:21
+   |
+LL |                     #[const_continue]
+   |                     ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #132306 <https://github.com/rust-lang/rust/issues/132306> for more information
+   = help: add `#![feature(loop_match)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the `#[const_continue]` attribute is an experimental feature
+  --> $DIR/feature-gate-loop-match.rs:22:21
+   |
+LL |                     #[const_continue]
+   |                     ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #132306 <https://github.com/rust-lang/rust/issues/132306> for more information
+   = help: add `#![feature(loop_match)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr
index 8e601a14753..e57ec9cc59b 100644
--- a/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr
+++ b/tests/ui/feature-gates/feature-gate-naked_functions_target_feature.stderr
@@ -1,8 +1,8 @@
 error[E0658]: `#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions
-  --> $DIR/feature-gate-naked_functions_target_feature.rs:7:1
+  --> $DIR/feature-gate-naked_functions_target_feature.rs:7:3
    |
 LL | #[target_feature(enable = "avx2")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |   ^^^^^^^^^^^^^^
    |
    = note: see issue #138568 <https://github.com/rust-lang/rust/issues/138568> for more information
    = help: add `#![feature(naked_functions_target_feature)]` to the crate attributes to enable
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
index 77cc307c9f4..ed5a11270f8 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs
@@ -11,5 +11,5 @@ fn none() {}
 
 #[optimize(banana)]
 //~^ ERROR the `#[optimize]` attribute is an experimental feature
-//~| ERROR E0722
+//~| ERROR malformed `optimize` attribute input [E0539]
 fn not_known() {}
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
index 4e6e4ac2703..e7e62b4f989 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
@@ -38,13 +38,16 @@ LL | #[optimize(banana)]
    = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0722]: invalid argument
-  --> $DIR/feature-gate-optimize_attribute.rs:12:12
+error[E0539]: malformed `optimize` attribute input
+  --> $DIR/feature-gate-optimize_attribute.rs:12:1
    |
 LL | #[optimize(banana)]
-   |            ^^^^^^
+   | ^^^^^^^^^^^------^^
+   | |          |
+   | |          valid arguments are `size`, `speed` or `none`
+   | help: must be of the form: `#[optimize(size|speed|none)]`
 
 error: aborting due to 5 previous errors
 
-Some errors have detailed explanations: E0658, E0722.
-For more information about an error, try `rustc --explain E0658`.
+Some errors have detailed explanations: E0539, E0658.
+For more information about an error, try `rustc --explain E0539`.
diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
index 663a83a665c..7746654555d 100644
--- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
@@ -17,6 +17,10 @@ fn foo(mut x: Pin<&mut Foo>) {
     let _y: &pin mut Foo = x; //~ ERROR pinned reference syntax is experimental
 }
 
+fn foo_const(x: Pin<&Foo>) {
+    let _y: &pin const Foo = x; //~ ERROR pinned reference syntax is experimental
+}
+
 fn foo_sugar(_: &pin mut Foo) {} //~ ERROR pinned reference syntax is experimental
 
 fn bar(x: Pin<&mut Foo>) {
@@ -31,6 +35,18 @@ fn baz(mut x: Pin<&mut Foo>) {
 
 fn baz_sugar(_: &pin const Foo) {} //~ ERROR pinned reference syntax is experimental
 
+fn borrows() {
+    let mut x: Pin<&mut _> = &pin mut Foo; //~ ERROR pinned reference syntax is experimental
+    foo(x.as_mut());
+    foo(x.as_mut());
+    foo_const(x.as_ref());
+
+    let x: Pin<&_> = &pin const Foo; //~ ERROR pinned reference syntax is experimental
+
+    foo_const(x);
+    foo_const(x);
+}
+
 #[cfg(any())]
 mod not_compiled {
     use std::pin::Pin;
@@ -63,6 +79,18 @@ mod not_compiled {
     }
 
     fn baz_sugar(_: &pin const Foo) {} //~ ERROR pinned reference syntax is experimental
+
+    fn borrows() {
+        let mut x: Pin<&mut _> = &pin mut Foo; //~ ERROR pinned reference syntax is experimental
+        foo(x.as_mut());
+        foo(x.as_mut());
+        foo_const(x.as_ref());
+
+        let x: Pin<&_> = &pin const Foo; //~ ERROR pinned reference syntax is experimental
+
+        foo_const(x);
+        foo_const(x);
+    }
 }
 
 fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
index 8ed7543d86e..a8890254fac 100644
--- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
@@ -29,7 +29,17 @@ LL |     let _y: &pin mut Foo = x;
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:20:18
+  --> $DIR/feature-gate-pin_ergonomics.rs:21:14
+   |
+LL |     let _y: &pin const Foo = x;
+   |              ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:24:18
    |
 LL | fn foo_sugar(_: &pin mut Foo) {}
    |                  ^^^
@@ -39,7 +49,7 @@ LL | fn foo_sugar(_: &pin mut Foo) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:32:18
+  --> $DIR/feature-gate-pin_ergonomics.rs:36:18
    |
 LL | fn baz_sugar(_: &pin const Foo) {}
    |                  ^^^
@@ -49,7 +59,27 @@ LL | fn baz_sugar(_: &pin const Foo) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:43:23
+  --> $DIR/feature-gate-pin_ergonomics.rs:39:31
+   |
+LL |     let mut x: Pin<&mut _> = &pin mut Foo;
+   |                               ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:44:23
+   |
+LL |     let x: Pin<&_> = &pin const Foo;
+   |                       ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:59:23
    |
 LL |         fn foo_sugar(&pin mut self) {}
    |                       ^^^
@@ -59,7 +89,7 @@ LL |         fn foo_sugar(&pin mut self) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:44:29
+  --> $DIR/feature-gate-pin_ergonomics.rs:60:29
    |
 LL |         fn foo_sugar_const(&pin const self) {}
    |                             ^^^
@@ -69,7 +99,7 @@ LL |         fn foo_sugar_const(&pin const self) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:50:18
+  --> $DIR/feature-gate-pin_ergonomics.rs:66:18
    |
 LL |         let _y: &pin mut Foo = x;
    |                  ^^^
@@ -79,7 +109,7 @@ LL |         let _y: &pin mut Foo = x;
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:53:22
+  --> $DIR/feature-gate-pin_ergonomics.rs:69:22
    |
 LL |     fn foo_sugar(_: &pin mut Foo) {}
    |                      ^^^
@@ -89,7 +119,7 @@ LL |     fn foo_sugar(_: &pin mut Foo) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:65:22
+  --> $DIR/feature-gate-pin_ergonomics.rs:81:22
    |
 LL |     fn baz_sugar(_: &pin const Foo) {}
    |                      ^^^
@@ -98,8 +128,28 @@ LL |     fn baz_sugar(_: &pin const Foo) {}
    = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:84:35
+   |
+LL |         let mut x: Pin<&mut _> = &pin mut Foo;
+   |                                   ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:89:27
+   |
+LL |         let x: Pin<&_> = &pin const Foo;
+   |                           ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
 error[E0382]: use of moved value: `x`
-  --> $DIR/feature-gate-pin_ergonomics.rs:24:9
+  --> $DIR/feature-gate-pin_ergonomics.rs:28:9
    |
 LL | fn bar(x: Pin<&mut Foo>) {
    |        - move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
@@ -117,7 +167,7 @@ LL | fn foo(mut x: Pin<&mut Foo>) {
    |    in this function
 
 error[E0382]: use of moved value: `x`
-  --> $DIR/feature-gate-pin_ergonomics.rs:29:5
+  --> $DIR/feature-gate-pin_ergonomics.rs:33:5
    |
 LL | fn baz(mut x: Pin<&mut Foo>) {
    |        ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
@@ -136,7 +186,7 @@ help: consider reborrowing the `Pin` instead of moving it
 LL |     x.as_mut().foo();
    |      +++++++++
 
-error: aborting due to 12 previous errors
+error: aborting due to 17 previous errors
 
 Some errors have detailed explanations: E0382, E0658.
 For more information about an error, try `rustc --explain E0382`.
diff --git a/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.rs b/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.rs
index b5fbcc9ccf8..c1469863792 100644
--- a/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.rs
+++ b/tests/ui/feature-gates/feature-gate-unsized_tuple_coercion.rs
@@ -1,4 +1,4 @@
 fn main() {
     let _ : &(dyn Send,) = &((),);
-    //~^ ERROR 2:28: 2:34: mismatched types [E0308]
+    //~^ ERROR mismatched types [E0308]
 }
diff --git a/tests/ui/feature-gates/gated-bad-feature.rs b/tests/ui/feature-gates/gated-bad-feature.rs
index 3114f661dc5..51f2db5556e 100644
--- a/tests/ui/feature-gates/gated-bad-feature.rs
+++ b/tests/ui/feature-gates/gated-bad-feature.rs
@@ -1,4 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
 #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
 //~^ ERROR malformed `feature`
 //~| ERROR malformed `feature`
diff --git a/tests/ui/feature-gates/gated-bad-feature.stderr b/tests/ui/feature-gates/gated-bad-feature.stderr
index 0e75dff14f8..e0e84d84235 100644
--- a/tests/ui/feature-gates/gated-bad-feature.stderr
+++ b/tests/ui/feature-gates/gated-bad-feature.stderr
@@ -1,43 +1,43 @@
 error[E0556]: malformed `feature` attribute input
-  --> $DIR/gated-bad-feature.rs:2:25
+  --> $DIR/gated-bad-feature.rs:1:25
    |
 LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
    |                         ^^^^^^^^ help: expected just one word: `foo`
 
 error[E0556]: malformed `feature` attribute input
-  --> $DIR/gated-bad-feature.rs:2:35
+  --> $DIR/gated-bad-feature.rs:1:35
    |
 LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
    |                                   ^^^^^^^^^^^ help: expected just one word: `foo`
 
 error[E0557]: feature has been removed
-  --> $DIR/gated-bad-feature.rs:9:12
+  --> $DIR/gated-bad-feature.rs:8:12
    |
 LL | #![feature(test_removed_feature)]
    |            ^^^^^^^^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.0.0 (you are using $RUSTC_VERSION)
+   = note: removed in 1.0.0
 
 error: malformed `feature` attribute input
-  --> $DIR/gated-bad-feature.rs:7:1
+  --> $DIR/gated-bad-feature.rs:6:1
    |
 LL | #![feature]
    | ^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name2, ...)]`
 
 error: malformed `feature` attribute input
-  --> $DIR/gated-bad-feature.rs:8:1
+  --> $DIR/gated-bad-feature.rs:7:1
    |
 LL | #![feature = "foo"]
    | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![feature(name1, name2, ...)]`
 
 error[E0635]: unknown feature `foo_bar_baz`
-  --> $DIR/gated-bad-feature.rs:2:12
+  --> $DIR/gated-bad-feature.rs:1:12
    |
 LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
    |            ^^^^^^^^^^^
 
 error[E0635]: unknown feature `foo`
-  --> $DIR/gated-bad-feature.rs:2:48
+  --> $DIR/gated-bad-feature.rs:1:48
    |
 LL | #![feature(foo_bar_baz, foo(bar), foo = "baz", foo)]
    |                                                ^^^
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
index 1c6868dc95d..d2b1d71ab87 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
@@ -379,14 +379,6 @@ warning: `#[proc_macro_derive]` only has an effect on functions
 LL | #![proc_macro_derive()]
    | ^^^^^^^^^^^^^^^^^^^^^^^
 
-warning: attribute should be applied to a function definition
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1
-   |
-LL | #![cold]
-   | ^^^^^^^^ cannot be applied to crates
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-
 warning: attribute should be applied to an `extern` block with non-Rust ABI
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:64:1
    |
@@ -411,6 +403,14 @@ LL | #![link_section = "1800"]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
+warning: attribute should be applied to a function definition
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1
+   |
+LL | #![cold]
+   | ^^^^^^^^ cannot be applied to crates
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
 warning: `#[must_use]` has no effect when applied to a module
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:72:1
    |
diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs
index ec6adb471ba..d8c5f48f9fd 100644
--- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs
+++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs
@@ -1,5 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![feature(external_doc)] //~ ERROR feature has been removed
 #![doc(include("README.md"))] //~ ERROR unknown `doc` attribute `include`
 
diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr
index 43205c7360b..bd8c56c61c3 100644
--- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr
+++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr
@@ -1,14 +1,14 @@
 error[E0557]: feature has been removed
-  --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:3:12
+  --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:1:12
    |
 LL | #![feature(external_doc)]
    |            ^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.54.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/85457> for more information
+   = note: removed in 1.54.0; see <https://github.com/rust-lang/rust/pull/85457> for more information
    = note: use #[doc = include_str!("filename")] instead, which handles macro invocations
 
 error: unknown `doc` attribute `include`
-  --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:4:8
+  --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:2:8
    |
 LL | #![doc(include("README.md"))]
    |        ^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/fmt/format-args-argument-span.stderr b/tests/ui/fmt/format-args-argument-span.stderr
index 4e2702383d6..d46cfb438cf 100644
--- a/tests/ui/fmt/format-args-argument-span.stderr
+++ b/tests/ui/fmt/format-args-argument-span.stderr
@@ -12,7 +12,9 @@ error[E0277]: `Option<{integer}>` doesn't implement `std::fmt::Display`
   --> $DIR/format-args-argument-span.rs:15:37
    |
 LL |     println!("{x:?} {x} {x:?}", x = Some(1));
-   |                                     ^^^^^^^ `Option<{integer}>` cannot be formatted with the default formatter
+   |                     ---             ^^^^^^^ `Option<{integer}>` cannot be formatted with the default formatter
+   |                     |
+   |                     required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `Option<{integer}>`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
@@ -22,7 +24,7 @@ error[E0277]: `DisplayOnly` doesn't implement `Debug`
   --> $DIR/format-args-argument-span.rs:18:19
    |
 LL |     println!("{x} {x:?} {x}");
-   |                   ^^^^^ `DisplayOnly` cannot be formatted using `{:?}`
+   |                   ^^^^^ `DisplayOnly` cannot be formatted using `{:?}` because it doesn't implement `Debug`
    |
    = help: the trait `Debug` is not implemented for `DisplayOnly`
    = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly`
@@ -37,7 +39,9 @@ error[E0277]: `DisplayOnly` doesn't implement `Debug`
   --> $DIR/format-args-argument-span.rs:20:35
    |
 LL |     println!("{x} {x:?} {x}", x = DisplayOnly);
-   |                                   ^^^^^^^^^^^ `DisplayOnly` cannot be formatted using `{:?}`
+   |                   -----           ^^^^^^^^^^^ `DisplayOnly` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |                   |
+   |                   required by this formatting parameter
    |
    = help: the trait `Debug` is not implemented for `DisplayOnly`
    = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly`
diff --git a/tests/ui/fmt/ifmt-unimpl.stderr b/tests/ui/fmt/ifmt-unimpl.stderr
index b8d4425a4a7..5e80f892dcb 100644
--- a/tests/ui/fmt/ifmt-unimpl.stderr
+++ b/tests/ui/fmt/ifmt-unimpl.stderr
@@ -4,7 +4,7 @@ error[E0277]: the trait bound `str: UpperHex` is not satisfied
 LL |     format!("{:X}", "3");
    |              ----   ^^^ the trait `UpperHex` is not implemented for `str`
    |              |
-   |              required by a bound introduced by this call
+   |              required by this formatting parameter
    |
    = help: the following other types implement trait `UpperHex`:
              &T
@@ -17,8 +17,6 @@ LL |     format!("{:X}", "3");
              i32
            and 9 others
    = note: required for `&str` to implement `UpperHex`
-note: required by a bound in `core::fmt::rt::Argument::<'_>::new_upper_hex`
-  --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL
    = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/fmt/non-source-literals.rs b/tests/ui/fmt/non-source-literals.rs
new file mode 100644
index 00000000000..e3ffdb40a6b
--- /dev/null
+++ b/tests/ui/fmt/non-source-literals.rs
@@ -0,0 +1,13 @@
+/// Do not point at the format string if it wasn't written in the source.
+//@ forbid-output: required by this formatting parameter
+
+#[derive(Debug)]
+pub struct NonDisplay;
+pub struct NonDebug;
+
+fn main() {
+    let _ = format!(concat!("{", "}"), NonDisplay); //~ ERROR
+    let _ = format!(concat!("{", "0", "}"), NonDisplay); //~ ERROR
+    let _ = format!(concat!("{:", "?}"), NonDebug); //~ ERROR
+    let _ = format!(concat!("{", "0", ":?}"), NonDebug); //~ ERROR
+}
diff --git a/tests/ui/fmt/non-source-literals.stderr b/tests/ui/fmt/non-source-literals.stderr
new file mode 100644
index 00000000000..5f8a6200dab
--- /dev/null
+++ b/tests/ui/fmt/non-source-literals.stderr
@@ -0,0 +1,53 @@
+error[E0277]: `NonDisplay` doesn't implement `std::fmt::Display`
+  --> $DIR/non-source-literals.rs:9:40
+   |
+LL |     let _ = format!(concat!("{", "}"), NonDisplay);
+   |                                        ^^^^^^^^^^ `NonDisplay` cannot be formatted with the default formatter
+   |
+   = help: the trait `std::fmt::Display` is not implemented for `NonDisplay`
+   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0277]: `NonDisplay` doesn't implement `std::fmt::Display`
+  --> $DIR/non-source-literals.rs:10:45
+   |
+LL |     let _ = format!(concat!("{", "0", "}"), NonDisplay);
+   |                                             ^^^^^^^^^^ `NonDisplay` cannot be formatted with the default formatter
+   |
+   = help: the trait `std::fmt::Display` is not implemented for `NonDisplay`
+   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0277]: `NonDebug` doesn't implement `Debug`
+  --> $DIR/non-source-literals.rs:11:42
+   |
+LL |     let _ = format!(concat!("{:", "?}"), NonDebug);
+   |                                          ^^^^^^^^ `NonDebug` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |
+   = help: the trait `Debug` is not implemented for `NonDebug`
+   = note: add `#[derive(Debug)]` to `NonDebug` or manually `impl Debug for NonDebug`
+   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider annotating `NonDebug` with `#[derive(Debug)]`
+   |
+LL + #[derive(Debug)]
+LL | pub struct NonDebug;
+   |
+
+error[E0277]: `NonDebug` doesn't implement `Debug`
+  --> $DIR/non-source-literals.rs:12:47
+   |
+LL |     let _ = format!(concat!("{", "0", ":?}"), NonDebug);
+   |                                               ^^^^^^^^ `NonDebug` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |
+   = help: the trait `Debug` is not implemented for `NonDebug`
+   = note: add `#[derive(Debug)]` to `NonDebug` or manually `impl Debug for NonDebug`
+   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider annotating `NonDebug` with `#[derive(Debug)]`
+   |
+LL + #[derive(Debug)]
+LL | pub struct NonDebug;
+   |
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/generic-associated-types/generic-associated-types-where.stderr b/tests/ui/generic-associated-types/generic-associated-types-where.stderr
index 7dce34650d7..637f86f7bec 100644
--- a/tests/ui/generic-associated-types/generic-associated-types-where.stderr
+++ b/tests/ui/generic-associated-types/generic-associated-types-where.stderr
@@ -2,9 +2,8 @@ error[E0277]: `T` doesn't implement `std::fmt::Display`
   --> $DIR/generic-associated-types-where.rs:18:22
    |
 LL |     type Assoc2<T> = Vec<T>;
-   |                      ^^^^^^ `T` cannot be formatted with the default formatter
+   |                      ^^^^^^ the trait `std::fmt::Display` is not implemented for `T`
    |
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 help: consider restricting type parameter `T` with trait `Display`
    |
 LL |     type Assoc2<T: std::fmt::Display> = Vec<T>;
diff --git a/tests/ui/generics/default-type-params-well-formedness.rs b/tests/ui/generics/default-type-params-well-formedness.rs
new file mode 100644
index 00000000000..22b8f5011f7
--- /dev/null
+++ b/tests/ui/generics/default-type-params-well-formedness.rs
@@ -0,0 +1,50 @@
+//! Test for well-formedness checking of default type parameters.
+//!
+//! Regression Test for: https://github.com/rust-lang/rust/issues/49344
+
+//@ run-pass
+
+#![allow(dead_code)]
+
+trait Trait<T> {}
+struct Foo<U, V = i32>(U, V)
+where
+    U: Trait<V>;
+
+trait Marker {}
+struct TwoParams<T, U>(T, U);
+impl Marker for TwoParams<i32, i32> {}
+
+// Clauses with more than 1 param are not checked.
+struct IndividuallyBogus<T = i32, U = i32>(TwoParams<T, U>)
+where
+    TwoParams<T, U>: Marker;
+
+struct BogusTogether<T = u32, U = i32>(T, U)
+where
+    TwoParams<T, U>: Marker;
+
+// Clauses with non-defaulted params are not checked.
+struct NonDefaultedInClause<T, U = i32>(TwoParams<T, U>)
+where
+    TwoParams<T, U>: Marker;
+
+struct DefaultedLhs<U, V = i32>(U, V)
+where
+    V: Trait<U>;
+
+// Dependent defaults are not checked.
+struct Dependent<T, U = T>(T, U)
+where
+    U: Copy;
+
+trait SelfBound<T: Copy = Self> {}
+
+// Not even for well-formedness.
+struct WellFormedProjection<A, T = <A as Iterator>::Item>(A, T);
+
+// Issue #49344, predicates with lifetimes should not be checked.
+trait Scope<'a> {}
+struct Request<'a, S: Scope<'a> = i32>(S, &'a ());
+
+fn main() {}
diff --git a/tests/ui/hygiene/no_implicit_prelude.stderr b/tests/ui/hygiene/no_implicit_prelude.stderr
index 5de6e3db327..42049da23eb 100644
--- a/tests/ui/hygiene/no_implicit_prelude.stderr
+++ b/tests/ui/hygiene/no_implicit_prelude.stderr
@@ -23,8 +23,6 @@ LL |         ().clone()
    |            ^^^^^
    |
    = help: items from traits can only be used if the trait is in scope
-help: there is a method `clone_from` with a similar name, but with different arguments
-  --> $SRC_DIR/core/src/clone.rs:LL:COL
    = note: this error originates in the macro `::bar::m` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: trait `Clone` which provides `clone` is implemented but not in scope; perhaps you want to import it
    |
diff --git a/tests/ui/impl-header-lifetime-elision/assoc-type.rs b/tests/ui/impl-header-lifetime-elision/assoc-type.rs
index db3c416540f..14b2ea647f1 100644
--- a/tests/ui/impl-header-lifetime-elision/assoc-type.rs
+++ b/tests/ui/impl-header-lifetime-elision/assoc-type.rs
@@ -9,7 +9,7 @@ trait MyTrait {
 
 impl MyTrait for &i32 {
     type Output = &i32;
-    //~^ ERROR 11:19: 11:20: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
+    //~^ ERROR in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
 }
 
 impl MyTrait for &u32 {
diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed b/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed
index 886fc1d0058..d8eceeff678 100644
--- a/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed
+++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed
@@ -1,5 +1,6 @@
 //@ run-rustfix
 
+#![allow(dead_code)]
 struct S<T>(T);
 struct S2;
 
diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.rs b/tests/ui/impl-trait/extra-impl-in-trait-impl.rs
index f3271993867..c2e511c0d05 100644
--- a/tests/ui/impl-trait/extra-impl-in-trait-impl.rs
+++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.rs
@@ -1,5 +1,6 @@
 //@ run-rustfix
 
+#![allow(dead_code)]
 struct S<T>(T);
 struct S2;
 
diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr b/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr
index 5aafc8b64d4..22e68463a8c 100644
--- a/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr
+++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr
@@ -1,23 +1,23 @@
 error: unexpected `impl` keyword
-  --> $DIR/extra-impl-in-trait-impl.rs:6:18
+  --> $DIR/extra-impl-in-trait-impl.rs:7:18
    |
 LL | impl<T: Default> impl Default for S<T> {
    |                  ^^^^^ help: remove the extra `impl`
    |
 note: this is parsed as an `impl Trait` type, but a trait is expected at this position
-  --> $DIR/extra-impl-in-trait-impl.rs:6:18
+  --> $DIR/extra-impl-in-trait-impl.rs:7:18
    |
 LL | impl<T: Default> impl Default for S<T> {
    |                  ^^^^^^^^^^^^
 
 error: unexpected `impl` keyword
-  --> $DIR/extra-impl-in-trait-impl.rs:12:6
+  --> $DIR/extra-impl-in-trait-impl.rs:13:6
    |
 LL | impl impl Default for S2 {
    |      ^^^^^ help: remove the extra `impl`
    |
 note: this is parsed as an `impl Trait` type, but a trait is expected at this position
-  --> $DIR/extra-impl-in-trait-impl.rs:12:6
+  --> $DIR/extra-impl-in-trait-impl.rs:13:6
    |
 LL | impl impl Default for S2 {
    |      ^^^^^^^^^^^^
diff --git a/tests/ui/impl-trait/in-bindings/lifetime-equality.rs b/tests/ui/impl-trait/in-bindings/lifetime-equality.rs
new file mode 100644
index 00000000000..6cf48dccc7d
--- /dev/null
+++ b/tests/ui/impl-trait/in-bindings/lifetime-equality.rs
@@ -0,0 +1,19 @@
+//@ check-pass
+
+#![feature(impl_trait_in_bindings)]
+
+// A test for #61773 which would have been difficult to support if we
+// were to represent `impl_trait_in_bindings` using opaque types.
+
+trait Trait<'a, 'b> { }
+impl<T> Trait<'_, '_> for T { }
+
+
+fn bar<'a, 'b>(data0: &'a u32, data1: &'b u32) {
+  let x: impl Trait<'_, '_> = (data0, data1);
+  force_equal(x);
+}
+
+fn force_equal<'a>(t: impl Trait<'a, 'a>) { }
+
+fn main() { }
diff --git a/tests/ui/impl-trait/in-bindings/region-lifetimes.rs b/tests/ui/impl-trait/in-bindings/region-lifetimes.rs
new file mode 100644
index 00000000000..189ab85a276
--- /dev/null
+++ b/tests/ui/impl-trait/in-bindings/region-lifetimes.rs
@@ -0,0 +1,17 @@
+//@ check-pass
+
+#![feature(impl_trait_in_bindings)]
+
+// A test for #61773 which would have been difficult to support if we
+// were to represent `impl_trait_in_bindings` using opaque types.
+
+trait Foo<'a> { }
+impl Foo<'_> for &u32 { }
+
+fn bar<'a>(data: &'a u32) {
+  let x: impl Foo<'_> = data;
+}
+
+fn main() {
+  let _: impl Foo<'_> = &44;
+}
diff --git a/tests/ui/impl-trait/in-trait/doesnt-satisfy.stderr b/tests/ui/impl-trait/in-trait/doesnt-satisfy.stderr
index 119195f17ff..df89ed9f3b5 100644
--- a/tests/ui/impl-trait/in-trait/doesnt-satisfy.stderr
+++ b/tests/ui/impl-trait/in-trait/doesnt-satisfy.stderr
@@ -2,10 +2,8 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
   --> $DIR/doesnt-satisfy.rs:6:17
    |
 LL |     fn bar() -> () {}
-   |                 ^^ `()` cannot be formatted with the default formatter
+   |                 ^^ the trait `std::fmt::Display` is not implemented for `()`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `()`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `Foo::bar::{anon_assoc#0}`
   --> $DIR/doesnt-satisfy.rs:2:22
    |
diff --git a/tests/ui/impl-trait/in-trait/wf-bounds.stderr b/tests/ui/impl-trait/in-trait/wf-bounds.stderr
index 634557094ce..40a029cdc92 100644
--- a/tests/ui/impl-trait/in-trait/wf-bounds.stderr
+++ b/tests/ui/impl-trait/in-trait/wf-bounds.stderr
@@ -39,9 +39,8 @@ error[E0277]: `T` doesn't implement `std::fmt::Display`
   --> $DIR/wf-bounds.rs:21:26
    |
 LL |     fn nya4<T>() -> impl Wf<NeedsDisplay<T>>;
-   |                          ^^^^^^^^^^^^^^^^^^^ `T` cannot be formatted with the default formatter
+   |                          ^^^^^^^^^^^^^^^^^^^ the trait `std::fmt::Display` is not implemented for `T`
    |
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `NeedsDisplay`
   --> $DIR/wf-bounds.rs:9:24
    |
diff --git a/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr b/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
index cd9ed0fb885..1dcd800cfc5 100644
--- a/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
+++ b/tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
@@ -8,7 +8,7 @@ LL |     x.push(0);
    |     ^^^^^^^^^ mutable borrow occurs here
 ...
 LL |     println!("{h}");
-   |               --- immutable borrow later used here
+   |                - immutable borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/foreign-2021.rs:7:13
diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
index 676b6c12f52..aa0f6400091 100644
--- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr
+++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
@@ -23,7 +23,7 @@ LL |     x.push(1);
    |     ^^^^^^^^^ mutable borrow occurs here
 ...
 LL |     println!("{a}");
-   |               --- immutable borrow later used here
+   |                - immutable borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:16:13
@@ -99,7 +99,7 @@ LL |     x.push(1);
    |     ^ second mutable borrow occurs here
 ...
 LL |     println!("{a}");
-   |               --- first borrow later used here
+   |                - first borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:63:13
@@ -175,7 +175,7 @@ LL |     s.f = 1;
    |     ^^^^^^^ `s.f` is assigned to here but it was already borrowed
 ...
 LL |     println!("{a}");
-   |               --- borrow later used here
+   |                - borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:112:13
@@ -197,7 +197,7 @@ LL |     s.f = 1;
    |     ^^^^^^^ `s.f` is assigned to here but it was already borrowed
 ...
 LL |     println!("{a}");
-   |               --- borrow later used here
+   |                - borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:128:13
@@ -219,7 +219,7 @@ LL |     s.f;
    |     ^^^ use of borrowed `s.f`
 ...
 LL |     println!("{a}");
-   |               --- borrow later used here
+   |                - borrow later used here
    |
 note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
   --> $DIR/migration-note.rs:140:13
diff --git a/tests/ui/imports/issue-28134.rs b/tests/ui/imports/issue-28134.rs
index 70d3a327c1a..aef2fe8facd 100644
--- a/tests/ui/imports/issue-28134.rs
+++ b/tests/ui/imports/issue-28134.rs
@@ -2,4 +2,4 @@
 
 #![allow(soft_unstable)]
 #![test]
-//~^ ERROR 4:1: 4:9: `test` attribute cannot be used at crate level
+//~^ ERROR `test` attribute cannot be used at crate level
diff --git a/tests/ui/imports/multiple-extern-by-macro-for-underscore.stderr b/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2015.stderr
index 1da5aa87070..985cd654c39 100644
--- a/tests/ui/imports/multiple-extern-by-macro-for-underscore.stderr
+++ b/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2015.stderr
@@ -1,5 +1,5 @@
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/multiple-extern-by-macro-for-underscore.rs:16:11
+  --> $DIR/multiple-extern-by-macro-for-underscore.rs:18:11
    |
 LL |     use ::_;
    |           ^ expected identifier, found reserved identifier
diff --git a/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2021.stderr b/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2021.stderr
new file mode 100644
index 00000000000..985cd654c39
--- /dev/null
+++ b/tests/ui/imports/multiple-extern-by-macro-for-underscore.ed2021.stderr
@@ -0,0 +1,8 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-extern-by-macro-for-underscore.rs:18:11
+   |
+LL |     use ::_;
+   |           ^ expected identifier, found reserved identifier
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/imports/multiple-extern-by-macro-for-underscore.rs b/tests/ui/imports/multiple-extern-by-macro-for-underscore.rs
index ddf735d8947..ab877e06246 100644
--- a/tests/ui/imports/multiple-extern-by-macro-for-underscore.rs
+++ b/tests/ui/imports/multiple-extern-by-macro-for-underscore.rs
@@ -1,4 +1,6 @@
-//@ edition: 2021
+//@ revisions: ed2015 ed2021
+//@[ed2015] edition: 2015
+//@[ed2021] edition: 2021
 
 // issue#128813
 
diff --git a/tests/ui/inference/hint-closure-signature-119266.rs b/tests/ui/inference/hint-closure-signature-119266.rs
index 35be600fd6a..6e136c57cca 100644
--- a/tests/ui/inference/hint-closure-signature-119266.rs
+++ b/tests/ui/inference/hint-closure-signature-119266.rs
@@ -3,7 +3,7 @@ fn main() {
     //~^ NOTE: the found closure
 
     let x: fn(i32) = x;
-    //~^ ERROR: 5:22: 5:23: mismatched types [E0308]
+    //~^ ERROR: mismatched types [E0308]
     //~| NOTE: incorrect number of function parameters
     //~| NOTE: expected due to this
     //~| NOTE: expected fn pointer `fn(i32)`
diff --git a/tests/ui/integral-indexing.rs b/tests/ui/integral-indexing.rs
index f076dfcb0a4..e20553af8a2 100644
--- a/tests/ui/integral-indexing.rs
+++ b/tests/ui/integral-indexing.rs
@@ -3,14 +3,14 @@ pub fn main() {
     let s: String = "abcdef".to_string();
     v[3_usize];
     v[3];
-    v[3u8];  //~ERROR : the type `[isize]` cannot be indexed by `u8`
-    v[3i8];  //~ERROR : the type `[isize]` cannot be indexed by `i8`
-    v[3u32]; //~ERROR : the type `[isize]` cannot be indexed by `u32`
-    v[3i32]; //~ERROR : the type `[isize]` cannot be indexed by `i32`
+    v[3u8];  //~ ERROR the type `[isize]` cannot be indexed by `u8`
+    v[3i8];  //~ ERROR the type `[isize]` cannot be indexed by `i8`
+    v[3u32]; //~ ERROR the type `[isize]` cannot be indexed by `u32`
+    v[3i32]; //~ ERROR the type `[isize]` cannot be indexed by `i32`
     s.as_bytes()[3_usize];
     s.as_bytes()[3];
-    s.as_bytes()[3u8];  //~ERROR : the type `[u8]` cannot be indexed by `u8`
-    s.as_bytes()[3i8];  //~ERROR : the type `[u8]` cannot be indexed by `i8`
-    s.as_bytes()[3u32]; //~ERROR : the type `[u8]` cannot be indexed by `u32`
-    s.as_bytes()[3i32]; //~ERROR : the type `[u8]` cannot be indexed by `i32`
+    s.as_bytes()[3u8];  //~ ERROR the type `[u8]` cannot be indexed by `u8`
+    s.as_bytes()[3i8];  //~ ERROR the type `[u8]` cannot be indexed by `i8`
+    s.as_bytes()[3u32]; //~ ERROR the type `[u8]` cannot be indexed by `u32`
+    s.as_bytes()[3i32]; //~ ERROR the type `[u8]` cannot be indexed by `i32`
 }
diff --git a/tests/ui/issues/issue-32950.rs b/tests/ui/issues/issue-32950.rs
deleted file mode 100644
index b51ac296776..00000000000
--- a/tests/ui/issues/issue-32950.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-#![feature(concat_idents)]
-#![expect(deprecated)] // concat_idents is deprecated
-
-#[derive(Debug)]
-struct Baz<T>(
-    concat_idents!(Foo, Bar) //~ ERROR `derive` cannot be used on items with type macros
-                             //~^ ERROR cannot find type `FooBar` in this scope
-);
-
-fn main() {}
diff --git a/tests/ui/issues/issue-32950.stderr b/tests/ui/issues/issue-32950.stderr
deleted file mode 100644
index 38a82542f89..00000000000
--- a/tests/ui/issues/issue-32950.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: `derive` cannot be used on items with type macros
-  --> $DIR/issue-32950.rs:6:5
-   |
-LL |     concat_idents!(Foo, Bar)
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0412]: cannot find type `FooBar` in this scope
-  --> $DIR/issue-32950.rs:6:5
-   |
-LL |     concat_idents!(Foo, Bar)
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0412`.
diff --git a/tests/ui/issues/issue-43988.stderr b/tests/ui/issues/issue-43988.stderr
index bd4eb8bbed3..fe61e136a51 100644
--- a/tests/ui/issues/issue-43988.stderr
+++ b/tests/ui/issues/issue-43988.stderr
@@ -38,7 +38,7 @@ LL |     #[repr]
    |     ^^^^^^^
    |     |
    |     expected this to be a list
-   |     help: must be of the form: `#[repr(C)]`
+   |     help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error[E0539]: malformed `inline` attribute input
   --> $DIR/issue-43988.rs:30:5
@@ -64,7 +64,7 @@ LL |     let _z = #[repr] 1;
    |              ^^^^^^^
    |              |
    |              expected this to be a list
-   |              help: must be of the form: `#[repr(C)]`
+   |              help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error[E0518]: attribute should be applied to function or closure
   --> $DIR/issue-43988.rs:5:5
diff --git a/tests/ui/issues/issue-50403.rs b/tests/ui/issues/issue-50403.rs
deleted file mode 100644
index f14958afc34..00000000000
--- a/tests/ui/issues/issue-50403.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-#![feature(concat_idents)]
-#![expect(deprecated)] // concat_idents is deprecated
-
-fn main() {
-    let x = concat_idents!(); //~ ERROR `concat_idents!()` takes 1 or more arguments
-}
diff --git a/tests/ui/issues/issue-50403.stderr b/tests/ui/issues/issue-50403.stderr
deleted file mode 100644
index e7dd05bb018..00000000000
--- a/tests/ui/issues/issue-50403.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: `concat_idents!()` takes 1 or more arguments
-  --> $DIR/issue-50403.rs:5:13
-   |
-LL |     let x = concat_idents!();
-   |             ^^^^^^^^^^^^^^^^
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/issues/issue-59488.stderr b/tests/ui/issues/issue-59488.stderr
index ac8862716c0..b6611ad63a8 100644
--- a/tests/ui/issues/issue-59488.stderr
+++ b/tests/ui/issues/issue-59488.stderr
@@ -87,18 +87,16 @@ error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug`
   --> $DIR/issue-59488.rs:30:5
    |
 LL |     assert_eq!(Foo::Bar, i);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}`
    |
-   = help: the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}`
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug`
   --> $DIR/issue-59488.rs:30:5
    |
 LL |     assert_eq!(Foo::Bar, i);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}`
    |
-   = help: the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}`
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 10 previous errors
diff --git a/tests/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr b/tests/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
index b30bcfb776c..736002c9335 100644
--- a/tests/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
+++ b/tests/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
@@ -26,9 +26,8 @@ LL | fn a() -> i32 {
    |    - consider calling this function
 ...
 LL |     assert_eq!(a, 0);
-   |     ^^^^^^^^^^^^^^^^ `fn() -> i32 {a}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn() -> i32 {a}`
    |
-   = help: the trait `Debug` is not implemented for fn item `fn() -> i32 {a}`
    = help: use parentheses to call this function: `a()`
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
diff --git a/tests/ui/issues/issue-92741.rs b/tests/ui/issues/issue-92741.rs
index f2e5fdafd9c..1c5d5810a57 100644
--- a/tests/ui/issues/issue-92741.rs
+++ b/tests/ui/issues/issue-92741.rs
@@ -1,17 +1,17 @@
 //@ run-rustfix
 fn main() {}
 fn _foo() -> bool {
-    &  //~ ERROR 4:5: 6:36: mismatched types [E0308]
+    &  //~ ERROR mismatched types [E0308]
     mut
     if true { true } else { false }
 }
 
 fn _bar() -> bool {
-    &  //~ ERROR 10:5: 11:40: mismatched types [E0308]
+    &  //~ ERROR mismatched types [E0308]
     mut if true { true } else { false }
 }
 
 fn _baz() -> bool {
-    & mut //~ ERROR 15:5: 16:36: mismatched types [E0308]
+    & mut //~ ERROR mismatched types [E0308]
     if true { true } else { false }
 }
diff --git a/tests/ui/lifetimes/no_lending_iterators.rs b/tests/ui/lifetimes/no_lending_iterators.rs
index b3e8ad08ba1..88b8cda0898 100644
--- a/tests/ui/lifetimes/no_lending_iterators.rs
+++ b/tests/ui/lifetimes/no_lending_iterators.rs
@@ -2,7 +2,7 @@ struct Data(String);
 
 impl Iterator for Data {
     type Item = &str;
-    //~^ ERROR 4:17: 4:18: associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
+    //~^ ERROR associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
 
     fn next(&mut self) -> Option<Self::Item> {
         Some(&self.0)
@@ -16,7 +16,7 @@ trait Bar {
 
 impl Bar for usize {
     type Item = &usize;
-    //~^ ERROR 18:17: 18:18: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
+    //~^ ERROR in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
 
     fn poke(&mut self, item: Self::Item) {
         self += *item;
@@ -25,7 +25,7 @@ impl Bar for usize {
 
 impl Bar for isize {
     type Item<'a> = &'a isize;
-    //~^ ERROR 27:14: 27:18: lifetime parameters or bounds on associated type `Item` do not match the trait declaration [E0195]
+    //~^ ERROR lifetime parameters or bounds on associated type `Item` do not match the trait declaration [E0195]
 
     fn poke(&mut self, item: Self::Item) {
         self += *item;
diff --git a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
index 66f9140f63c..b0d64b531cb 100644
--- a/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
+++ b/tests/ui/lifetimes/temporary-lifetime-extension-tuple-ctor.stderr
@@ -6,7 +6,7 @@ LL |     let g = some(&temp());
    |                   |
    |                   creates a temporary value which is freed while still in use
 LL |     println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?}");
-   |                                                   ----- borrow later used here
+   |                                                    - borrow later used here
    |
 help: consider using a `let` binding to create a longer lived value
    |
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs
index 9babc20d1a1..9ccc9ce4fdb 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs
+++ b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.rs
@@ -11,7 +11,7 @@ extern crate minicore;
 
 #[link(name = "foo", kind = "raw-dylib")]
 extern "stdcall" {
-//~^ WARN: calling convention not supported on this target
+//~^ WARN: unsupported_calling_conventions
 //~| WARN: previously accepted
     fn f(x: i32);
     //~^ ERROR ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr
index 95ea9080486..91e42f2909e 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr
+++ b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr
@@ -1,4 +1,4 @@
-warning: use of calling convention not supported on this target
+warning: "stdcall" is not a supported ABI for the current target
   --> $DIR/unsupported-abi.rs:13:1
    |
 LL | / extern "stdcall" {
diff --git a/tests/ui/lint/dead-code/issue-41883.stderr b/tests/ui/lint/dead-code/issue-41883.stderr
index cf079e4dda3..47ccef9a530 100644
--- a/tests/ui/lint/dead-code/issue-41883.stderr
+++ b/tests/ui/lint/dead-code/issue-41883.stderr
@@ -29,8 +29,6 @@ error: struct `UnusedStruct` is never constructed
    |
 LL |     struct UnusedStruct;
    |            ^^^^^^^^^^^^
-   |
-   = note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/lint/dead-code/issue-59003.rs b/tests/ui/lint/dead-code/issue-59003.rs
index e3dcaca5778..319cf2db149 100644
--- a/tests/ui/lint/dead-code/issue-59003.rs
+++ b/tests/ui/lint/dead-code/issue-59003.rs
@@ -4,8 +4,8 @@
 
 #![deny(dead_code)]
 
+#[allow(dead_code)]
 struct Foo {
-    #[allow(dead_code)]
     inner: u32,
 }
 
diff --git a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs
new file mode 100644
index 00000000000..25777438456
--- /dev/null
+++ b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs
@@ -0,0 +1,37 @@
+#![deny(dead_code)]
+
+struct Foo(u8); //~ ERROR struct `Foo` is never constructed
+
+enum Bar { //~ ERROR enum `Bar` is never used
+    Var1(u8),
+    Var2(u8),
+}
+
+pub trait Tr1 {
+    fn f1() -> Self;
+}
+
+impl Tr1 for Foo {
+    fn f1() -> Foo {
+        let f = Foo(0);
+        let Foo(tag) = f;
+        Foo(tag)
+    }
+}
+
+impl Tr1 for Bar {
+    fn f1() -> Bar {
+        let b = Bar::Var1(0);
+        let b = if let Bar::Var1(_) = b {
+            Bar::Var1(0)
+        } else {
+            Bar::Var2(0)
+        };
+        match b {
+            Bar::Var1(_) => Bar::Var2(0),
+            Bar::Var2(_) => Bar::Var1(0),
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr
new file mode 100644
index 00000000000..7c1a4b45977
--- /dev/null
+++ b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr
@@ -0,0 +1,20 @@
+error: struct `Foo` is never constructed
+  --> $DIR/lint-unused-adt-appeared-in-pattern.rs:3:8
+   |
+LL | struct Foo(u8);
+   |        ^^^
+   |
+note: the lint level is defined here
+  --> $DIR/lint-unused-adt-appeared-in-pattern.rs:1:9
+   |
+LL | #![deny(dead_code)]
+   |         ^^^^^^^^^
+
+error: enum `Bar` is never used
+  --> $DIR/lint-unused-adt-appeared-in-pattern.rs:5:6
+   |
+LL | enum Bar {
+   |      ^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr
index b992005318f..25a7d96cb89 100644
--- a/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr
+++ b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr
@@ -56,8 +56,6 @@ warning: struct `Foo` is never constructed
    |
 LL | struct Foo(usize, #[allow(unused)] usize);
    |        ^^^
-   |
-   = note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
 
 error: aborting due to 2 previous errors; 2 warnings emitted
 
diff --git a/tests/ui/lint/dead-code/not-lint-adt-appeared-in-pattern-issue-120770.rs b/tests/ui/lint/dead-code/not-lint-adt-appeared-in-pattern-issue-120770.rs
new file mode 100644
index 00000000000..43a2e431904
--- /dev/null
+++ b/tests/ui/lint/dead-code/not-lint-adt-appeared-in-pattern-issue-120770.rs
@@ -0,0 +1,32 @@
+//@ check-pass
+
+#![deny(dead_code)]
+
+#[repr(u8)]
+#[derive(Copy, Clone, Debug)]
+pub enum RecordField {
+    Target = 1,
+    Level,
+    Module,
+    File,
+    Line,
+    NumArgs,
+}
+
+unsafe trait Pod {}
+
+#[repr(transparent)]
+struct RecordFieldWrapper(RecordField);
+
+unsafe impl Pod for RecordFieldWrapper {}
+
+fn try_read<T: Pod>(buf: &[u8]) -> T {
+    unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) }
+}
+
+pub fn foo(buf: &[u8]) -> RecordField {
+    let RecordFieldWrapper(tag) = try_read(buf);
+    tag
+}
+
+fn main() {}
diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs
index 5b755d62a05..415eb4138de 100644
--- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs
+++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs
@@ -2,7 +2,7 @@
 
 struct T1; //~ ERROR struct `T1` is never constructed
 pub struct T2(i32); //~ ERROR field `0` is never read
-struct T3;
+struct T3; //~ ERROR struct `T3` is never constructed
 
 trait Trait1 { //~ ERROR trait `Trait1` is never used
     const UNUSED: i32;
diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr
index 2441a3f868d..778dadee153 100644
--- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr
+++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr
@@ -20,11 +20,17 @@ LL | pub struct T2(i32);
    |
    = help: consider removing this field
 
+error: struct `T3` is never constructed
+  --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:5:8
+   |
+LL | struct T3;
+   |        ^^
+
 error: trait `Trait1` is never used
   --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7
    |
 LL | trait Trait1 {
    |       ^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.rs b/tests/ui/lint/dead-code/unused-struct-derive-default.rs
index 330ad32dd57..f20b7cb66ee 100644
--- a/tests/ui/lint/dead-code/unused-struct-derive-default.rs
+++ b/tests/ui/lint/dead-code/unused-struct-derive-default.rs
@@ -22,4 +22,5 @@ pub struct T2 {
 
 fn main() {
     let _x: Used = Default::default();
+    let _e: E = Default::default();
 }
diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.stderr b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr
index bbb0bd7be70..7422f9a39f3 100644
--- a/tests/ui/lint/dead-code/unused-struct-derive-default.stderr
+++ b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr
@@ -4,7 +4,6 @@ error: struct `T` is never constructed
 LL | struct T;
    |        ^
    |
-   = note: `T` has a derived impl for the trait `Default`, but this is intentionally ignored during dead code analysis
 note: the lint level is defined here
   --> $DIR/unused-struct-derive-default.rs:1:9
    |
diff --git a/tests/ui/lint/lint-non-uppercase-usages.fixed b/tests/ui/lint/lint-non-uppercase-usages.fixed
new file mode 100644
index 00000000000..231991dcae0
--- /dev/null
+++ b/tests/ui/lint/lint-non-uppercase-usages.fixed
@@ -0,0 +1,44 @@
+// Checks that the `non_upper_case_globals` emits suggestions for usages as well
+// <https://github.com/rust-lang/rust/issues/124061>
+
+//@ check-pass
+//@ run-rustfix
+
+#![allow(dead_code)]
+
+use std::cell::Cell;
+
+const MY_STATIC: u32 = 0;
+//~^ WARN constant `my_static` should have an upper case name
+//~| SUGGESTION MY_STATIC
+
+const LOL: u32 = MY_STATIC + 0;
+//~^ SUGGESTION MY_STATIC
+
+mod my_mod {
+    const INSIDE_MOD: u32 = super::MY_STATIC + 0;
+    //~^ SUGGESTION MY_STATIC
+}
+
+thread_local! {
+    static FOO_FOO: Cell<usize> = unreachable!();
+    //~^ WARN constant `fooFOO` should have an upper case name
+    //~| SUGGESTION FOO_FOO
+}
+
+fn foo<const FOO: u32>() {
+    //~^ WARN const parameter `foo` should have an upper case name
+    //~| SUGGESTION FOO
+    let _a = FOO + 1;
+    //~^ SUGGESTION FOO
+}
+
+fn main() {
+    let _a = crate::MY_STATIC;
+    //~^ SUGGESTION MY_STATIC
+
+    FOO_FOO.set(9);
+    //~^ SUGGESTION FOO_FOO
+    println!("{}", FOO_FOO.get());
+    //~^ SUGGESTION FOO_FOO
+}
diff --git a/tests/ui/lint/lint-non-uppercase-usages.rs b/tests/ui/lint/lint-non-uppercase-usages.rs
new file mode 100644
index 00000000000..9cdf5e47003
--- /dev/null
+++ b/tests/ui/lint/lint-non-uppercase-usages.rs
@@ -0,0 +1,44 @@
+// Checks that the `non_upper_case_globals` emits suggestions for usages as well
+// <https://github.com/rust-lang/rust/issues/124061>
+
+//@ check-pass
+//@ run-rustfix
+
+#![allow(dead_code)]
+
+use std::cell::Cell;
+
+const my_static: u32 = 0;
+//~^ WARN constant `my_static` should have an upper case name
+//~| SUGGESTION MY_STATIC
+
+const LOL: u32 = my_static + 0;
+//~^ SUGGESTION MY_STATIC
+
+mod my_mod {
+    const INSIDE_MOD: u32 = super::my_static + 0;
+    //~^ SUGGESTION MY_STATIC
+}
+
+thread_local! {
+    static fooFOO: Cell<usize> = unreachable!();
+    //~^ WARN constant `fooFOO` should have an upper case name
+    //~| SUGGESTION FOO_FOO
+}
+
+fn foo<const foo: u32>() {
+    //~^ WARN const parameter `foo` should have an upper case name
+    //~| SUGGESTION FOO
+    let _a = foo + 1;
+    //~^ SUGGESTION FOO
+}
+
+fn main() {
+    let _a = crate::my_static;
+    //~^ SUGGESTION MY_STATIC
+
+    fooFOO.set(9);
+    //~^ SUGGESTION FOO_FOO
+    println!("{}", fooFOO.get());
+    //~^ SUGGESTION FOO_FOO
+}
diff --git a/tests/ui/lint/lint-non-uppercase-usages.stderr b/tests/ui/lint/lint-non-uppercase-usages.stderr
new file mode 100644
index 00000000000..7c7e573a88e
--- /dev/null
+++ b/tests/ui/lint/lint-non-uppercase-usages.stderr
@@ -0,0 +1,39 @@
+warning: constant `my_static` should have an upper case name
+  --> $DIR/lint-non-uppercase-usages.rs:11:7
+   |
+LL | const my_static: u32 = 0;
+   |       ^^^^^^^^^
+   |
+   = note: `#[warn(non_upper_case_globals)]` on by default
+help: convert the identifier to upper case
+   |
+LL - const my_static: u32 = 0;
+LL + const MY_STATIC: u32 = 0;
+   |
+
+warning: constant `fooFOO` should have an upper case name
+  --> $DIR/lint-non-uppercase-usages.rs:24:12
+   |
+LL |     static fooFOO: Cell<usize> = unreachable!();
+   |            ^^^^^^
+   |
+help: convert the identifier to upper case
+   |
+LL -     static fooFOO: Cell<usize> = unreachable!();
+LL +     static FOO_FOO: Cell<usize> = unreachable!();
+   |
+
+warning: const parameter `foo` should have an upper case name
+  --> $DIR/lint-non-uppercase-usages.rs:29:14
+   |
+LL | fn foo<const foo: u32>() {
+   |              ^^^
+   |
+help: convert the identifier to upper case (notice the capitalization difference)
+   |
+LL - fn foo<const foo: u32>() {
+LL + fn foo<const FOO: u32>() {
+   |
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr
index e1c45e832af..df6cada6b06 100644
--- a/tests/ui/lint/unused/unused-attr-duplicate.stderr
+++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr
@@ -66,19 +66,6 @@ LL | #[should_panic]
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:60:1
-   |
-LL | #[must_use = "some message"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
-   |
-note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:59:1
-   |
-LL | #[must_use]
-   | ^^^^^^^^^^^
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-
-error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:66:1
    |
 LL | #[non_exhaustive]
@@ -103,30 +90,6 @@ LL | #[automatically_derived]
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:77:1
-   |
-LL | #[cold]
-   | ^^^^^^^ help: remove this attribute
-   |
-note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:76:1
-   |
-LL | #[cold]
-   | ^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:79:1
-   |
-LL | #[track_caller]
-   | ^^^^^^^^^^^^^^^ help: remove this attribute
-   |
-note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:78:1
-   |
-LL | #[track_caller]
-   | ^^^^^^^^^^^^^^^
-
-error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:92:1
    |
 LL | #[export_name = "exported_symbol_name"]
@@ -140,18 +103,6 @@ LL | #[export_name = "exported_symbol_name2"]
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:98:1
-   |
-LL | #[no_mangle]
-   | ^^^^^^^^^^^^ help: remove this attribute
-   |
-note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:97:1
-   |
-LL | #[no_mangle]
-   | ^^^^^^^^^^^^
-
-error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:102:1
    |
 LL | #[used]
@@ -277,6 +228,19 @@ LL |     #[macro_export]
    |     ^^^^^^^^^^^^^^^
 
 error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:60:1
+   |
+LL | #[must_use = "some message"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:59:1
+   |
+LL | #[must_use]
+   | ^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
+error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:74:1
    |
 LL | #[inline(never)]
@@ -289,5 +253,41 @@ LL | #[inline(always)]
    | ^^^^^^^^^^^^^^^^^
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
+error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:77:1
+   |
+LL | #[cold]
+   | ^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:76:1
+   |
+LL | #[cold]
+   | ^^^^^^^
+
+error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:79:1
+   |
+LL | #[track_caller]
+   | ^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:78:1
+   |
+LL | #[track_caller]
+   | ^^^^^^^^^^^^^^^
+
+error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:98:1
+   |
+LL | #[no_mangle]
+   | ^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:97:1
+   |
+LL | #[no_mangle]
+   | ^^^^^^^^^^^^
+
 error: aborting due to 23 previous errors
 
diff --git a/tests/ui/loop-match/break-to-block.rs b/tests/ui/loop-match/break-to-block.rs
new file mode 100644
index 00000000000..e7451a944c3
--- /dev/null
+++ b/tests/ui/loop-match/break-to-block.rs
@@ -0,0 +1,23 @@
+// Test that a `break` without `#[const_continue]` still works as expected.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+fn main() {
+    assert_eq!(helper(), 1);
+}
+
+fn helper() -> u8 {
+    let mut state = 0u8;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                0 => break 'blk 1,
+                _ => break 'a state,
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/const-continue-to-block.rs b/tests/ui/loop-match/const-continue-to-block.rs
new file mode 100644
index 00000000000..fd7ebeefeb6
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-block.rs
@@ -0,0 +1,26 @@
+// Test that a `#[const_continue]` that breaks to a normal labeled block (that
+// is not part of a `#[loop_match]`) produces an error.
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+fn const_continue_to_block() -> u8 {
+    let state = 0;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                0 => {
+                    #[const_continue]
+                    break 'blk 1;
+                }
+                _ => 'b: {
+                    #[const_continue]
+                    break 'b 2;
+                    //~^ ERROR `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+                }
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/const-continue-to-block.stderr b/tests/ui/loop-match/const-continue-to-block.stderr
new file mode 100644
index 00000000000..3a5339a0394
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-block.stderr
@@ -0,0 +1,8 @@
+error: `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+  --> $DIR/const-continue-to-block.rs:20:27
+   |
+LL |                     break 'b 2;
+   |                           ^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/loop-match/const-continue-to-loop.rs b/tests/ui/loop-match/const-continue-to-loop.rs
new file mode 100644
index 00000000000..c363e617cfb
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-loop.rs
@@ -0,0 +1,27 @@
+// Test that a `#[const_continue]` that breaks to the label of the loop itself
+// rather than to the label of the block within the `#[loop_match]` produces an
+// error.
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+fn const_continue_to_loop() -> u8 {
+    let mut state = 0;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                0 => {
+                    #[const_continue]
+                    break 'blk 1;
+                }
+                _ => {
+                    #[const_continue]
+                    break 'a 2;
+                    //~^ ERROR `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+                }
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/const-continue-to-loop.stderr b/tests/ui/loop-match/const-continue-to-loop.stderr
new file mode 100644
index 00000000000..a217b3ac72c
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-loop.stderr
@@ -0,0 +1,8 @@
+error: `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+  --> $DIR/const-continue-to-loop.rs:21:27
+   |
+LL |                     break 'a 2;
+   |                           ^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/loop-match/const-continue-to-polymorphic-const.rs b/tests/ui/loop-match/const-continue-to-polymorphic-const.rs
new file mode 100644
index 00000000000..9a91c977911
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-polymorphic-const.rs
@@ -0,0 +1,29 @@
+// Test that a `#[const_continue]` that breaks on a polymorphic constant produces an error.
+// A polymorphic constant does not have a concrete value at MIR building time, and therefore the
+// `#[loop_match]~ desugaring can't handle such values.
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+trait Foo {
+    const TARGET: u8;
+
+    fn test_u8(mut state: u8) -> &'static str {
+        #[loop_match]
+        loop {
+            state = 'blk: {
+                match state {
+                    0 => {
+                        #[const_continue]
+                        break 'blk Self::TARGET;
+                        //~^ ERROR could not determine the target branch for this `#[const_continue]`
+                    }
+
+                    1 => return "bar",
+                    2 => return "baz",
+                    _ => unreachable!(),
+                }
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/const-continue-to-polymorphic-const.stderr b/tests/ui/loop-match/const-continue-to-polymorphic-const.stderr
new file mode 100644
index 00000000000..4d183a2fbeb
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-polymorphic-const.stderr
@@ -0,0 +1,8 @@
+error: could not determine the target branch for this `#[const_continue]`
+  --> $DIR/const-continue-to-polymorphic-const.rs:18:36
+   |
+LL |                         break 'blk Self::TARGET;
+   |                                    ^^^^^^^^^^^^ this value is too generic
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/loop-match/drop-in-match-arm.rs b/tests/ui/loop-match/drop-in-match-arm.rs
new file mode 100644
index 00000000000..731af659012
--- /dev/null
+++ b/tests/ui/loop-match/drop-in-match-arm.rs
@@ -0,0 +1,47 @@
+// Test that dropping values works in match arms, which is nontrivial
+// because each match arm needs its own scope.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+use std::sync::atomic::{AtomicBool, Ordering};
+
+fn main() {
+    assert_eq!(helper(), 1);
+    assert!(DROPPED.load(Ordering::Relaxed));
+}
+
+static DROPPED: AtomicBool = AtomicBool::new(false);
+
+struct X;
+
+impl Drop for X {
+    fn drop(&mut self) {
+        DROPPED.store(true, Ordering::Relaxed);
+    }
+}
+
+#[no_mangle]
+#[inline(never)]
+fn helper() -> i32 {
+    let mut state = 0;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                0 => match X {
+                    _ => {
+                        assert!(!DROPPED.load(Ordering::Relaxed));
+                        break 'blk 1;
+                    }
+                },
+                _ => {
+                    assert!(DROPPED.load(Ordering::Relaxed));
+                    break 'a state;
+                }
+            }
+        };
+    }
+}
diff --git a/tests/ui/loop-match/invalid-attribute.rs b/tests/ui/loop-match/invalid-attribute.rs
new file mode 100644
index 00000000000..d8d2f605eb4
--- /dev/null
+++ b/tests/ui/loop-match/invalid-attribute.rs
@@ -0,0 +1,43 @@
+// Test that the `#[loop_match]` and `#[const_continue]` attributes can only be
+// placed on expressions.
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![loop_match] //~ ERROR should be applied to a loop
+#![const_continue] //~ ERROR should be applied to a break expression
+
+extern "C" {
+    #[loop_match] //~ ERROR should be applied to a loop
+    #[const_continue] //~ ERROR should be applied to a break expression
+    fn f();
+}
+
+#[loop_match] //~ ERROR should be applied to a loop
+#[const_continue] //~ ERROR should be applied to a break expression
+#[repr(C)]
+struct S {
+    a: u32,
+    b: u32,
+}
+
+trait Invoke {
+    #[loop_match] //~ ERROR should be applied to a loop
+    #[const_continue] //~ ERROR should be applied to a break expression
+    extern "C" fn invoke(&self);
+}
+
+#[loop_match] //~ ERROR should be applied to a loop
+#[const_continue] //~ ERROR should be applied to a break expression
+extern "C" fn ok() {}
+
+fn main() {
+    #[loop_match] //~ ERROR should be applied to a loop
+    #[const_continue] //~ ERROR should be applied to a break expression
+    || {};
+
+    {
+        #[loop_match] //~ ERROR should be applied to a loop
+        #[const_continue] //~ ERROR should be applied to a break expression
+        5
+    };
+}
diff --git a/tests/ui/loop-match/invalid-attribute.stderr b/tests/ui/loop-match/invalid-attribute.stderr
new file mode 100644
index 00000000000..07015311f9c
--- /dev/null
+++ b/tests/ui/loop-match/invalid-attribute.stderr
@@ -0,0 +1,131 @@
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:16:1
+   |
+LL | #[const_continue]
+   | ^^^^^^^^^^^^^^^^^
+LL | #[repr(C)]
+LL | struct S {
+   | -------- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:15:1
+   |
+LL | #[loop_match]
+   | ^^^^^^^^^^^^^
+...
+LL | struct S {
+   | -------- not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:30:1
+   |
+LL | #[const_continue]
+   | ^^^^^^^^^^^^^^^^^
+LL | extern "C" fn ok() {}
+   | ------------------ not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:29:1
+   |
+LL | #[loop_match]
+   | ^^^^^^^^^^^^^
+LL | #[const_continue]
+LL | extern "C" fn ok() {}
+   | ------------------ not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:35:5
+   |
+LL |     #[const_continue]
+   |     ^^^^^^^^^^^^^^^^^
+LL |     || {};
+   |     -- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:34:5
+   |
+LL |     #[loop_match]
+   |     ^^^^^^^^^^^^^
+LL |     #[const_continue]
+LL |     || {};
+   |     -- not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:40:9
+   |
+LL |         #[const_continue]
+   |         ^^^^^^^^^^^^^^^^^
+LL |         5
+   |         - not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:39:9
+   |
+LL |         #[loop_match]
+   |         ^^^^^^^^^^^^^
+LL |         #[const_continue]
+LL |         5
+   |         - not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:25:5
+   |
+LL |     #[const_continue]
+   |     ^^^^^^^^^^^^^^^^^
+LL |     extern "C" fn invoke(&self);
+   |     ---------------------------- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:24:5
+   |
+LL |     #[loop_match]
+   |     ^^^^^^^^^^^^^
+LL |     #[const_continue]
+LL |     extern "C" fn invoke(&self);
+   |     ---------------------------- not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:11:5
+   |
+LL |     #[const_continue]
+   |     ^^^^^^^^^^^^^^^^^
+LL |     fn f();
+   |     ------- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:10:5
+   |
+LL |     #[loop_match]
+   |     ^^^^^^^^^^^^^
+LL |     #[const_continue]
+LL |     fn f();
+   |     ------- not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:7:1
+   |
+LL | / #![allow(incomplete_features)]
+LL | | #![feature(loop_match)]
+LL | | #![loop_match]
+LL | | #![const_continue]
+   | | ^^^^^^^^^^^^^^^^^^
+...  |
+LL | |     };
+LL | | }
+   | |_- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:6:1
+   |
+LL | / #![allow(incomplete_features)]
+LL | | #![feature(loop_match)]
+LL | | #![loop_match]
+   | | ^^^^^^^^^^^^^^
+LL | | #![const_continue]
+...  |
+LL | |     };
+LL | | }
+   | |_- not a loop
+
+error: aborting due to 14 previous errors
+
diff --git a/tests/ui/loop-match/invalid.rs b/tests/ui/loop-match/invalid.rs
new file mode 100644
index 00000000000..2ddc19f4fc6
--- /dev/null
+++ b/tests/ui/loop-match/invalid.rs
@@ -0,0 +1,161 @@
+// Test that the correct error is emitted when `#[loop_match]` is applied to
+// syntax it does not support.
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+enum State {
+    A,
+    B,
+    C,
+}
+
+fn invalid_update() {
+    let mut fake = State::A;
+    let state = State::A;
+    #[loop_match]
+    loop {
+        fake = 'blk: {
+            //~^ ERROR invalid update of the `#[loop_match]` state
+            match state {
+                _ => State::B,
+            }
+        }
+    }
+}
+
+fn invalid_scrutinee() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match State::A {
+                //~^ ERROR invalid match on `#[loop_match]` state
+                _ => State::B,
+            }
+        }
+    }
+}
+
+fn bad_statements_1() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        1;
+        //~^ ERROR statements are not allowed in this position within a `#[loop_match]`
+        state = 'blk: {
+            match State::A {
+                _ => State::B,
+            }
+        }
+    }
+}
+
+fn bad_statements_2() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            1;
+            //~^ ERROR statements are not allowed in this position within a `#[loop_match]`
+            match State::A {
+                _ => State::B,
+            }
+        }
+    }
+}
+
+fn bad_rhs_1() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        state = State::B
+        //~^ ERROR this expression must be a single `match` wrapped in a labeled block
+    }
+}
+
+fn bad_rhs_2() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            State::B
+            //~^ ERROR this expression must be a single `match` wrapped in a labeled block
+        }
+    }
+}
+
+fn bad_rhs_3() {
+    let mut state = ();
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            //~^ ERROR this expression must be a single `match` wrapped in a labeled block
+        }
+    }
+}
+
+fn missing_assignment() {
+    #[loop_match]
+    loop {
+        () //~ ERROR  expected a single assignment expression
+    }
+}
+
+fn empty_loop_body() {
+    #[loop_match]
+    loop {
+        //~^ ERROR  expected a single assignment expression
+    }
+}
+
+fn break_without_value() {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    break 'blk;
+                    //~^ ERROR mismatched types
+                }
+                _ => break 'a,
+            }
+        }
+    }
+}
+
+fn break_without_value_unit() {
+    let mut state = ();
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                () => {
+                    #[const_continue]
+                    break 'blk;
+                    //~^ ERROR a `#[const_continue]` must break to a label with a value
+                }
+            }
+        }
+    }
+}
+
+fn arm_has_guard(cond: bool) {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    break 'blk State::B;
+                }
+                State::B if cond => break 'a,
+                //~^ ERROR match arms that are part of a `#[loop_match]` cannot have guards
+                _ => break 'a,
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/invalid.stderr b/tests/ui/loop-match/invalid.stderr
new file mode 100644
index 00000000000..51fdd024c6f
--- /dev/null
+++ b/tests/ui/loop-match/invalid.stderr
@@ -0,0 +1,91 @@
+error[E0308]: mismatched types
+  --> $DIR/invalid.rs:120:21
+   |
+LL |                     break 'blk;
+   |                     ^^^^^^^^^^ expected `State`, found `()`
+   |
+help: give the `break` a value of the expected type
+   |
+LL |                     break 'blk /* value */;
+   |                                +++++++++++
+
+error: invalid update of the `#[loop_match]` state
+  --> $DIR/invalid.rs:18:9
+   |
+LL |         fake = 'blk: {
+   |         ^^^^
+LL |
+LL |             match state {
+   |                   ----- the assignment must update this variable
+
+error: invalid match on `#[loop_match]` state
+  --> $DIR/invalid.rs:32:19
+   |
+LL |             match State::A {
+   |                   ^^^^^^^^
+   |
+   = note: a local variable must be the scrutinee within a `#[loop_match]`
+
+error: statements are not allowed in this position within a `#[loop_match]`
+  --> $DIR/invalid.rs:44:9
+   |
+LL |         1;
+   |         ^^
+
+error: statements are not allowed in this position within a `#[loop_match]`
+  --> $DIR/invalid.rs:59:13
+   |
+LL |             1;
+   |             ^^
+
+error: this expression must be a single `match` wrapped in a labeled block
+  --> $DIR/invalid.rs:72:17
+   |
+LL |         state = State::B
+   |                 ^^^^^^^^
+
+error: this expression must be a single `match` wrapped in a labeled block
+  --> $DIR/invalid.rs:82:13
+   |
+LL |             State::B
+   |             ^^^^^^^^
+
+error: this expression must be a single `match` wrapped in a labeled block
+  --> $DIR/invalid.rs:92:17
+   |
+LL |           state = 'blk: {
+   |  _________________^
+LL | |
+LL | |         }
+   | |_________^
+
+error: expected a single assignment expression
+  --> $DIR/invalid.rs:101:9
+   |
+LL |         ()
+   |         ^^
+
+error: expected a single assignment expression
+  --> $DIR/invalid.rs:107:10
+   |
+LL |       loop {
+   |  __________^
+LL | |
+LL | |     }
+   | |_____^
+
+error: a `#[const_continue]` must break to a label with a value
+  --> $DIR/invalid.rs:137:21
+   |
+LL |                     break 'blk;
+   |                     ^^^^^^^^^^
+
+error: match arms that are part of a `#[loop_match]` cannot have guards
+  --> $DIR/invalid.rs:155:29
+   |
+LL |                 State::B if cond => break 'a,
+   |                             ^^^^
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/loop-match/loop-match.rs b/tests/ui/loop-match/loop-match.rs
new file mode 100644
index 00000000000..f38bc01f333
--- /dev/null
+++ b/tests/ui/loop-match/loop-match.rs
@@ -0,0 +1,45 @@
+// Test that a basic correct example of `#[loop_match]` with `#[const_continue]`
+// works correctly.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+enum State {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    break 'blk State::B;
+                }
+                State::B => {
+                    // Without special logic, the compiler believes this is a
+                    // reassignment to an immutable variable because of the
+                    // `loop`. So this tests that local variables work.
+                    let _a = 0;
+
+                    if true {
+                        #[const_continue]
+                        break 'blk State::C;
+                    } else {
+                        #[const_continue]
+                        break 'blk State::A;
+                    }
+                }
+                State::C => break 'a,
+            }
+        };
+    }
+
+    assert!(matches!(state, State::C))
+}
diff --git a/tests/ui/loop-match/macro.rs b/tests/ui/loop-match/macro.rs
new file mode 100644
index 00000000000..98c98b9b627
--- /dev/null
+++ b/tests/ui/loop-match/macro.rs
@@ -0,0 +1,48 @@
+// Test that macros can be defined in the labeled block. This should not trigger an error about
+// statements not being allowed in that position, and should of course work as expected.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+enum State {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            macro_rules! const_continue {
+                ($e:expr) => {
+                    #[const_continue]
+                    break 'blk $e;
+                };
+            }
+            match state {
+                State::A => {
+                    const_continue!(State::B);
+                }
+                State::B => {
+                    // Without special logic, the compiler believes this is a
+                    // reassignment to an immutable variable because of the
+                    // `loop`. So this tests that local variables work.
+                    let _a = 0;
+
+                    if true {
+                        const_continue!(State::C);
+                    } else {
+                        const_continue!(State::A);
+                    }
+                }
+                State::C => break 'a,
+            }
+        };
+    }
+
+    assert!(matches!(state, State::C))
+}
diff --git a/tests/ui/loop-match/nested.rs b/tests/ui/loop-match/nested.rs
new file mode 100644
index 00000000000..aaddfae11de
--- /dev/null
+++ b/tests/ui/loop-match/nested.rs
@@ -0,0 +1,83 @@
+// Test that a nested `#[loop_match]` works as expected, and that e.g. a
+// `#[const_continue]` of the inner `#[loop_match]` does not interact with the
+// outer `#[loop_match]`.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+enum State1 {
+    A,
+    B,
+    C,
+}
+
+enum State2 {
+    X,
+    Y,
+    Z,
+}
+
+fn main() {
+    assert_eq!(run(), concat!("ab", "xyz", "xyz", "c"))
+}
+
+fn run() -> String {
+    let mut accum = String::new();
+
+    let mut state1 = State1::A;
+    let mut state2 = State2::X;
+
+    let mut first = true;
+
+    #[loop_match]
+    'a: loop {
+        state1 = 'blk1: {
+            match state1 {
+                State1::A => {
+                    accum.push('a');
+                    #[const_continue]
+                    break 'blk1 State1::B;
+                }
+                State1::B => {
+                    accum.push('b');
+                    #[loop_match]
+                    loop {
+                        state2 = 'blk2: {
+                            match state2 {
+                                State2::X => {
+                                    accum.push('x');
+                                    #[const_continue]
+                                    break 'blk2 State2::Y;
+                                }
+                                State2::Y => {
+                                    accum.push('y');
+                                    #[const_continue]
+                                    break 'blk2 State2::Z;
+                                }
+                                State2::Z => {
+                                    accum.push('z');
+                                    if first {
+                                        first = false;
+                                        #[const_continue]
+                                        break 'blk2 State2::X;
+                                    } else {
+                                        #[const_continue]
+                                        break 'blk1 State1::C;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                State1::C => {
+                    accum.push('c');
+                    break 'a;
+                }
+            }
+        }
+    }
+
+    accum
+}
diff --git a/tests/ui/loop-match/or-patterns.rs b/tests/ui/loop-match/or-patterns.rs
new file mode 100644
index 00000000000..775243b9c62
--- /dev/null
+++ b/tests/ui/loop-match/or-patterns.rs
@@ -0,0 +1,54 @@
+// Test that `#[loop_match]` supports or-patterns.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum State {
+    A,
+    B,
+    C,
+    D,
+}
+
+fn main() {
+    let mut states = vec![];
+    let mut first = true;
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    states.push(state);
+                    if first {
+                        #[const_continue]
+                        break 'blk State::B;
+                    } else {
+                        #[const_continue]
+                        break 'blk State::D;
+                    }
+                }
+                State::B | State::D => {
+                    states.push(state);
+                    if first {
+                        first = false;
+                        #[const_continue]
+                        break 'blk State::A;
+                    } else {
+                        #[const_continue]
+                        break 'blk State::C;
+                    }
+                }
+                State::C => {
+                    states.push(state);
+                    break 'a;
+                }
+            }
+        }
+    }
+
+    assert_eq!(states, [State::A, State::B, State::A, State::D, State::C]);
+}
diff --git a/tests/ui/loop-match/unsupported-type.rs b/tests/ui/loop-match/unsupported-type.rs
new file mode 100644
index 00000000000..9100a1103ab
--- /dev/null
+++ b/tests/ui/loop-match/unsupported-type.rs
@@ -0,0 +1,27 @@
+// Test that the right error is emitted when the `#[loop_match]` state is an
+// unsupported type.
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+fn unsupported_type() {
+    let mut state = Some(false);
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            //~^ ERROR this `#[loop_match]` state value has type `Option<bool>`, which is not supported
+            match state {
+                Some(false) => {
+                    #[const_continue]
+                    break 'blk Some(true);
+                }
+                Some(true) => {
+                    #[const_continue]
+                    break 'blk None;
+                }
+                None => break 'a,
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/unsupported-type.stderr b/tests/ui/loop-match/unsupported-type.stderr
new file mode 100644
index 00000000000..ede3d86796f
--- /dev/null
+++ b/tests/ui/loop-match/unsupported-type.stderr
@@ -0,0 +1,10 @@
+error: this `#[loop_match]` state value has type `Option<bool>`, which is not supported
+  --> $DIR/unsupported-type.rs:12:9
+   |
+LL |         state = 'blk: {
+   |         ^^^^^
+   |
+   = note: only integers, floats, bool, char, and enums without fields are supported
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/loop-match/unwind.rs b/tests/ui/loop-match/unwind.rs
new file mode 100644
index 00000000000..39e2e4537b1
--- /dev/null
+++ b/tests/ui/loop-match/unwind.rs
@@ -0,0 +1,53 @@
+// Test that `#[const_continue]` correctly emits cleanup paths for drops.
+//
+// Here, we first drop `DropBomb`, causing an unwind. Then `ExitOnDrop` should
+// be dropped, causing us to exit with `0` rather than with some non-zero value
+// due to the panic, which is what causes the test to pass.
+
+//@ run-pass
+//@ needs-unwind
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+enum State {
+    A,
+    B,
+}
+
+struct ExitOnDrop;
+
+impl Drop for ExitOnDrop {
+    fn drop(&mut self) {
+        std::process::exit(0);
+    }
+}
+
+struct DropBomb;
+
+impl Drop for DropBomb {
+    fn drop(&mut self) {
+        panic!("this must unwind");
+    }
+}
+
+fn main() {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    let _exit = ExitOnDrop;
+                    let _bomb = DropBomb;
+
+                    #[const_continue]
+                    break 'blk State::B;
+                }
+                State::B => break 'a,
+            }
+        };
+    }
+
+    unreachable!();
+}
diff --git a/tests/ui/loop-match/valid-patterns.rs b/tests/ui/loop-match/valid-patterns.rs
new file mode 100644
index 00000000000..4e0e4798a0b
--- /dev/null
+++ b/tests/ui/loop-match/valid-patterns.rs
@@ -0,0 +1,117 @@
+// Test that signed and unsigned integer patterns work with `#[loop_match]`.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+fn main() {
+    assert_eq!(integer(0), 2);
+    assert_eq!(integer(-1), 2);
+    assert_eq!(integer(2), 2);
+
+    assert_eq!(boolean(true), false);
+    assert_eq!(boolean(false), false);
+
+    assert_eq!(character('a'), 'b');
+    assert_eq!(character('b'), 'b');
+    assert_eq!(character('c'), 'd');
+    assert_eq!(character('d'), 'd');
+
+    assert_eq!(test_f32(1.0), core::f32::consts::PI);
+    assert_eq!(test_f32(2.5), core::f32::consts::PI);
+    assert_eq!(test_f32(4.0), 4.0);
+
+    assert_eq!(test_f64(1.0), core::f64::consts::PI);
+    assert_eq!(test_f64(2.5), core::f64::consts::PI);
+    assert_eq!(test_f64(4.0), 4.0);
+}
+
+fn integer(mut state: i32) -> i32 {
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                -1 => {
+                    #[const_continue]
+                    break 'blk 2;
+                }
+                0 => {
+                    #[const_continue]
+                    break 'blk -1;
+                }
+                2 => break 'a,
+                _ => unreachable!("weird value {:?}", state),
+            }
+        }
+    }
+
+    state
+}
+
+fn boolean(mut state: bool) -> bool {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                true => {
+                    #[const_continue]
+                    break 'blk false;
+                }
+                false => return state,
+            }
+        }
+    }
+}
+
+fn character(mut state: char) -> char {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                'a' => {
+                    #[const_continue]
+                    break 'blk 'b';
+                }
+                'b' => return state,
+                'c' => {
+                    #[const_continue]
+                    break 'blk 'd';
+                }
+                _ => return state,
+            }
+        }
+    }
+}
+
+fn test_f32(mut state: f32) -> f32 {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                1.0 => {
+                    #[const_continue]
+                    break 'blk 2.5;
+                }
+                2.0..3.0 => return core::f32::consts::PI,
+                _ => return state,
+            }
+        }
+    }
+}
+
+fn test_f64(mut state: f64) -> f64 {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                1.0 => {
+                    #[const_continue]
+                    break 'blk 2.5;
+                }
+                2.0..3.0 => return core::f64::consts::PI,
+                _ => return state,
+            }
+        }
+    }
+}
diff --git a/tests/ui/macros/concat-bytes-error.rs b/tests/ui/macros/concat-bytes-error.rs
index db5d3cab0bd..8130fc54d8d 100644
--- a/tests/ui/macros/concat-bytes-error.rs
+++ b/tests/ui/macros/concat-bytes-error.rs
@@ -1,20 +1,44 @@
+//@ edition: 2021
+// 2021 edition for C string literals
+
 #![feature(concat_bytes)]
 
 fn main() {
+    // Identifiers
     concat_bytes!(pie); //~ ERROR expected a byte literal
     concat_bytes!(pie, pie); //~ ERROR expected a byte literal
+
+    // String literals
     concat_bytes!("tnrsi", "tnri"); //~ ERROR cannot concatenate string literals
+    //~^ SUGGESTION b"tnrsi"
+    concat_bytes!(r"tnrsi", r"tnri"); //~ ERROR cannot concatenate string literals
+    //~^ SUGGESTION br"tnrsi"
+    concat_bytes!(r#"tnrsi"#, r###"tnri"###); //~ ERROR cannot concatenate string literals
+    //~^ SUGGESTION br#"tnrsi"#
+    concat_bytes!(c"tnrsi", c"tnri"); //~ ERROR cannot concatenate C string literals
+    //~^ SUGGESTION b"tnrsi\0"
+    concat_bytes!(cr"tnrsi", cr"tnri"); //~ ERROR cannot concatenate C string literals
+    concat_bytes!(cr#"tnrsi"#, cr###"tnri"###); //~ ERROR cannot concatenate C string literals
+
+    // Other literals
     concat_bytes!(2.8); //~ ERROR cannot concatenate float literals
     concat_bytes!(300); //~ ERROR cannot concatenate numeric literals
+    //~^ SUGGESTION [300]
     concat_bytes!('a'); //~ ERROR cannot concatenate character literals
+    //~^ SUGGESTION b'a'
     concat_bytes!(true, false); //~ ERROR cannot concatenate boolean literals
     concat_bytes!(42, b"va", b'l'); //~ ERROR cannot concatenate numeric literals
+    //~^ SUGGESTION [42]
     concat_bytes!(42, b"va", b'l', [1, 2]); //~ ERROR cannot concatenate numeric literals
+    //~^ SUGGESTION [42]
+
+    // Nested items
     concat_bytes!([
         "hi", //~ ERROR cannot concatenate string literals
     ]);
     concat_bytes!([
         'a', //~ ERROR cannot concatenate character literals
+        //~^ SUGGESTION b'a'
     ]);
     concat_bytes!([
         true, //~ ERROR cannot concatenate boolean literals
@@ -38,6 +62,7 @@ fn main() {
         [5, 6, 7], //~ ERROR cannot concatenate doubly nested array
     ]);
     concat_bytes!(5u16); //~ ERROR cannot concatenate numeric literals
+    //~^ SUGGESTION [5u16]
     concat_bytes!([5u16]); //~ ERROR numeric literal is not a `u8`
     concat_bytes!([3; ()]); //~ ERROR repeat count is not a positive number
     concat_bytes!([3; -2]); //~ ERROR repeat count is not a positive number
diff --git a/tests/ui/macros/concat-bytes-error.stderr b/tests/ui/macros/concat-bytes-error.stderr
index 3f2c64922e3..447d7a663fd 100644
--- a/tests/ui/macros/concat-bytes-error.stderr
+++ b/tests/ui/macros/concat-bytes-error.stderr
@@ -1,5 +1,5 @@
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:4:19
+  --> $DIR/concat-bytes-error.rs:8:19
    |
 LL |     concat_bytes!(pie);
    |                   ^^^
@@ -7,7 +7,7 @@ LL |     concat_bytes!(pie);
    = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
 
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:5:19
+  --> $DIR/concat-bytes-error.rs:9:19
    |
 LL |     concat_bytes!(pie, pie);
    |                   ^^^  ^^^
@@ -15,85 +15,126 @@ LL |     concat_bytes!(pie, pie);
    = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
 
 error: cannot concatenate string literals
-  --> $DIR/concat-bytes-error.rs:6:19
+  --> $DIR/concat-bytes-error.rs:12:19
    |
 LL |     concat_bytes!("tnrsi", "tnri");
    |                   ^^^^^^^ help: try using a byte string: `b"tnrsi"`
 
+error: cannot concatenate string literals
+  --> $DIR/concat-bytes-error.rs:14:19
+   |
+LL |     concat_bytes!(r"tnrsi", r"tnri");
+   |                   ^^^^^^^^ help: try using a byte string: `br"tnrsi"`
+
+error: cannot concatenate string literals
+  --> $DIR/concat-bytes-error.rs:16:19
+   |
+LL |     concat_bytes!(r#"tnrsi"#, r###"tnri"###);
+   |                   ^^^^^^^^^^ help: try using a byte string: `br#"tnrsi"#`
+
+error: cannot concatenate C string literals
+  --> $DIR/concat-bytes-error.rs:18:19
+   |
+LL |     concat_bytes!(c"tnrsi", c"tnri");
+   |                   ^^^^^^^^ help: try using a null-terminated byte string: `b"tnrsi\0"`
+   |
+note: concatenating C strings is ambiguous about including the '\0'
+  --> $DIR/concat-bytes-error.rs:18:19
+   |
+LL |     concat_bytes!(c"tnrsi", c"tnri");
+   |                   ^^^^^^^^
+   = note: concatenating C strings is ambiguous about including the '\0'
+
+error: cannot concatenate C string literals
+  --> $DIR/concat-bytes-error.rs:20:19
+   |
+LL |     concat_bytes!(cr"tnrsi", cr"tnri");
+   |                   ^^^^^^^^^
+   |
+   = note: concatenating C strings is ambiguous about including the '\0'
+
+error: cannot concatenate C string literals
+  --> $DIR/concat-bytes-error.rs:21:19
+   |
+LL |     concat_bytes!(cr#"tnrsi"#, cr###"tnri"###);
+   |                   ^^^^^^^^^^^
+   |
+   = note: concatenating C strings is ambiguous about including the '\0'
+
 error: cannot concatenate float literals
-  --> $DIR/concat-bytes-error.rs:7:19
+  --> $DIR/concat-bytes-error.rs:24:19
    |
 LL |     concat_bytes!(2.8);
    |                   ^^^
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:8:19
+  --> $DIR/concat-bytes-error.rs:25:19
    |
 LL |     concat_bytes!(300);
    |                   ^^^ help: try wrapping the number in an array: `[300]`
 
 error: cannot concatenate character literals
-  --> $DIR/concat-bytes-error.rs:9:19
+  --> $DIR/concat-bytes-error.rs:27:19
    |
 LL |     concat_bytes!('a');
    |                   ^^^ help: try using a byte character: `b'a'`
 
 error: cannot concatenate boolean literals
-  --> $DIR/concat-bytes-error.rs:10:19
+  --> $DIR/concat-bytes-error.rs:29:19
    |
 LL |     concat_bytes!(true, false);
    |                   ^^^^
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:11:19
+  --> $DIR/concat-bytes-error.rs:30:19
    |
 LL |     concat_bytes!(42, b"va", b'l');
    |                   ^^ help: try wrapping the number in an array: `[42]`
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:12:19
+  --> $DIR/concat-bytes-error.rs:32:19
    |
 LL |     concat_bytes!(42, b"va", b'l', [1, 2]);
    |                   ^^ help: try wrapping the number in an array: `[42]`
 
 error: cannot concatenate string literals
-  --> $DIR/concat-bytes-error.rs:14:9
+  --> $DIR/concat-bytes-error.rs:37:9
    |
 LL |         "hi",
    |         ^^^^
 
 error: cannot concatenate character literals
-  --> $DIR/concat-bytes-error.rs:17:9
+  --> $DIR/concat-bytes-error.rs:40:9
    |
 LL |         'a',
    |         ^^^ help: try using a byte character: `b'a'`
 
 error: cannot concatenate boolean literals
-  --> $DIR/concat-bytes-error.rs:20:9
+  --> $DIR/concat-bytes-error.rs:44:9
    |
 LL |         true,
    |         ^^^^
 
 error: cannot concatenate boolean literals
-  --> $DIR/concat-bytes-error.rs:23:9
+  --> $DIR/concat-bytes-error.rs:47:9
    |
 LL |         false,
    |         ^^^^^
 
 error: cannot concatenate float literals
-  --> $DIR/concat-bytes-error.rs:26:9
+  --> $DIR/concat-bytes-error.rs:50:9
    |
 LL |         2.6,
    |         ^^^
 
 error: numeric literal is out of bounds
-  --> $DIR/concat-bytes-error.rs:29:9
+  --> $DIR/concat-bytes-error.rs:53:9
    |
 LL |         265,
    |         ^^^
 
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:32:9
+  --> $DIR/concat-bytes-error.rs:56:9
    |
 LL |         -33,
    |         ^^^
@@ -101,7 +142,7 @@ LL |         -33,
    = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:35:9
+  --> $DIR/concat-bytes-error.rs:59:9
    |
 LL |         b"hi!",
    |         ^^^^^^
@@ -110,43 +151,43 @@ LL |         b"hi!",
    = help: try flattening the array
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:38:9
+  --> $DIR/concat-bytes-error.rs:62:9
    |
 LL |         [5, 6, 7],
    |         ^^^^^^^^^
 
 error: cannot concatenate numeric literals
-  --> $DIR/concat-bytes-error.rs:40:19
+  --> $DIR/concat-bytes-error.rs:64:19
    |
 LL |     concat_bytes!(5u16);
    |                   ^^^^ help: try wrapping the number in an array: `[5u16]`
 
 error: numeric literal is not a `u8`
-  --> $DIR/concat-bytes-error.rs:41:20
+  --> $DIR/concat-bytes-error.rs:66:20
    |
 LL |     concat_bytes!([5u16]);
    |                    ^^^^
 
 error: repeat count is not a positive number
-  --> $DIR/concat-bytes-error.rs:42:23
+  --> $DIR/concat-bytes-error.rs:67:23
    |
 LL |     concat_bytes!([3; ()]);
    |                       ^^
 
 error: repeat count is not a positive number
-  --> $DIR/concat-bytes-error.rs:43:23
+  --> $DIR/concat-bytes-error.rs:68:23
    |
 LL |     concat_bytes!([3; -2]);
    |                       ^^
 
 error: repeat count is not a positive number
-  --> $DIR/concat-bytes-error.rs:44:25
+  --> $DIR/concat-bytes-error.rs:69:25
    |
 LL |     concat_bytes!([pie; -2]);
    |                         ^^
 
 error: expected a byte literal
-  --> $DIR/concat-bytes-error.rs:45:20
+  --> $DIR/concat-bytes-error.rs:70:20
    |
 LL |     concat_bytes!([pie; 2]);
    |                    ^^^
@@ -154,28 +195,28 @@ LL |     concat_bytes!([pie; 2]);
    = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
 
 error: cannot concatenate float literals
-  --> $DIR/concat-bytes-error.rs:46:20
+  --> $DIR/concat-bytes-error.rs:71:20
    |
 LL |     concat_bytes!([2.2; 0]);
    |                    ^^^
 
 error: repeat count is not a positive number
-  --> $DIR/concat-bytes-error.rs:47:25
+  --> $DIR/concat-bytes-error.rs:72:25
    |
 LL |     concat_bytes!([5.5; ()]);
    |                         ^^
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:48:20
+  --> $DIR/concat-bytes-error.rs:73:20
    |
 LL |     concat_bytes!([[1, 2, 3]; 3]);
    |                    ^^^^^^^^^
 
 error: cannot concatenate doubly nested array
-  --> $DIR/concat-bytes-error.rs:49:20
+  --> $DIR/concat-bytes-error.rs:74:20
    |
 LL |     concat_bytes!([[42; 2]; 3]);
    |                    ^^^^^^^
 
-error: aborting due to 28 previous errors
+error: aborting due to 33 previous errors
 
diff --git a/tests/ui/macros/format-args-temporaries-in-write.stderr b/tests/ui/macros/format-args-temporaries-in-write.stderr
index e58a43383f6..489987899cd 100644
--- a/tests/ui/macros/format-args-temporaries-in-write.stderr
+++ b/tests/ui/macros/format-args-temporaries-in-write.stderr
@@ -13,11 +13,6 @@ LL |     };
    |     -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
    |     |
    |     `mutex` dropped here while still borrowed
-   |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
-   |
-LL |         write!(Out, "{}", mutex.lock()); /* no semicolon */
-   |                                        +
 
 error[E0597]: `mutex` does not live long enough
   --> $DIR/format-args-temporaries-in-write.rs:47:29
@@ -34,11 +29,6 @@ LL |     };
    |     -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
    |     |
    |     `mutex` dropped here while still borrowed
-   |
-help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
-   |
-LL |         writeln!(Out, "{}", mutex.lock()); /* no semicolon */
-   |                                          +
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/macros/macro-comma-support-rpass.rs b/tests/ui/macros/macro-comma-support-rpass.rs
index 5a4bac70b1c..ef6c1ff6fd0 100644
--- a/tests/ui/macros/macro-comma-support-rpass.rs
+++ b/tests/ui/macros/macro-comma-support-rpass.rs
@@ -15,7 +15,6 @@
 #![cfg_attr(core, no_std)]
 
 #![allow(deprecated)] // for deprecated `try!()` macro
-#![feature(concat_idents)]
 
 #[cfg(std)] use std::fmt;
 #[cfg(core)] use core::fmt;
@@ -80,17 +79,6 @@ fn concat() {
 }
 
 #[test]
-fn concat_idents() {
-    fn foo() {}
-    fn foobar() {}
-
-    concat_idents!(foo)();
-    concat_idents!(foo,)();
-    concat_idents!(foo, bar)();
-    concat_idents!(foo, bar,)();
-}
-
-#[test]
 fn debug_assert() {
     debug_assert!(true);
     debug_assert!(true, );
diff --git a/tests/ui/macros/macro-match-nonterminal.rs b/tests/ui/macros/macro-match-nonterminal.rs
index fa2af945a1f..1643cddb192 100644
--- a/tests/ui/macros/macro-match-nonterminal.rs
+++ b/tests/ui/macros/macro-match-nonterminal.rs
@@ -2,11 +2,10 @@ macro_rules! test {
     ($a, $b) => {
         //~^ ERROR missing fragment
         //~| ERROR missing fragment
-        //~| ERROR missing fragment
         ()
     };
 }
 
 fn main() {
-    test!()
+    test!() //~ ERROR unexpected end of macro invocation
 }
diff --git a/tests/ui/macros/macro-match-nonterminal.stderr b/tests/ui/macros/macro-match-nonterminal.stderr
index 8196d795c4c..a92d099ca00 100644
--- a/tests/ui/macros/macro-match-nonterminal.stderr
+++ b/tests/ui/macros/macro-match-nonterminal.stderr
@@ -24,7 +24,16 @@ help: try adding a specifier here
 LL |     ($a, $b:spec) => {
    |            +++++
 
-error: missing fragment specifier
+error: unexpected end of macro invocation
+  --> $DIR/macro-match-nonterminal.rs:10:5
+   |
+LL | macro_rules! test {
+   | ----------------- when calling this macro
+...
+LL |     test!()
+   |     ^^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$a:tt`
   --> $DIR/macro-match-nonterminal.rs:2:6
    |
 LL |     ($a, $b) => {
diff --git a/tests/ui/macros/macro-metavar-expr-concat/empty-input.rs b/tests/ui/macros/macro-metavar-expr-concat/empty-input.rs
new file mode 100644
index 00000000000..caad63c5f6b
--- /dev/null
+++ b/tests/ui/macros/macro-metavar-expr-concat/empty-input.rs
@@ -0,0 +1,12 @@
+// Issue 50403
+// Ensure that `concat` can't create empty identifiers
+// FIXME(macro_metavar_expr_concat): this error message could be improved
+
+macro_rules! empty {
+    () => { ${concat()} } //~ ERROR expected identifier or string literal
+                          //~^ERROR expected expression
+}
+
+fn main() {
+    let x = empty!();
+}
diff --git a/tests/ui/macros/macro-metavar-expr-concat/empty-input.stderr b/tests/ui/macros/macro-metavar-expr-concat/empty-input.stderr
new file mode 100644
index 00000000000..e95032dd247
--- /dev/null
+++ b/tests/ui/macros/macro-metavar-expr-concat/empty-input.stderr
@@ -0,0 +1,19 @@
+error: expected identifier or string literal
+  --> $DIR/empty-input.rs:6:14
+   |
+LL |     () => { ${concat()} }
+   |              ^^^^^^^^^^
+
+error: expected expression, found `$`
+  --> $DIR/empty-input.rs:6:13
+   |
+LL |     () => { ${concat()} }
+   |             ^ expected expression
+...
+LL |     let x = empty!();
+   |             -------- in this macro invocation
+   |
+   = note: this error originates in the macro `empty` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/macros/macro-missing-fragment-deduplication.rs b/tests/ui/macros/macro-missing-fragment-deduplication.rs
index 481f08fa111..fc81c713b4d 100644
--- a/tests/ui/macros/macro-missing-fragment-deduplication.rs
+++ b/tests/ui/macros/macro-missing-fragment-deduplication.rs
@@ -2,12 +2,11 @@
 
 macro_rules! m {
     ($name) => {}; //~ ERROR missing fragment
-                   //~| ERROR missing fragment
 }
 
 fn main() {
-    m!();
-    m!();
-    m!();
-    m!();
+    m!(); //~ ERROR unexpected end
+    m!(); //~ ERROR unexpected end
+    m!(); //~ ERROR unexpected end
+    m!(); //~ ERROR unexpected end
 }
diff --git a/tests/ui/macros/macro-missing-fragment-deduplication.stderr b/tests/ui/macros/macro-missing-fragment-deduplication.stderr
index 820f7eb3cf7..29d2ae0e16e 100644
--- a/tests/ui/macros/macro-missing-fragment-deduplication.stderr
+++ b/tests/ui/macros/macro-missing-fragment-deduplication.stderr
@@ -11,11 +11,65 @@ help: try adding a specifier here
 LL |     ($name:spec) => {};
    |           +++++
 
-error: missing fragment specifier
+error: unexpected end of macro invocation
+  --> $DIR/macro-missing-fragment-deduplication.rs:8:5
+   |
+LL | macro_rules! m {
+   | -------------- when calling this macro
+...
+LL |     m!();
+   |     ^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$name:tt`
+  --> $DIR/macro-missing-fragment-deduplication.rs:4:6
+   |
+LL |     ($name) => {};
+   |      ^^^^^
+
+error: unexpected end of macro invocation
+  --> $DIR/macro-missing-fragment-deduplication.rs:9:5
+   |
+LL | macro_rules! m {
+   | -------------- when calling this macro
+...
+LL |     m!();
+   |     ^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$name:tt`
+  --> $DIR/macro-missing-fragment-deduplication.rs:4:6
+   |
+LL |     ($name) => {};
+   |      ^^^^^
+
+error: unexpected end of macro invocation
+  --> $DIR/macro-missing-fragment-deduplication.rs:10:5
+   |
+LL | macro_rules! m {
+   | -------------- when calling this macro
+...
+LL |     m!();
+   |     ^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$name:tt`
+  --> $DIR/macro-missing-fragment-deduplication.rs:4:6
+   |
+LL |     ($name) => {};
+   |      ^^^^^
+
+error: unexpected end of macro invocation
+  --> $DIR/macro-missing-fragment-deduplication.rs:11:5
+   |
+LL | macro_rules! m {
+   | -------------- when calling this macro
+...
+LL |     m!();
+   |     ^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$name:tt`
   --> $DIR/macro-missing-fragment-deduplication.rs:4:6
    |
 LL |     ($name) => {};
    |      ^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/macros/macro-missing-fragment.rs b/tests/ui/macros/macro-missing-fragment.rs
index 533aa147bcb..7ed9074020e 100644
--- a/tests/ui/macros/macro-missing-fragment.rs
+++ b/tests/ui/macros/macro-missing-fragment.rs
@@ -2,7 +2,6 @@
 
 macro_rules! used_arm {
     ( $( any_token $field_rust_type )* ) => {}; //~ ERROR missing fragment
-                                                //~| ERROR missing fragment
 }
 
 macro_rules! used_macro_unused_arm {
diff --git a/tests/ui/macros/macro-missing-fragment.stderr b/tests/ui/macros/macro-missing-fragment.stderr
index 4a99d7d949c..886292378d1 100644
--- a/tests/ui/macros/macro-missing-fragment.stderr
+++ b/tests/ui/macros/macro-missing-fragment.stderr
@@ -12,7 +12,7 @@ LL |     ( $( any_token $field_rust_type:spec )* ) => {};
    |                                    +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:10:7
+  --> $DIR/macro-missing-fragment.rs:9:7
    |
 LL |     ( $name ) => {};
    |       ^^^^^
@@ -25,7 +25,7 @@ LL |     ( $name:spec ) => {};
    |            +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:14:7
+  --> $DIR/macro-missing-fragment.rs:13:7
    |
 LL |     ( $name ) => {};
    |       ^^^^^
@@ -37,11 +37,5 @@ help: try adding a specifier here
 LL |     ( $name:spec ) => {};
    |            +++++
 
-error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:4:20
-   |
-LL |     ( $( any_token $field_rust_type )* ) => {};
-   |                    ^^^^^^^^^^^^^^^^
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/macros/macro-reexport-removed.rs b/tests/ui/macros/macro-reexport-removed.rs
index c1267f14cd8..4a054686d77 100644
--- a/tests/ui/macros/macro-reexport-removed.rs
+++ b/tests/ui/macros/macro-reexport-removed.rs
@@ -1,5 +1,4 @@
 //@ aux-build:two_macros.rs
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
 
 #![feature(macro_reexport)] //~ ERROR feature has been removed
 
diff --git a/tests/ui/macros/macro-reexport-removed.stderr b/tests/ui/macros/macro-reexport-removed.stderr
index d4940eeb775..8130fe0c4bd 100644
--- a/tests/ui/macros/macro-reexport-removed.stderr
+++ b/tests/ui/macros/macro-reexport-removed.stderr
@@ -1,14 +1,14 @@
 error[E0557]: feature has been removed
-  --> $DIR/macro-reexport-removed.rs:4:12
+  --> $DIR/macro-reexport-removed.rs:3:12
    |
 LL | #![feature(macro_reexport)]
    |            ^^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.0.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/49982> for more information
+   = note: removed in 1.0.0; see <https://github.com/rust-lang/rust/pull/49982> for more information
    = note: subsumed by `pub use`
 
 error: cannot find attribute `macro_reexport` in this scope
-  --> $DIR/macro-reexport-removed.rs:6:3
+  --> $DIR/macro-reexport-removed.rs:5:3
    |
 LL | #[macro_reexport(macro_one)]
    |   ^^^^^^^^^^^^^^ help: a built-in attribute with a similar name exists: `macro_export`
diff --git a/tests/ui/macros/macros-nonfatal-errors.rs b/tests/ui/macros/macros-nonfatal-errors.rs
index 091d64ea5d9..1349d741510 100644
--- a/tests/ui/macros/macros-nonfatal-errors.rs
+++ b/tests/ui/macros/macros-nonfatal-errors.rs
@@ -3,9 +3,8 @@
 // test that errors in a (selection) of macros don't kill compilation
 // immediately, so that we get more errors listed at a time.
 
-#![feature(trace_macros, concat_idents)]
+#![feature(trace_macros)]
 #![feature(stmt_expr_attributes)]
-#![expect(deprecated)] // concat_idents is deprecated
 
 use std::arch::asm;
 
@@ -105,8 +104,6 @@ fn main() {
     asm!(invalid); //~ ERROR
     llvm_asm!(invalid); //~ ERROR
 
-    concat_idents!("not", "idents"); //~ ERROR
-
     option_env!(invalid); //~ ERROR
     env!(invalid); //~ ERROR
     env!(foo, abr, baz); //~ ERROR
diff --git a/tests/ui/macros/macros-nonfatal-errors.stderr b/tests/ui/macros/macros-nonfatal-errors.stderr
index 2f990cb24e2..bc34bd1c8ec 100644
--- a/tests/ui/macros/macros-nonfatal-errors.stderr
+++ b/tests/ui/macros/macros-nonfatal-errors.stderr
@@ -1,5 +1,5 @@
 error: the `#[default]` attribute may only be used on unit enum variants
-  --> $DIR/macros-nonfatal-errors.rs:14:5
+  --> $DIR/macros-nonfatal-errors.rs:13:5
    |
 LL |     #[default]
    |     ^^^^^^^^^^
@@ -7,7 +7,7 @@ LL |     #[default]
    = help: consider a manual implementation of `Default`
 
 error: the `#[default]` attribute may only be used on unit enum variants
-  --> $DIR/macros-nonfatal-errors.rs:19:36
+  --> $DIR/macros-nonfatal-errors.rs:18:36
    |
 LL | struct DefaultInnerAttrTupleStruct(#[default] ());
    |                                    ^^^^^^^^^^
@@ -15,7 +15,7 @@ LL | struct DefaultInnerAttrTupleStruct(#[default] ());
    = help: consider a manual implementation of `Default`
 
 error: the `#[default]` attribute may only be used on unit enum variants
-  --> $DIR/macros-nonfatal-errors.rs:23:1
+  --> $DIR/macros-nonfatal-errors.rs:22:1
    |
 LL | #[default]
    | ^^^^^^^^^^
@@ -23,7 +23,7 @@ LL | #[default]
    = help: consider a manual implementation of `Default`
 
 error: the `#[default]` attribute may only be used on unit enum variants
-  --> $DIR/macros-nonfatal-errors.rs:27:1
+  --> $DIR/macros-nonfatal-errors.rs:26:1
    |
 LL | #[default]
    | ^^^^^^^^^^
@@ -31,7 +31,7 @@ LL | #[default]
    = help: consider a manual implementation of `Default`
 
 error: the `#[default]` attribute may only be used on unit enum variants
-  --> $DIR/macros-nonfatal-errors.rs:37:11
+  --> $DIR/macros-nonfatal-errors.rs:36:11
    |
 LL |     Foo = #[default] 0,
    |           ^^^^^^^^^^
@@ -39,7 +39,7 @@ LL |     Foo = #[default] 0,
    = help: consider a manual implementation of `Default`
 
 error: the `#[default]` attribute may only be used on unit enum variants
-  --> $DIR/macros-nonfatal-errors.rs:38:14
+  --> $DIR/macros-nonfatal-errors.rs:37:14
    |
 LL |     Bar([u8; #[default] 1]),
    |              ^^^^^^^^^^
@@ -47,7 +47,7 @@ LL |     Bar([u8; #[default] 1]),
    = help: consider a manual implementation of `Default`
 
 error[E0665]: `#[derive(Default)]` on enum with no `#[default]`
-  --> $DIR/macros-nonfatal-errors.rs:43:10
+  --> $DIR/macros-nonfatal-errors.rs:42:10
    |
 LL |   #[derive(Default)]
    |            ^^^^^^^
@@ -67,7 +67,7 @@ LL |     #[default] Bar,
    |     ++++++++++
 
 error[E0665]: `#[derive(Default)]` on enum with no `#[default]`
-  --> $DIR/macros-nonfatal-errors.rs:49:10
+  --> $DIR/macros-nonfatal-errors.rs:48:10
    |
 LL |   #[derive(Default)]
    |            ^^^^^^^
@@ -78,7 +78,7 @@ LL | | }
    | |_- this enum needs a unit variant marked with `#[default]`
 
 error: multiple declared defaults
-  --> $DIR/macros-nonfatal-errors.rs:55:10
+  --> $DIR/macros-nonfatal-errors.rs:54:10
    |
 LL | #[derive(Default)]
    |          ^^^^^^^
@@ -95,7 +95,7 @@ LL |     Baz,
    = note: only one variant can be default
 
 error: `#[default]` attribute does not accept a value
-  --> $DIR/macros-nonfatal-errors.rs:67:5
+  --> $DIR/macros-nonfatal-errors.rs:66:5
    |
 LL |     #[default = 1]
    |     ^^^^^^^^^^^^^^
@@ -103,7 +103,7 @@ LL |     #[default = 1]
    = help: try using `#[default]`
 
 error: multiple `#[default]` attributes
-  --> $DIR/macros-nonfatal-errors.rs:75:5
+  --> $DIR/macros-nonfatal-errors.rs:74:5
    |
 LL |     #[default]
    |     ---------- `#[default]` used here
@@ -114,13 +114,13 @@ LL |     Foo,
    |
    = note: only one `#[default]` attribute is needed
 help: try removing this
-  --> $DIR/macros-nonfatal-errors.rs:74:5
+  --> $DIR/macros-nonfatal-errors.rs:73:5
    |
 LL |     #[default]
    |     ^^^^^^^^^^
 
 error: multiple `#[default]` attributes
-  --> $DIR/macros-nonfatal-errors.rs:85:5
+  --> $DIR/macros-nonfatal-errors.rs:84:5
    |
 LL |     #[default]
    |     ---------- `#[default]` used here
@@ -132,7 +132,7 @@ LL |     Foo,
    |
    = note: only one `#[default]` attribute is needed
 help: try removing these
-  --> $DIR/macros-nonfatal-errors.rs:82:5
+  --> $DIR/macros-nonfatal-errors.rs:81:5
    |
 LL |     #[default]
    |     ^^^^^^^^^^
@@ -142,7 +142,7 @@ LL |     #[default]
    |     ^^^^^^^^^^
 
 error: the `#[default]` attribute may only be used on unit enum variants
-  --> $DIR/macros-nonfatal-errors.rs:92:5
+  --> $DIR/macros-nonfatal-errors.rs:91:5
    |
 LL |     Foo {},
    |     ^^^
@@ -150,7 +150,7 @@ LL |     Foo {},
    = help: consider a manual implementation of `Default`
 
 error: default variant must be exhaustive
-  --> $DIR/macros-nonfatal-errors.rs:100:5
+  --> $DIR/macros-nonfatal-errors.rs:99:5
    |
 LL |     #[non_exhaustive]
    |     ----------------- declared `#[non_exhaustive]` here
@@ -160,37 +160,31 @@ LL |     Foo,
    = help: consider a manual implementation of `Default`
 
 error: asm template must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:105:10
+  --> $DIR/macros-nonfatal-errors.rs:104:10
    |
 LL |     asm!(invalid);
    |          ^^^^^^^
 
-error: `concat_idents!()` requires ident args
-  --> $DIR/macros-nonfatal-errors.rs:108:5
-   |
-LL |     concat_idents!("not", "idents");
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 error: argument must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:110:17
+  --> $DIR/macros-nonfatal-errors.rs:107:17
    |
 LL |     option_env!(invalid);
    |                 ^^^^^^^
 
 error: expected string literal
-  --> $DIR/macros-nonfatal-errors.rs:111:10
+  --> $DIR/macros-nonfatal-errors.rs:108:10
    |
 LL |     env!(invalid);
    |          ^^^^^^^
 
 error: `env!()` takes 1 or 2 arguments
-  --> $DIR/macros-nonfatal-errors.rs:112:5
+  --> $DIR/macros-nonfatal-errors.rs:109:5
    |
 LL |     env!(foo, abr, baz);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined at compile time
-  --> $DIR/macros-nonfatal-errors.rs:113:5
+  --> $DIR/macros-nonfatal-errors.rs:110:5
    |
 LL |     env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -198,7 +192,7 @@ LL |     env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST");
    = help: use `std::env::var("RUST_HOPEFULLY_THIS_DOESNT_EXIST")` to read the variable at run time
 
 error: format argument must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:115:13
+  --> $DIR/macros-nonfatal-errors.rs:112:13
    |
 LL |     format!(invalid);
    |             ^^^^^^^
@@ -209,43 +203,43 @@ LL |     format!("{}", invalid);
    |             +++++
 
 error: argument must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:117:14
+  --> $DIR/macros-nonfatal-errors.rs:114:14
    |
 LL |     include!(invalid);
    |              ^^^^^^^
 
 error: argument must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:119:18
+  --> $DIR/macros-nonfatal-errors.rs:116:18
    |
 LL |     include_str!(invalid);
    |                  ^^^^^^^
 
 error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG
-  --> $DIR/macros-nonfatal-errors.rs:120:5
+  --> $DIR/macros-nonfatal-errors.rs:117:5
    |
 LL |     include_str!("i'd be quite surprised if a file with this name existed");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: argument must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:121:20
+  --> $DIR/macros-nonfatal-errors.rs:118:20
    |
 LL |     include_bytes!(invalid);
    |                    ^^^^^^^
 
 error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG
-  --> $DIR/macros-nonfatal-errors.rs:122:5
+  --> $DIR/macros-nonfatal-errors.rs:119:5
    |
 LL |     include_bytes!("i'd be quite surprised if a file with this name existed");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: trace_macros! accepts only `true` or `false`
-  --> $DIR/macros-nonfatal-errors.rs:124:5
+  --> $DIR/macros-nonfatal-errors.rs:121:5
    |
 LL |     trace_macros!(invalid);
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: default variant must be exhaustive
-  --> $DIR/macros-nonfatal-errors.rs:134:9
+  --> $DIR/macros-nonfatal-errors.rs:131:9
    |
 LL |         #[non_exhaustive]
    |         ----------------- declared `#[non_exhaustive]` here
@@ -255,11 +249,11 @@ LL |         Foo,
    = help: consider a manual implementation of `Default`
 
 error: cannot find macro `llvm_asm` in this scope
-  --> $DIR/macros-nonfatal-errors.rs:106:5
+  --> $DIR/macros-nonfatal-errors.rs:105:5
    |
 LL |     llvm_asm!(invalid);
    |     ^^^^^^^^
 
-error: aborting due to 29 previous errors
+error: aborting due to 28 previous errors
 
 For more information about this error, try `rustc --explain E0665`.
diff --git a/tests/ui/macros/missing-writer-issue-139830.rs b/tests/ui/macros/missing-writer-issue-139830.rs
new file mode 100644
index 00000000000..da4608776c3
--- /dev/null
+++ b/tests/ui/macros/missing-writer-issue-139830.rs
@@ -0,0 +1,9 @@
+// Make sure we don't suggest a method change inside the `write!` macro.
+//
+// See <https://github.com/rust-lang/rust/issues/139830>
+
+fn main() {
+    let mut buf = String::new();
+    let _ = write!(buf, "foo");
+    //~^ ERROR cannot write into `String`
+}
diff --git a/tests/ui/macros/missing-writer-issue-139830.stderr b/tests/ui/macros/missing-writer-issue-139830.stderr
new file mode 100644
index 00000000000..34dd61328e0
--- /dev/null
+++ b/tests/ui/macros/missing-writer-issue-139830.stderr
@@ -0,0 +1,23 @@
+error[E0599]: cannot write into `String`
+  --> $DIR/missing-writer-issue-139830.rs:7:20
+   |
+LL |     let _ = write!(buf, "foo");
+   |                    ^^^
+  --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL
+   |
+   = note: the method is available for `String` here
+   |
+note: must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method
+  --> $DIR/missing-writer-issue-139830.rs:7:20
+   |
+LL |     let _ = write!(buf, "foo");
+   |                    ^^^
+   = help: items from traits can only be used if the trait is in scope
+help: trait `Write` which provides `write_fmt` is implemented but not in scope; perhaps you want to import it
+   |
+LL + use std::fmt::Write;
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/methods/suggest-convert-ptr-to-ref.stderr b/tests/ui/methods/suggest-convert-ptr-to-ref.stderr
index 7d52b20121e..8cb97ea458b 100644
--- a/tests/ui/methods/suggest-convert-ptr-to-ref.stderr
+++ b/tests/ui/methods/suggest-convert-ptr-to-ref.stderr
@@ -2,7 +2,7 @@ error[E0599]: `*const u8` doesn't implement `std::fmt::Display`
   --> $DIR/suggest-convert-ptr-to-ref.rs:5:22
    |
 LL |     println!("{}", z.to_string());
-   |                      ^^^^^^^^^ `*const u8` cannot be formatted with the default formatter
+   |                      ^^^^^^^^^ method cannot be called on `*const u8` due to unsatisfied trait bounds
    |
 note: the method `to_string` exists on the type `&u8`
   --> $SRC_DIR/alloc/src/string.rs:LL:COL
@@ -11,13 +11,12 @@ note: the method `to_string` exists on the type `&u8`
    = note: the following trait bounds were not satisfied:
            `*const u8: std::fmt::Display`
            which is required by `*const u8: ToString`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 
 error[E0599]: `*mut u8` doesn't implement `std::fmt::Display`
   --> $DIR/suggest-convert-ptr-to-ref.rs:8:22
    |
 LL |     println!("{}", t.to_string());
-   |                      ^^^^^^^^^ `*mut u8` cannot be formatted with the default formatter
+   |                      ^^^^^^^^^ method cannot be called on `*mut u8` due to unsatisfied trait bounds
    |
 note: the method `to_string` exists on the type `&&mut u8`
   --> $SRC_DIR/alloc/src/string.rs:LL:COL
@@ -26,7 +25,6 @@ note: the method `to_string` exists on the type `&&mut u8`
    = note: the following trait bounds were not satisfied:
            `*mut u8: std::fmt::Display`
            which is required by `*mut u8: ToString`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 
 error[E0599]: no method named `make_ascii_lowercase` found for raw pointer `*mut u8` in the current scope
   --> $DIR/suggest-convert-ptr-to-ref.rs:9:7
diff --git a/tests/crashes/131451.rs b/tests/ui/mir/unreachable-loop-jump-threading.rs
index cd5b44bad8a..8403906bb5c 100644
--- a/tests/crashes/131451.rs
+++ b/tests/ui/mir/unreachable-loop-jump-threading.rs
@@ -1,9 +1,10 @@
-//@ known-bug: #131451
+//@ build-pass
 //@ needs-rustc-debug-assertions
 //@ compile-flags: -Zmir-enable-passes=+GVN -Zmir-enable-passes=+JumpThreading --crate-type=lib
 
 pub fn fun(terminate: bool) {
     while true {}
+    //~^ WARN denote infinite loops with `loop { ... }`
 
     while !terminate {}
 }
diff --git a/tests/ui/mir/unreachable-loop-jump-threading.stderr b/tests/ui/mir/unreachable-loop-jump-threading.stderr
new file mode 100644
index 00000000000..21b174c8021
--- /dev/null
+++ b/tests/ui/mir/unreachable-loop-jump-threading.stderr
@@ -0,0 +1,10 @@
+warning: denote infinite loops with `loop { ... }`
+  --> $DIR/unreachable-loop-jump-threading.rs:6:5
+   |
+LL |     while true {}
+   |     ^^^^^^^^^^ help: use `loop`
+   |
+   = note: `#[warn(while_true)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/mismatched_types/method-help-unsatisfied-bound.stderr b/tests/ui/mismatched_types/method-help-unsatisfied-bound.stderr
index be3a3e2abf1..23bc9dc0f84 100644
--- a/tests/ui/mismatched_types/method-help-unsatisfied-bound.stderr
+++ b/tests/ui/mismatched_types/method-help-unsatisfied-bound.stderr
@@ -2,9 +2,8 @@ error[E0277]: `Foo` doesn't implement `Debug`
   --> $DIR/method-help-unsatisfied-bound.rs:5:7
    |
 LL |     a.unwrap();
-   |       ^^^^^^ `Foo` cannot be formatted using `{:?}`
+   |       ^^^^^^ the trait `Debug` is not implemented for `Foo`
    |
-   = help: the trait `Debug` is not implemented for `Foo`
    = note: add `#[derive(Debug)]` to `Foo` or manually `impl Debug for Foo`
 note: required by a bound in `Result::<T, E>::unwrap`
   --> $SRC_DIR/core/src/result.rs:LL:COL
diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs
index f589e88f68e..0632b822c55 100644
--- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs
+++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs
@@ -2,17 +2,17 @@
 #![crate_type = "lib"]
 
 pub fn foo(arg: Option<&Vec<i32>>) -> Option<&[i32]> {
-    arg //~ ERROR 5:5: 5:8: mismatched types [E0308]
+    arg //~ ERROR mismatched types [E0308]
 }
 
 pub fn bar(arg: Option<&Vec<i32>>) -> &[i32] {
-    arg.unwrap_or(&[]) //~ ERROR 9:19: 9:22: mismatched types [E0308]
+    arg.unwrap_or(&[]) //~ ERROR mismatched types [E0308]
 }
 
 pub fn barzz<'a>(arg: Option<&'a Vec<i32>>, v: &'a [i32]) -> &'a [i32] {
-    arg.unwrap_or(v) //~ ERROR 13:19: 13:20: mismatched types [E0308]
+    arg.unwrap_or(v) //~ ERROR mismatched types [E0308]
 }
 
 pub fn convert_result(arg: Result<&Vec<i32>, ()>) -> &[i32] {
-    arg.unwrap_or(&[]) //~ ERROR 17:19: 17:22: mismatched types [E0308]
+    arg.unwrap_or(&[]) //~ ERROR mismatched types [E0308]
 }
diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr
index 0d203c1aacb..802ac669a10 100644
--- a/tests/ui/modules/issue-107649.stderr
+++ b/tests/ui/modules/issue-107649.stderr
@@ -2,11 +2,10 @@ error[E0277]: `Dummy` doesn't implement `Debug`
    --> $DIR/issue-107649.rs:105:5
     |
 105 |     dbg!(lib::Dummy);
-    |     ^^^^^^^^^^^^^^^^ `Dummy` cannot be formatted using `{:?}`
+    |     ^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `Dummy`
     |
-    = help: the trait `Debug` is not implemented for `Dummy`
     = note: add `#[derive(Debug)]` to `Dummy` or manually `impl Debug for Dummy`
-    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
+    = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `Dummy` with `#[derive(Debug)]`
    --> $DIR/auxiliary/dummy_lib.rs:2:1
     |
diff --git a/tests/ui/never_type/never-type-fallback-option.rs b/tests/ui/never_type/never-type-fallback-option.rs
new file mode 100644
index 00000000000..9c8103aa0a4
--- /dev/null
+++ b/tests/ui/never_type/never-type-fallback-option.rs
@@ -0,0 +1,22 @@
+//@ run-pass
+
+#![allow(warnings)]
+
+//! Tests type inference fallback to `!` (never type) in `Option` context.
+//!
+//! Regression test for issues:
+//! - https://github.com/rust-lang/rust/issues/39808
+//! - https://github.com/rust-lang/rust/issues/39984
+//!
+//! Here the type of `c` is `Option<?T>`, where `?T` is unconstrained.
+//! Because there is data-flow from the `{ return; }` block, which
+//! diverges and hence has type `!`, into `c`, we will default `?T` to
+//! `!`, and hence this code compiles rather than failing and requiring
+//! a type annotation.
+
+fn main() {
+    let c = Some({
+        return;
+    });
+    c.unwrap();
+}
diff --git a/tests/ui/no_std/simple-runs.rs b/tests/ui/no_std/simple-runs.rs
index 8931ac7ed11..af44dfec311 100644
--- a/tests/ui/no_std/simple-runs.rs
+++ b/tests/ui/no_std/simple-runs.rs
@@ -4,6 +4,7 @@
 //@ compile-flags: -Cpanic=abort
 //@ ignore-wasm different `main` convention
 
+#![feature(lang_items)]
 #![no_std]
 #![no_main]
 
@@ -35,6 +36,17 @@ fn panic_handler(_info: &PanicInfo<'_>) -> ! {
     loop {}
 }
 
+#[lang = "eh_personality"]
+extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
+
 #[no_mangle]
 extern "C" fn main(_argc: c_int, _argv: *const *const c_char) -> c_int {
     0
diff --git a/tests/ui/on-unimplemented/no-debug.stderr b/tests/ui/on-unimplemented/no-debug.stderr
index 97d67dbd82e..5b0b060d40e 100644
--- a/tests/ui/on-unimplemented/no-debug.stderr
+++ b/tests/ui/on-unimplemented/no-debug.stderr
@@ -2,7 +2,9 @@ error[E0277]: `Foo` doesn't implement `Debug`
   --> $DIR/no-debug.rs:10:27
    |
 LL |     println!("{:?} {:?}", Foo, Bar);
-   |                           ^^^ `Foo` cannot be formatted using `{:?}`
+   |               ----        ^^^ `Foo` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               |
+   |               required by this formatting parameter
    |
    = help: the trait `Debug` is not implemented for `Foo`
    = note: add `#[derive(Debug)]` to `Foo` or manually `impl Debug for Foo`
@@ -17,7 +19,9 @@ error[E0277]: `Bar` doesn't implement `Debug`
   --> $DIR/no-debug.rs:10:32
    |
 LL |     println!("{:?} {:?}", Foo, Bar);
-   |                                ^^^ `Bar` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |                    ----        ^^^ `Bar` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |                    |
+   |                    required by this formatting parameter
    |
    = help: the trait `Debug` is not implemented for `Bar`
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -26,7 +30,9 @@ error[E0277]: `Foo` doesn't implement `std::fmt::Display`
   --> $DIR/no-debug.rs:11:23
    |
 LL |     println!("{} {}", Foo, Bar);
-   |                       ^^^ `Foo` cannot be formatted with the default formatter
+   |               --      ^^^ `Foo` cannot be formatted with the default formatter
+   |               |
+   |               required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `Foo`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
@@ -36,7 +42,9 @@ error[E0277]: `Bar` doesn't implement `std::fmt::Display`
   --> $DIR/no-debug.rs:11:28
    |
 LL |     println!("{} {}", Foo, Bar);
-   |                            ^^^ `Bar` cannot be formatted with the default formatter
+   |                  --        ^^^ `Bar` cannot be formatted with the default formatter
+   |                  |
+   |                  required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `Bar`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/panic-runtime/incompatible-type.rs b/tests/ui/panic-runtime/incompatible-type.rs
index 4cbcfec11c9..f82c23d68c2 100644
--- a/tests/ui/panic-runtime/incompatible-type.rs
+++ b/tests/ui/panic-runtime/incompatible-type.rs
@@ -21,4 +21,12 @@ pub fn test(_: DropMe) {
 }
 
 #[rustc_std_internal_symbol]
-pub unsafe extern "C" fn rust_eh_personality() {}
+pub unsafe extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
diff --git a/tests/ui/panics/location-detail-unwrap-multiline.rs b/tests/ui/panics/location-detail-unwrap-multiline.rs
index 56e1760d851..e49e11a6061 100644
--- a/tests/ui/panics/location-detail-unwrap-multiline.rs
+++ b/tests/ui/panics/location-detail-unwrap-multiline.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ compile-flags: -Cstrip=none -Cdebuginfo=line-tables-only -Copt-level=0
 //@ exec-env:RUST_BACKTRACE=1
-//@ regex-error-pattern: location-detail-unwrap-multiline\.rs:11(:10)?\n
+//@ regex-error-pattern: location-detail-unwrap-multiline\.rs:11(:10)?:\n
 //@ needs-unwind
 //@ ignore-android FIXME #17520
 
diff --git a/tests/ui/parser/bad-lit-suffixes.rs b/tests/ui/parser/bad-lit-suffixes.rs
index f29dc53d322..4e8edf4d46e 100644
--- a/tests/ui/parser/bad-lit-suffixes.rs
+++ b/tests/ui/parser/bad-lit-suffixes.rs
@@ -33,7 +33,6 @@ fn f() {}
 
 #[must_use = "string"suffix]
 //~^ ERROR suffixes on string literals are invalid
-//~| ERROR malformed `must_use` attribute input
 fn g() {}
 
 #[link(name = "string"suffix)]
diff --git a/tests/ui/parser/bad-lit-suffixes.stderr b/tests/ui/parser/bad-lit-suffixes.stderr
index 86ef35bf783..416143e496a 100644
--- a/tests/ui/parser/bad-lit-suffixes.stderr
+++ b/tests/ui/parser/bad-lit-suffixes.stderr
@@ -22,29 +22,14 @@ error: suffixes on string literals are invalid
 LL | #[must_use = "string"suffix]
    |              ^^^^^^^^^^^^^^ invalid suffix `suffix`
 
-error: malformed `must_use` attribute input
-  --> $DIR/bad-lit-suffixes.rs:34:1
-   |
-LL | #[must_use = "string"suffix]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the following are the possible correct uses
-   |
-LL - #[must_use = "string"suffix]
-LL + #[must_use = "reason"]
-   |
-LL - #[must_use = "string"suffix]
-LL + #[must_use]
-   |
-
 error: suffixes on string literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:39:15
+  --> $DIR/bad-lit-suffixes.rs:38:15
    |
 LL | #[link(name = "string"suffix)]
    |               ^^^^^^^^^^^^^^ invalid suffix `suffix`
 
 error: invalid suffix `suffix` for number literal
-  --> $DIR/bad-lit-suffixes.rs:43:41
+  --> $DIR/bad-lit-suffixes.rs:42:41
    |
 LL | #[rustc_layout_scalar_valid_range_start(0suffix)]
    |                                         ^^^^^^^ invalid suffix `suffix`
@@ -165,5 +150,5 @@ LL |     1.0e10suffix;
    |
    = help: valid suffixes are `f32` and `f64`
 
-error: aborting due to 21 previous errors; 2 warnings emitted
+error: aborting due to 20 previous errors; 2 warnings emitted
 
diff --git a/tests/ui/parser/issues/issue-105366.fixed b/tests/ui/parser/issues/issue-105366.fixed
index 7157b647524..95419dc07f2 100644
--- a/tests/ui/parser/issues/issue-105366.fixed
+++ b/tests/ui/parser/issues/issue-105366.fixed
@@ -1,5 +1,6 @@
 //@ run-rustfix
 
+#[allow(dead_code)]
 struct Foo;
 
 impl From<i32> for Foo {
diff --git a/tests/ui/parser/issues/issue-105366.rs b/tests/ui/parser/issues/issue-105366.rs
index dc3cb8b343d..3278b737991 100644
--- a/tests/ui/parser/issues/issue-105366.rs
+++ b/tests/ui/parser/issues/issue-105366.rs
@@ -1,5 +1,6 @@
 //@ run-rustfix
 
+#[allow(dead_code)]
 struct Foo;
 
 fn From<i32> for Foo {
diff --git a/tests/ui/parser/issues/issue-105366.stderr b/tests/ui/parser/issues/issue-105366.stderr
index d8c79a0e0ea..225e436b4aa 100644
--- a/tests/ui/parser/issues/issue-105366.stderr
+++ b/tests/ui/parser/issues/issue-105366.stderr
@@ -1,5 +1,5 @@
 error: you might have meant to write `impl` instead of `fn`
-  --> $DIR/issue-105366.rs:5:1
+  --> $DIR/issue-105366.rs:6:1
    |
 LL | fn From<i32> for Foo {
    | ^^
diff --git a/tests/ui/parser/macro/issue-33569.rs b/tests/ui/parser/macro/issue-33569.rs
index e0a5352ab06..7288fa858db 100644
--- a/tests/ui/parser/macro/issue-33569.rs
+++ b/tests/ui/parser/macro/issue-33569.rs
@@ -1,11 +1,10 @@
 macro_rules! foo {
     { $+ } => { //~ ERROR expected identifier, found `+`
                 //~^ ERROR missing fragment specifier
-                //~| ERROR missing fragment specifier
         $(x)(y) //~ ERROR expected one of: `*`, `+`, or `?`
     }
 }
 
-foo!();
+foo!(); //~ ERROR unexpected end
 
 fn main() {}
diff --git a/tests/ui/parser/macro/issue-33569.stderr b/tests/ui/parser/macro/issue-33569.stderr
index 0d53c04c1c9..dd8e38f0d6e 100644
--- a/tests/ui/parser/macro/issue-33569.stderr
+++ b/tests/ui/parser/macro/issue-33569.stderr
@@ -4,12 +4,6 @@ error: expected identifier, found `+`
 LL |     { $+ } => {
    |        ^
 
-error: expected one of: `*`, `+`, or `?`
-  --> $DIR/issue-33569.rs:5:13
-   |
-LL |         $(x)(y)
-   |             ^^^
-
 error: missing fragment specifier
   --> $DIR/issue-33569.rs:2:8
    |
@@ -23,7 +17,22 @@ help: try adding a specifier here
 LL |     { $+:spec } => {
    |         +++++
 
-error: missing fragment specifier
+error: expected one of: `*`, `+`, or `?`
+  --> $DIR/issue-33569.rs:4:13
+   |
+LL |         $(x)(y)
+   |             ^^^
+
+error: unexpected end of macro invocation
+  --> $DIR/issue-33569.rs:8:1
+   |
+LL | macro_rules! foo {
+   | ---------------- when calling this macro
+...
+LL | foo!();
+   | ^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$<!dummy!>:tt`
   --> $DIR/issue-33569.rs:2:8
    |
 LL |     { $+ } => {
diff --git a/tests/ui/parser/recover/recover-field-semi.rs b/tests/ui/parser/recover/recover-field-semi.rs
index b703578860e..b6f235f8ad1 100644
--- a/tests/ui/parser/recover/recover-field-semi.rs
+++ b/tests/ui/parser/recover/recover-field-semi.rs
@@ -3,7 +3,7 @@ struct Foo {
     //~^ ERROR struct fields are separated by `,`
 }
 
-union Bar { //~ ERROR
+union Bar {
     foo: i32;
     //~^ ERROR union fields are separated by `,`
 }
@@ -13,4 +13,6 @@ enum Baz {
     //~^ ERROR struct fields are separated by `,`
 }
 
-fn main() {}
+fn main() {
+    let _ = Foo { foo: "" }; //~ ERROR mismatched types
+}
diff --git a/tests/ui/parser/recover/recover-field-semi.stderr b/tests/ui/parser/recover/recover-field-semi.stderr
index 3cf4847488c..9b1a34e134b 100644
--- a/tests/ui/parser/recover/recover-field-semi.stderr
+++ b/tests/ui/parser/recover/recover-field-semi.stderr
@@ -22,14 +22,12 @@ LL |     Qux { foo: i32; }
    |     |
    |     while parsing this struct
 
-error: unions cannot have zero fields
-  --> $DIR/recover-field-semi.rs:6:1
+error[E0308]: mismatched types
+  --> $DIR/recover-field-semi.rs:17:24
    |
-LL | / union Bar {
-LL | |     foo: i32;
-LL | |
-LL | | }
-   | |_^
+LL |     let _ = Foo { foo: "" };
+   |                        ^^ expected `i32`, found `&str`
 
 error: aborting due to 4 previous errors
 
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr
new file mode 100644
index 00000000000..cc438461a5d
--- /dev/null
+++ b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr
@@ -0,0 +1,238 @@
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:39:14
+   |
+LL |     let foo = Foo::default();
+   |         --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo);
+   |                          --- value moved here
+LL |     foo_move(foo);
+   |              ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo);
+   |                          --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:43:14
+   |
+LL |     let foo = Foo::default();
+   |         --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo;
+   |                      --- value moved here
+LL |     foo_move(foo);
+   |              ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo;
+   |                      --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:52:14
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- value moved here
+LL |     foo_move(foo);
+   |              ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:56:14
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo; // ok
+   |                      --- value moved here
+LL |     foo_move(foo);
+   |              ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:68:14
+   |
+LL |     let foo = Foo::default();
+   |         --- binding `foo` declared here
+LL |     let x = &pin const foo; // ok
+   |             -------------- borrow of `foo` occurs here
+LL |     foo_move(foo);
+   |              ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_ref(x);
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin const foo; // ok
+   |                        --- you could clone this value
+
+error[E0382]: borrow of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:76:13
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- value moved here
+LL |     foo_ref(&foo);
+   |             ^^^^ value borrowed here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- you could clone this value
+
+error[E0382]: borrow of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:80:13
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo; // ok
+   |                      --- value moved here
+LL |     foo_ref(&foo);
+   |             ^^^^ value borrowed here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:99:26
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- value moved here
+LL |     foo_pin_mut(&pin mut foo);
+   |                          ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:103:26
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo; // ok
+   |                      --- value moved here
+LL |     foo_pin_mut(&pin mut foo);
+   |                          ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:115:26
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- binding `foo` declared here
+LL |     let x = &pin const foo; // ok
+   |             -------------- borrow of `foo` occurs here
+LL |     foo_pin_mut(&pin mut foo);
+   |                          ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_ref(x);
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin const foo; // ok
+   |                        --- you could clone this value
+
+error[E0382]: borrow of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:123:17
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- value moved here
+LL |     foo_pin_ref(&pin const foo);
+   |                 ^^^^^^^^^^^^^^ value borrowed here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- you could clone this value
+
+error[E0382]: borrow of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:127:17
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo; // ok
+   |                      --- value moved here
+LL |     foo_pin_ref(&pin const foo);
+   |                 ^^^^^^^^^^^^^^ value borrowed here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error: aborting due to 12 previous errors
+
+Some errors have detailed explanations: E0382, E0505.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/tests/ui/pin-ergonomics/borrow-unpin.rs b/tests/ui/pin-ergonomics/borrow-unpin.rs
new file mode 100644
index 00000000000..61e69bab12b
--- /dev/null
+++ b/tests/ui/pin-ergonomics/borrow-unpin.rs
@@ -0,0 +1,143 @@
+//@ revisions: unpin pinned
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+
+// For now, in order to ensure soundness, we move the place in `&pin mut place`
+// if `place` is not `Unpin`.
+// In the next step, we borrow the place instead of moving it, after that we
+// have to makes sure `&pin mut place` and `&pin const place` cannot violate
+// the mut-xor-share rules.
+
+use std::pin::Pin;
+use std::marker::PhantomPinned;
+
+#[cfg(pinned)]
+#[derive(Default)]
+struct Foo(PhantomPinned);
+
+#[cfg(unpin)]
+#[derive(Default)]
+struct Foo;
+
+fn foo_mut(_: &mut Foo) {
+}
+
+fn foo_ref(_: &Foo) {
+}
+
+fn foo_pin_mut(_: Pin<&mut Foo>) {
+}
+
+fn foo_pin_ref(_: Pin<&Foo>) {
+}
+
+fn foo_move(_: Foo) {}
+
+fn immutable_pin_mut_then_move() {
+    let foo = Foo::default();
+    foo_pin_mut(&pin mut foo); //[unpin]~ ERROR cannot borrow `foo` as mutable, as it is not declared as mutable
+    foo_move(foo); //[pinned]~ ERROR use of moved value: `foo`
+
+    let foo = Foo::default();
+    let x = &pin mut foo; //[unpin]~ ERROR cannot borrow `foo` as mutable, as it is not declared as mutable
+    foo_move(foo); //[pinned]~ ERROR use of moved value: `foo`
+    //[unpin]~^ ERROR cannot move out of `foo` because it is borrowed
+    foo_pin_mut(x); //
+}
+
+
+fn pin_mut_then_move() {
+    let mut foo = Foo::default();
+    foo_pin_mut(&pin mut foo); // ok
+    foo_move(foo); //[pinned]~ ERROR use of moved value: `foo`
+
+    let mut foo = Foo::default();
+    let x = &pin mut foo; // ok
+    foo_move(foo); //[pinned]~ ERROR use of moved value: `foo`
+    //[unpin]~^ ERROR cannot move out of `foo` because it is borrowed
+    foo_pin_mut(x); //
+}
+
+fn pin_ref_then_move() {
+    let foo = Foo::default();
+    foo_pin_ref(&pin const foo); // ok
+    foo_move(foo); // ok
+
+    let foo = Foo::default();
+    let x = &pin const foo; // ok
+    foo_move(foo); //[pinned]~ ERROR cannot move out of `foo` because it is borrowed
+    //[unpin]~^ ERROR cannot move out of `foo` because it is borrowed
+    foo_pin_ref(x);
+}
+
+fn pin_mut_then_ref() {
+    let mut foo = Foo::default();
+    foo_pin_mut(&pin mut foo); // ok
+    foo_ref(&foo); //[pinned]~ ERROR borrow of moved value: `foo`
+
+    let mut foo = Foo::default();
+    let x = &pin mut foo; // ok
+    foo_ref(&foo); //[pinned]~ ERROR borrow of moved value: `foo`
+    //[unpin]~^ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable
+    foo_pin_mut(x);
+}
+
+fn pin_ref_then_ref() {
+    let mut foo = Foo::default();
+    foo_pin_ref(&pin const foo); // ok
+    foo_ref(&foo); // ok
+
+    let mut foo = Foo::default();
+    let x = &pin const foo; // ok
+    foo_ref(&foo); // ok
+    foo_pin_ref(x);
+}
+
+fn pin_mut_then_pin_mut() {
+    let mut foo = Foo::default();
+    foo_pin_mut(&pin mut foo); // ok
+    foo_pin_mut(&pin mut foo); //[pinned]~ ERROR use of moved value: `foo`
+
+    let mut foo = Foo::default();
+    let x = &pin mut foo; // ok
+    foo_pin_mut(&pin mut foo); //[pinned]~ ERROR use of moved value: `foo`
+    //[unpin]~^ ERROR cannot borrow `foo` as mutable more than once at a time
+    foo_pin_mut(x);
+}
+
+fn pin_ref_then_pin_mut() {
+    let mut foo = Foo::default();
+    foo_pin_ref(&pin const foo); // ok
+    foo_pin_mut(&pin mut foo); // ok
+
+    let mut foo = Foo::default();
+    let x = &pin const foo; // ok
+    foo_pin_mut(&pin mut foo); //[pinned]~ ERROR cannot move out of `foo` because it is borrowed
+    //[unpin]~^ ERROR cannot borrow `foo` as mutable because it is also borrowed as immutable
+    foo_pin_ref(x);
+}
+
+fn pin_mut_then_pin_ref() {
+    let mut foo = Foo::default();
+    foo_pin_mut(&pin mut foo); // ok
+    foo_pin_ref(&pin const foo); //[pinned]~ ERROR borrow of moved value: `foo`
+
+    let mut foo = Foo::default();
+    let x = &pin mut foo; // ok
+    foo_pin_ref(&pin const foo); //[pinned]~ ERROR borrow of moved value: `foo`
+    //[unpin]~^ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable
+    foo_pin_mut(x);
+}
+
+fn pin_ref_then_pin_ref() {
+    let mut foo = Foo::default();
+    foo_pin_ref(&pin const foo); // ok
+    foo_pin_ref(&pin const foo); // ok
+
+    let mut foo = Foo::default();
+    let x = &pin const foo; // ok
+    foo_pin_ref(&pin const foo); // ok
+    foo_pin_ref(x);
+}
+
+fn main() {}
diff --git a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr
new file mode 100644
index 00000000000..bf9921343ee
--- /dev/null
+++ b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr
@@ -0,0 +1,136 @@
+error[E0596]: cannot borrow `foo` as mutable, as it is not declared as mutable
+  --> $DIR/borrow-unpin.rs:38:17
+   |
+LL |     foo_pin_mut(&pin mut foo);
+   |                 ^^^^^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut foo = Foo::default();
+   |         +++
+
+error[E0596]: cannot borrow `foo` as mutable, as it is not declared as mutable
+  --> $DIR/borrow-unpin.rs:42:13
+   |
+LL |     let x = &pin mut foo;
+   |             ^^^^^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut foo = Foo::default();
+   |         +++
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:43:14
+   |
+LL |     let foo = Foo::default();
+   |         --- binding `foo` declared here
+LL |     let x = &pin mut foo;
+   |             ------------ borrow of `foo` occurs here
+LL |     foo_move(foo);
+   |              ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_mut(x); //
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:20:1
+   |
+LL | struct Foo;
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo;
+   |                      --- you could clone this value
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:56:14
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- binding `foo` declared here
+LL |     let x = &pin mut foo; // ok
+   |             ------------ borrow of `foo` occurs here
+LL |     foo_move(foo);
+   |              ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_mut(x); //
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:20:1
+   |
+LL | struct Foo;
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:68:14
+   |
+LL |     let foo = Foo::default();
+   |         --- binding `foo` declared here
+LL |     let x = &pin const foo; // ok
+   |             -------------- borrow of `foo` occurs here
+LL |     foo_move(foo);
+   |              ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_ref(x);
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:20:1
+   |
+LL | struct Foo;
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin const foo; // ok
+   |                        --- you could clone this value
+
+error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
+  --> $DIR/borrow-unpin.rs:80:13
+   |
+LL |     let x = &pin mut foo; // ok
+   |             ------------ mutable borrow occurs here
+LL |     foo_ref(&foo);
+   |             ^^^^ immutable borrow occurs here
+LL |
+LL |     foo_pin_mut(x);
+   |                 - mutable borrow later used here
+
+error[E0499]: cannot borrow `foo` as mutable more than once at a time
+  --> $DIR/borrow-unpin.rs:103:17
+   |
+LL |     let x = &pin mut foo; // ok
+   |             ------------ first mutable borrow occurs here
+LL |     foo_pin_mut(&pin mut foo);
+   |                 ^^^^^^^^^^^^ second mutable borrow occurs here
+LL |
+LL |     foo_pin_mut(x);
+   |                 - first borrow later used here
+
+error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable
+  --> $DIR/borrow-unpin.rs:115:17
+   |
+LL |     let x = &pin const foo; // ok
+   |             -------------- immutable borrow occurs here
+LL |     foo_pin_mut(&pin mut foo);
+   |                 ^^^^^^^^^^^^ mutable borrow occurs here
+LL |
+LL |     foo_pin_ref(x);
+   |                 - immutable borrow later used here
+
+error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
+  --> $DIR/borrow-unpin.rs:127:17
+   |
+LL |     let x = &pin mut foo; // ok
+   |             ------------ mutable borrow occurs here
+LL |     foo_pin_ref(&pin const foo);
+   |                 ^^^^^^^^^^^^^^ immutable borrow occurs here
+LL |
+LL |     foo_pin_mut(x);
+   |                 - mutable borrow later used here
+
+error: aborting due to 9 previous errors
+
+Some errors have detailed explanations: E0499, E0502, E0505, E0596.
+For more information about an error, try `rustc --explain E0499`.
diff --git a/tests/ui/pin-ergonomics/borrow.rs b/tests/ui/pin-ergonomics/borrow.rs
new file mode 100644
index 00000000000..f221165848b
--- /dev/null
+++ b/tests/ui/pin-ergonomics/borrow.rs
@@ -0,0 +1,38 @@
+//@ check-pass
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+
+// Makes sure we can handle `&pin mut place` and `&pin const place` as sugar for
+// `std::pin::pin!(place)` and `Pin::new(&place)`.
+
+use std::pin::Pin;
+
+struct Foo;
+
+fn foo_pin_mut(_: Pin<&mut Foo>) {
+}
+
+fn foo_pin_ref(_: Pin<&Foo>) {
+}
+
+fn bar() {
+    let mut x: Pin<&mut _> = &pin mut Foo;
+    foo_pin_mut(x.as_mut());
+    foo_pin_mut(x.as_mut());
+    foo_pin_ref(x);
+
+    let x: Pin<&_> = &pin const Foo;
+
+    foo_pin_ref(x);
+    foo_pin_ref(x);
+}
+
+fn baz(mut x: Foo, y: Foo) {
+    let _x = &pin mut x;
+    let _x = x; // ok because `Foo: Unpin` and thus `&pin mut x` doesn't move `x`
+
+    let _y = &pin const y;
+    let _y = y; // ok because `&pin const y` dosn't move `y`
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.rs b/tests/ui/pin-ergonomics/coerce-non-pointer-pin.rs
index a95665f126d..a95665f126d 100644
--- a/tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.rs
+++ b/tests/ui/pin-ergonomics/coerce-non-pointer-pin.rs
diff --git a/tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.stderr b/tests/ui/pin-ergonomics/coerce-non-pointer-pin.stderr
index 2deb5b09884..2deb5b09884 100644
--- a/tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.stderr
+++ b/tests/ui/pin-ergonomics/coerce-non-pointer-pin.stderr
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-arg.rs b/tests/ui/pin-ergonomics/reborrow-arg.rs
index 2008bd1f52d..2008bd1f52d 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-arg.rs
+++ b/tests/ui/pin-ergonomics/reborrow-arg.rs
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.rs b/tests/ui/pin-ergonomics/reborrow-const-as-mut.rs
index 27c70a7b4df..27c70a7b4df 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.rs
+++ b/tests/ui/pin-ergonomics/reborrow-const-as-mut.rs
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.stderr b/tests/ui/pin-ergonomics/reborrow-const-as-mut.stderr
index 36bbf1c493a..36bbf1c493a 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.stderr
+++ b/tests/ui/pin-ergonomics/reborrow-const-as-mut.stderr
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-once.rs b/tests/ui/pin-ergonomics/reborrow-once.rs
index 241efadef7d..241efadef7d 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-once.rs
+++ b/tests/ui/pin-ergonomics/reborrow-once.rs
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-once.stderr b/tests/ui/pin-ergonomics/reborrow-once.stderr
index dc8e424ad2a..dc8e424ad2a 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-once.stderr
+++ b/tests/ui/pin-ergonomics/reborrow-once.stderr
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-self.rs b/tests/ui/pin-ergonomics/reborrow-self.rs
index ee617617da0..ee617617da0 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-self.rs
+++ b/tests/ui/pin-ergonomics/reborrow-self.rs
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-shorter.rs b/tests/ui/pin-ergonomics/reborrow-shorter.rs
index 06c266e0035..06c266e0035 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-shorter.rs
+++ b/tests/ui/pin-ergonomics/reborrow-shorter.rs
diff --git a/tests/ui/async-await/pin-ergonomics/sugar-ambiguity.rs b/tests/ui/pin-ergonomics/sugar-ambiguity.rs
index d183000931e..d183000931e 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar-ambiguity.rs
+++ b/tests/ui/pin-ergonomics/sugar-ambiguity.rs
diff --git a/tests/ui/async-await/pin-ergonomics/sugar-no-const.rs b/tests/ui/pin-ergonomics/sugar-no-const.rs
index dd6456b6034..dd6456b6034 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar-no-const.rs
+++ b/tests/ui/pin-ergonomics/sugar-no-const.rs
diff --git a/tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr b/tests/ui/pin-ergonomics/sugar-no-const.stderr
index 062b6d3f487..062b6d3f487 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr
+++ b/tests/ui/pin-ergonomics/sugar-no-const.stderr
diff --git a/tests/ui/async-await/pin-ergonomics/sugar-self.rs b/tests/ui/pin-ergonomics/sugar-self.rs
index 3d71b54b1ae..3d71b54b1ae 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar-self.rs
+++ b/tests/ui/pin-ergonomics/sugar-self.rs
diff --git a/tests/ui/async-await/pin-ergonomics/sugar.rs b/tests/ui/pin-ergonomics/sugar.rs
index 8dbdec418b1..8dbdec418b1 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar.rs
+++ b/tests/ui/pin-ergonomics/sugar.rs
diff --git a/tests/ui/print-calling-conventions.stdout b/tests/ui/print-calling-conventions.stdout
index 7b5ae495660..b8b939e1c04 100644
--- a/tests/ui/print-calling-conventions.stdout
+++ b/tests/ui/print-calling-conventions.stdout
@@ -1,6 +1,4 @@
 C
-C-cmse-nonsecure-call
-C-cmse-nonsecure-entry
 C-unwind
 Rust
 aapcs
@@ -9,6 +7,8 @@ avr-interrupt
 avr-non-blocking-interrupt
 cdecl
 cdecl-unwind
+cmse-nonsecure-call
+cmse-nonsecure-entry
 custom
 efiapi
 fastcall
@@ -20,6 +20,7 @@ riscv-interrupt-m
 riscv-interrupt-s
 rust-call
 rust-cold
+rust-invalid
 stdcall
 stdcall-unwind
 system
diff --git a/tests/ui/range/range-1.stderr b/tests/ui/range/range-1.stderr
index 37669dd3f47..8878ba14309 100644
--- a/tests/ui/range/range-1.stderr
+++ b/tests/ui/range/range-1.stderr
@@ -10,16 +10,6 @@ error[E0277]: the trait bound `bool: Step` is not satisfied
 LL |     for i in false..true {}
    |              ^^^^^^^^^^^ the trait `Step` is not implemented for `bool`
    |
-   = help: the following other types implement trait `Step`:
-             Char
-             Ipv4Addr
-             Ipv6Addr
-             char
-             i128
-             i16
-             i32
-             i64
-           and 8 others
    = note: required for `std::ops::Range<bool>` to implement `Iterator`
    = note: required for `std::ops::Range<bool>` to implement `IntoIterator`
 
diff --git a/tests/ui/repr/attr-usage-repr.rs b/tests/ui/repr/attr-usage-repr.rs
index cbf99f16e03..ca63ac564fc 100644
--- a/tests/ui/repr/attr-usage-repr.rs
+++ b/tests/ui/repr/attr-usage-repr.rs
@@ -45,7 +45,7 @@ enum EInt {
     B,
 }
 
-#[repr()] //~ ERROR attribute should be applied to a struct, enum, function, associated function, or union [E0517]
+#[repr()] //~ ERROR attribute should be applied to a struct, enum, or union [E0517]
 type SirThisIsAType = i32;
 
 #[repr()]
diff --git a/tests/ui/repr/attr-usage-repr.stderr b/tests/ui/repr/attr-usage-repr.stderr
index a25e68c483f..a62992c597a 100644
--- a/tests/ui/repr/attr-usage-repr.stderr
+++ b/tests/ui/repr/attr-usage-repr.stderr
@@ -36,13 +36,13 @@ LL | |     B,
 LL | | }
    | |_- not a struct
 
-error[E0517]: attribute should be applied to a struct, enum, function, associated function, or union
+error[E0517]: attribute should be applied to a struct, enum, or union
   --> $DIR/attr-usage-repr.rs:48:1
    |
 LL | #[repr()]
    | ^^^^^^^^^
 LL | type SirThisIsAType = i32;
-   | -------------------------- not a struct, enum, function, associated function, or union
+   | -------------------------- not a struct, enum, or union
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/repr/malformed-repr-hints.stderr b/tests/ui/repr/malformed-repr-hints.stderr
index 7a6e9ccc73e..6fb92755761 100644
--- a/tests/ui/repr/malformed-repr-hints.stderr
+++ b/tests/ui/repr/malformed-repr-hints.stderr
@@ -1,15 +1,3 @@
-error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
-  --> $DIR/malformed-repr-hints.rs:14:8
-   |
-LL | #[repr(align(2, 4))]
-   |        ^^^^^^^^^^^
-
-error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
-  --> $DIR/malformed-repr-hints.rs:18:8
-   |
-LL | #[repr(align())]
-   |        ^^^^^^^
-
 error[E0552]: incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all
   --> $DIR/malformed-repr-hints.rs:6:8
    |
@@ -22,6 +10,18 @@ error[E0589]: invalid `repr(align)` attribute: `align` needs an argument
 LL | #[repr(align)]
    |        ^^^^^ help: supply an argument here: `align(...)`
 
+error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
+  --> $DIR/malformed-repr-hints.rs:14:8
+   |
+LL | #[repr(align(2, 4))]
+   |        ^^^^^^^^^^^
+
+error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
+  --> $DIR/malformed-repr-hints.rs:18:8
+   |
+LL | #[repr(align())]
+   |        ^^^^^^^
+
 error[E0552]: invalid representation hint: `Rust` does not take a parenthesized argument list
   --> $DIR/malformed-repr-hints.rs:23:8
    |
diff --git a/tests/ui/repr/repr.stderr b/tests/ui/repr/repr.stderr
index f3b11398eaa..9e581332278 100644
--- a/tests/ui/repr/repr.stderr
+++ b/tests/ui/repr/repr.stderr
@@ -5,7 +5,7 @@ LL | #[repr]
    | ^^^^^^^
    | |
    | expected this to be a list
-   | help: must be of the form: `#[repr(C)]`
+   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error[E0539]: malformed `repr` attribute input
   --> $DIR/repr.rs:4:1
@@ -14,7 +14,7 @@ LL | #[repr = "B"]
    | ^^^^^^^^^^^^^
    | |
    | expected this to be a list
-   | help: must be of the form: `#[repr(C)]`
+   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error[E0539]: malformed `repr` attribute input
   --> $DIR/repr.rs:7:1
@@ -23,7 +23,7 @@ LL | #[repr = "C"]
    | ^^^^^^^^^^^^^
    | |
    | expected this to be a list
-   | help: must be of the form: `#[repr(C)]`
+   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr
index e22d812c8b0..6088945b829 100644
--- a/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr
+++ b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr
@@ -1,8 +1,12 @@
-error: malformed `track_caller` attribute input
+error[E0565]: malformed `track_caller` attribute input
   --> $DIR/error-odd-syntax.rs:1:1
    |
 LL | #[track_caller(1)]
-   | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[track_caller]`
+   | ^^^^^^^^^^^^^^---^
+   | |             |
+   | |             didn't expect any arguments here
+   | help: must be of the form: `#[track_caller]`
 
 error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0565`.
diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr
index d3cafbc6350..30360806138 100644
--- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr
+++ b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr
@@ -1,17 +1,17 @@
 error[E0736]: attribute incompatible with `#[unsafe(naked)]`
-  --> $DIR/error-with-naked.rs:5:1
+  --> $DIR/error-with-naked.rs:5:3
    |
 LL | #[track_caller]
-   | ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]`
+   |   ^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]`
 LL |
 LL | #[unsafe(naked)]
    | ---------------- function marked with `#[unsafe(naked)]` here
 
 error[E0736]: attribute incompatible with `#[unsafe(naked)]`
-  --> $DIR/error-with-naked.rs:17:5
+  --> $DIR/error-with-naked.rs:17:7
    |
 LL |     #[track_caller]
-   |     ^^^^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]`
+   |       ^^^^^^^^^^^^ the `track_caller` attribute is incompatible with `#[unsafe(naked)]`
 LL |
 LL |     #[unsafe(naked)]
    |     ---------------- function marked with `#[unsafe(naked)]` here
diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-ref-impl.rs b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-ref-impl.rs
new file mode 100644
index 00000000000..c6e38c0758d
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-ref-impl.rs
@@ -0,0 +1,17 @@
+/// Check that only `&X: Debug` is required, not `X: Debug`
+//@check-pass
+
+use std::fmt::Debug;
+use std::fmt::Formatter;
+
+struct X;
+
+impl Debug for &X {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
+        f.write_str("X")
+    }
+}
+
+fn main() {
+    dbg!(X);
+}
diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs
index f2fb62d76f3..fe71f106fdf 100644
--- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs
+++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs
@@ -1,4 +1,7 @@
 // Test ensuring that `dbg!(expr)` requires the passed type to implement `Debug`.
+//
+// `dbg!` shouldn't tell the user about format literal syntax; the user didn't write one.
+//@ forbid-output: cannot be formatted using
 
 struct NotDebug;
 
diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr
index 7ec018a95cc..4e0ae918415 100644
--- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr
+++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr
@@ -1,12 +1,11 @@
 error[E0277]: `NotDebug` doesn't implement `Debug`
-  --> $DIR/dbg-macro-requires-debug.rs:6:23
+  --> $DIR/dbg-macro-requires-debug.rs:9:23
    |
 LL |     let _: NotDebug = dbg!(NotDebug);
-   |                       ^^^^^^^^^^^^^^ `NotDebug` cannot be formatted using `{:?}`
+   |                       ^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `NotDebug`
    |
-   = help: the trait `Debug` is not implemented for `NotDebug`
    = note: add `#[derive(Debug)]` to `NotDebug` or manually `impl Debug for NotDebug`
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `NotDebug` with `#[derive(Debug)]`
    |
 LL + #[derive(Debug)]
diff --git a/tests/ui/rustdoc/renamed-features-rustdoc_internals.rs b/tests/ui/rustdoc/renamed-features-rustdoc_internals.rs
index 2257130280d..739c624d0c6 100644
--- a/tests/ui/rustdoc/renamed-features-rustdoc_internals.rs
+++ b/tests/ui/rustdoc/renamed-features-rustdoc_internals.rs
@@ -1,5 +1,3 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![feature(doc_keyword)] //~ ERROR
 #![feature(doc_primitive)] //~ ERROR
 #![crate_type = "lib"]
diff --git a/tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr b/tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr
index 9c664da8ee6..0608a8b58a2 100644
--- a/tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr
+++ b/tests/ui/rustdoc/renamed-features-rustdoc_internals.stderr
@@ -1,19 +1,19 @@
 error[E0557]: feature has been removed
-  --> $DIR/renamed-features-rustdoc_internals.rs:3:12
+  --> $DIR/renamed-features-rustdoc_internals.rs:1:12
    |
 LL | #![feature(doc_keyword)]
    |            ^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.58.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/90420> for more information
+   = note: removed in 1.58.0; see <https://github.com/rust-lang/rust/pull/90420> for more information
    = note: merged into `#![feature(rustdoc_internals)]`
 
 error[E0557]: feature has been removed
-  --> $DIR/renamed-features-rustdoc_internals.rs:4:12
+  --> $DIR/renamed-features-rustdoc_internals.rs:2:12
    |
 LL | #![feature(doc_primitive)]
    |            ^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in 1.58.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/90420> for more information
+   = note: removed in 1.58.0; see <https://github.com/rust-lang/rust/pull/90420> for more information
    = note: merged into `#![feature(rustdoc_internals)]`
 
 error: aborting due to 2 previous errors
diff --git a/tests/ui/sanitizer/cfi/invalid-attr-encoding.rs b/tests/ui/sanitizer/cfi/invalid-attr-encoding.rs
index 7ef6bd2f0ac..23ffabad62f 100644
--- a/tests/ui/sanitizer/cfi/invalid-attr-encoding.rs
+++ b/tests/ui/sanitizer/cfi/invalid-attr-encoding.rs
@@ -7,5 +7,5 @@
 #![no_core]
 #![no_main]
 
-#[cfi_encoding] //~ERROR 10:1: 10:16: malformed `cfi_encoding` attribute input
+#[cfi_encoding] //~ ERROR malformed `cfi_encoding` attribute input
 pub struct Type1(i32);
diff --git a/tests/ui/sized/unsized-binding.rs b/tests/ui/sized/unsized-binding.rs
index 3b99b0f6e96..ce6c1527376 100644
--- a/tests/ui/sized/unsized-binding.rs
+++ b/tests/ui/sized/unsized-binding.rs
@@ -1,5 +1,5 @@
 fn main() {
     let x = *""; //~ ERROR E0277
-    println!("{}", x);
-    println!("{}", x);
+    drop(x);
+    drop(x);
 }
diff --git a/tests/ui/span/issue-71363.stderr b/tests/ui/span/issue-71363.stderr
index 90b623e89cf..31069914daa 100644
--- a/tests/ui/span/issue-71363.stderr
+++ b/tests/ui/span/issue-71363.stderr
@@ -2,10 +2,8 @@ error[E0277]: `MyError` doesn't implement `std::fmt::Display`
  --> $DIR/issue-71363.rs:4:28
   |
 4 | impl std::error::Error for MyError {}
-  |                            ^^^^^^^ `MyError` cannot be formatted with the default formatter
+  |                            ^^^^^^^ the trait `std::fmt::Display` is not implemented for `MyError`
   |
-  = help: the trait `std::fmt::Display` is not implemented for `MyError`
-  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `std::error::Error`
  --> $SRC_DIR/core/src/error.rs:LL:COL
 
@@ -13,9 +11,8 @@ error[E0277]: `MyError` doesn't implement `Debug`
  --> $DIR/issue-71363.rs:4:28
   |
 4 | impl std::error::Error for MyError {}
-  |                            ^^^^^^^ `MyError` cannot be formatted using `{:?}`
+  |                            ^^^^^^^ the trait `Debug` is not implemented for `MyError`
   |
-  = help: the trait `Debug` is not implemented for `MyError`
   = note: add `#[derive(Debug)]` to `MyError` or manually `impl Debug for MyError`
 note: required by a bound in `std::error::Error`
  --> $SRC_DIR/core/src/error.rs:LL:COL
diff --git a/tests/ui/statics/read_before_init.rs b/tests/ui/statics/read_before_init.rs
new file mode 100644
index 00000000000..d779ef6dffa
--- /dev/null
+++ b/tests/ui/statics/read_before_init.rs
@@ -0,0 +1,22 @@
+//! This test checks the one code path that does not go through
+//! the regular CTFE memory access (as an optimization). We forgot
+//! to duplicate the static item self-initialization check, allowing
+//! reading from the uninitialized static memory before it was
+//! initialized at the end of the static initializer.
+//!
+//! https://github.com/rust-lang/rust/issues/142532
+
+use std::mem::MaybeUninit;
+
+pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
+//~^ ERROR: encountered static that tried to initialize itself with itself
+
+const fn foo(x: &i32) -> MaybeUninit<i32> {
+    let mut temp = MaybeUninit::<i32>::uninit();
+    unsafe {
+        std::ptr::copy(x, temp.as_mut_ptr(), 1);
+    }
+    temp
+}
+
+fn main() {}
diff --git a/tests/ui/statics/read_before_init.stderr b/tests/ui/statics/read_before_init.stderr
new file mode 100644
index 00000000000..aeebcf7d9ce
--- /dev/null
+++ b/tests/ui/statics/read_before_init.stderr
@@ -0,0 +1,17 @@
+error[E0080]: encountered static that tried to initialize itself with itself
+  --> $DIR/read_before_init.rs:11:45
+   |
+LL | pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
+   |                                             ^^^^^^^^^ evaluation of `X` failed inside this call
+   |
+note: inside `foo`
+  --> $DIR/read_before_init.rs:17:9
+   |
+LL |         std::ptr::copy(x, temp.as_mut_ptr(), 1);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `std::ptr::copy::<i32>`
+  --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr
index 88f91bef30b..b3b8784fa27 100644
--- a/tests/ui/stats/input-stats.stderr
+++ b/tests/ui/stats/input-stats.stderr
@@ -1,52 +1,7 @@
-ast-stats POST EXPANSION AST STATS
+ast-stats ================================================================
+ast-stats POST EXPANSION AST STATS: input_stats
 ast-stats Name                Accumulated Size         Count     Item Size
 ast-stats ----------------------------------------------------------------
-ast-stats Crate                     40 (NN.N%)             1            40
-ast-stats GenericArgs               40 (NN.N%)             1            40
-ast-stats - AngleBracketed            40 (NN.N%)             1
-ast-stats ExprField                 48 (NN.N%)             1            48
-ast-stats WherePredicate            72 (NN.N%)             1            72
-ast-stats - BoundPredicate            72 (NN.N%)             1
-ast-stats ForeignItem               80 (NN.N%)             1            80
-ast-stats - Fn                        80 (NN.N%)             1
-ast-stats Arm                       96 (NN.N%)             2            48
-ast-stats Local                     96 (NN.N%)             1            96
-ast-stats FnDecl                   120 (NN.N%)             5            24
-ast-stats InlineAsm                120 (NN.N%)             1           120
-ast-stats Attribute                128 (NN.N%)             4            32
-ast-stats - DocComment                32 (NN.N%)             1
-ast-stats - Normal                    96 (NN.N%)             3
-ast-stats Param                    160 (NN.N%)             4            40
-ast-stats Stmt                     160 (NN.N%)             5            32
-ast-stats - Let                       32 (NN.N%)             1
-ast-stats - Semi                      32 (NN.N%)             1
-ast-stats - Expr                      96 (NN.N%)             3
-ast-stats Block                    192 (NN.N%)             6            32
-ast-stats FieldDef                 208 (NN.N%)             2           104
-ast-stats Variant                  208 (NN.N%)             2           104
-ast-stats AssocItem                320 (NN.N%)             4            80
-ast-stats - Fn                       160 (NN.N%)             2
-ast-stats - Type                     160 (NN.N%)             2
-ast-stats GenericBound             352 (NN.N%)             4            88
-ast-stats - Trait                    352 (NN.N%)             4
-ast-stats GenericParam             480 (NN.N%)             5            96
-ast-stats Pat                      504 (NN.N%)             7            72
-ast-stats - Struct                    72 (NN.N%)             1
-ast-stats - Wild                      72 (NN.N%)             1
-ast-stats - Ident                    360 (NN.N%)             5
-ast-stats Expr                     648 (NN.N%)             9            72
-ast-stats - InlineAsm                 72 (NN.N%)             1
-ast-stats - Match                     72 (NN.N%)             1
-ast-stats - Path                      72 (NN.N%)             1
-ast-stats - Struct                    72 (NN.N%)             1
-ast-stats - Lit                      144 (NN.N%)             2
-ast-stats - Block                    216 (NN.N%)             3
-ast-stats PathSegment              864 (NN.N%)            36            24
-ast-stats Ty                       896 (NN.N%)            14            64
-ast-stats - Ptr                       64 (NN.N%)             1
-ast-stats - Ref                       64 (NN.N%)             1
-ast-stats - ImplicitSelf             128 (NN.N%)             2
-ast-stats - Path                     640 (NN.N%)            10
 ast-stats Item                   1_584 (NN.N%)            11           144
 ast-stats - Enum                     144 (NN.N%)             1
 ast-stats - ExternCrate              144 (NN.N%)             1
@@ -55,57 +10,61 @@ ast-stats - Impl                     144 (NN.N%)             1
 ast-stats - Trait                    144 (NN.N%)             1
 ast-stats - Fn                       288 (NN.N%)             2
 ast-stats - Use                      576 (NN.N%)             4
+ast-stats Ty                       896 (NN.N%)            14            64
+ast-stats - Ptr                       64 (NN.N%)             1
+ast-stats - Ref                       64 (NN.N%)             1
+ast-stats - ImplicitSelf             128 (NN.N%)             2
+ast-stats - Path                     640 (NN.N%)            10
+ast-stats PathSegment              864 (NN.N%)            36            24
+ast-stats Expr                     648 (NN.N%)             9            72
+ast-stats - InlineAsm                 72 (NN.N%)             1
+ast-stats - Match                     72 (NN.N%)             1
+ast-stats - Path                      72 (NN.N%)             1
+ast-stats - Struct                    72 (NN.N%)             1
+ast-stats - Lit                      144 (NN.N%)             2
+ast-stats - Block                    216 (NN.N%)             3
+ast-stats Pat                      504 (NN.N%)             7            72
+ast-stats - Struct                    72 (NN.N%)             1
+ast-stats - Wild                      72 (NN.N%)             1
+ast-stats - Ident                    360 (NN.N%)             5
+ast-stats GenericParam             480 (NN.N%)             5            96
+ast-stats GenericBound             352 (NN.N%)             4            88
+ast-stats - Trait                    352 (NN.N%)             4
+ast-stats AssocItem                320 (NN.N%)             4            80
+ast-stats - Fn                       160 (NN.N%)             2
+ast-stats - Type                     160 (NN.N%)             2
+ast-stats Variant                  208 (NN.N%)             2           104
+ast-stats FieldDef                 208 (NN.N%)             2           104
+ast-stats Block                    192 (NN.N%)             6            32
+ast-stats Stmt                     160 (NN.N%)             5            32
+ast-stats - Let                       32 (NN.N%)             1
+ast-stats - Semi                      32 (NN.N%)             1
+ast-stats - Expr                      96 (NN.N%)             3
+ast-stats Param                    160 (NN.N%)             4            40
+ast-stats Attribute                128 (NN.N%)             4            32
+ast-stats - DocComment                32 (NN.N%)             1
+ast-stats - Normal                    96 (NN.N%)             3
+ast-stats InlineAsm                120 (NN.N%)             1           120
+ast-stats FnDecl                   120 (NN.N%)             5            24
+ast-stats Local                     96 (NN.N%)             1            96
+ast-stats Arm                       96 (NN.N%)             2            48
+ast-stats ForeignItem               80 (NN.N%)             1            80
+ast-stats - Fn                        80 (NN.N%)             1
+ast-stats WherePredicate            72 (NN.N%)             1            72
+ast-stats - BoundPredicate            72 (NN.N%)             1
+ast-stats ExprField                 48 (NN.N%)             1            48
+ast-stats GenericArgs               40 (NN.N%)             1            40
+ast-stats - AngleBracketed            40 (NN.N%)             1
+ast-stats Crate                     40 (NN.N%)             1            40
 ast-stats ----------------------------------------------------------------
 ast-stats Total                  7_416                   127
-ast-stats
-hir-stats HIR STATS
+ast-stats ================================================================
+hir-stats ================================================================
+hir-stats HIR STATS: input_stats
 hir-stats Name                Accumulated Size         Count     Item Size
 hir-stats ----------------------------------------------------------------
-hir-stats ForeignItemRef            24 (NN.N%)             1            24
-hir-stats Lifetime                  28 (NN.N%)             1            28
-hir-stats Mod                       32 (NN.N%)             1            32
-hir-stats ExprField                 40 (NN.N%)             1            40
-hir-stats TraitItemRef              56 (NN.N%)             2            28
-hir-stats GenericArg                64 (NN.N%)             4            16
-hir-stats - Type                      16 (NN.N%)             1
-hir-stats - Lifetime                  48 (NN.N%)             3
-hir-stats Param                     64 (NN.N%)             2            32
-hir-stats Body                      72 (NN.N%)             3            24
-hir-stats ImplItemRef               72 (NN.N%)             2            36
-hir-stats InlineAsm                 72 (NN.N%)             1            72
-hir-stats Local                     72 (NN.N%)             1            72
-hir-stats WherePredicate            72 (NN.N%)             3            24
-hir-stats - BoundPredicate            72 (NN.N%)             3
-hir-stats Arm                       80 (NN.N%)             2            40
-hir-stats Stmt                      96 (NN.N%)             3            32
-hir-stats - Expr                      32 (NN.N%)             1
-hir-stats - Let                       32 (NN.N%)             1
-hir-stats - Semi                      32 (NN.N%)             1
-hir-stats FnDecl                   120 (NN.N%)             3            40
-hir-stats FieldDef                 128 (NN.N%)             2            64
-hir-stats GenericArgs              144 (NN.N%)             3            48
-hir-stats Variant                  144 (NN.N%)             2            72
-hir-stats Attribute                160 (NN.N%)             4            40
-hir-stats GenericBound             256 (NN.N%)             4            64
-hir-stats - Trait                    256 (NN.N%)             4
-hir-stats Block                    288 (NN.N%)             6            48
-hir-stats Pat                      360 (NN.N%)             5            72
-hir-stats - Struct                    72 (NN.N%)             1
-hir-stats - Wild                      72 (NN.N%)             1
-hir-stats - Binding                  216 (NN.N%)             3
-hir-stats GenericParam             400 (NN.N%)             5            80
-hir-stats Generics                 560 (NN.N%)            10            56
-hir-stats Ty                       720 (NN.N%)            15            48
-hir-stats - Ptr                       48 (NN.N%)             1
-hir-stats - Ref                       48 (NN.N%)             1
-hir-stats - Path                     624 (NN.N%)            13
-hir-stats Expr                     768 (NN.N%)            12            64
-hir-stats - InlineAsm                 64 (NN.N%)             1
-hir-stats - Match                     64 (NN.N%)             1
-hir-stats - Path                      64 (NN.N%)             1
-hir-stats - Struct                    64 (NN.N%)             1
-hir-stats - Lit                      128 (NN.N%)             2
-hir-stats - Block                    384 (NN.N%)             6
+hir-stats PathSegment            1_776 (NN.N%)            37            48
+hir-stats Path                   1_040 (NN.N%)            26            40
 hir-stats Item                     968 (NN.N%)            11            88
 hir-stats - Enum                      88 (NN.N%)             1
 hir-stats - ExternCrate               88 (NN.N%)             1
@@ -114,8 +73,51 @@ hir-stats - Impl                      88 (NN.N%)             1
 hir-stats - Trait                     88 (NN.N%)             1
 hir-stats - Fn                       176 (NN.N%)             2
 hir-stats - Use                      352 (NN.N%)             4
-hir-stats Path                   1_040 (NN.N%)            26            40
-hir-stats PathSegment            1_776 (NN.N%)            37            48
+hir-stats Expr                     768 (NN.N%)            12            64
+hir-stats - InlineAsm                 64 (NN.N%)             1
+hir-stats - Match                     64 (NN.N%)             1
+hir-stats - Path                      64 (NN.N%)             1
+hir-stats - Struct                    64 (NN.N%)             1
+hir-stats - Lit                      128 (NN.N%)             2
+hir-stats - Block                    384 (NN.N%)             6
+hir-stats Ty                       720 (NN.N%)            15            48
+hir-stats - Ptr                       48 (NN.N%)             1
+hir-stats - Ref                       48 (NN.N%)             1
+hir-stats - Path                     624 (NN.N%)            13
+hir-stats Generics                 560 (NN.N%)            10            56
+hir-stats GenericParam             400 (NN.N%)             5            80
+hir-stats Pat                      360 (NN.N%)             5            72
+hir-stats - Struct                    72 (NN.N%)             1
+hir-stats - Wild                      72 (NN.N%)             1
+hir-stats - Binding                  216 (NN.N%)             3
+hir-stats Block                    288 (NN.N%)             6            48
+hir-stats GenericBound             256 (NN.N%)             4            64
+hir-stats - Trait                    256 (NN.N%)             4
+hir-stats Attribute                160 (NN.N%)             4            40
+hir-stats Variant                  144 (NN.N%)             2            72
+hir-stats GenericArgs              144 (NN.N%)             3            48
+hir-stats FieldDef                 128 (NN.N%)             2            64
+hir-stats FnDecl                   120 (NN.N%)             3            40
+hir-stats Stmt                      96 (NN.N%)             3            32
+hir-stats - Expr                      32 (NN.N%)             1
+hir-stats - Let                       32 (NN.N%)             1
+hir-stats - Semi                      32 (NN.N%)             1
+hir-stats Arm                       80 (NN.N%)             2            40
+hir-stats WherePredicate            72 (NN.N%)             3            24
+hir-stats - BoundPredicate            72 (NN.N%)             3
+hir-stats Local                     72 (NN.N%)             1            72
+hir-stats InlineAsm                 72 (NN.N%)             1            72
+hir-stats ImplItemRef               72 (NN.N%)             2            36
+hir-stats Body                      72 (NN.N%)             3            24
+hir-stats Param                     64 (NN.N%)             2            32
+hir-stats GenericArg                64 (NN.N%)             4            16
+hir-stats - Type                      16 (NN.N%)             1
+hir-stats - Lifetime                  48 (NN.N%)             3
+hir-stats TraitItemRef              56 (NN.N%)             2            28
+hir-stats ExprField                 40 (NN.N%)             1            40
+hir-stats Mod                       32 (NN.N%)             1            32
+hir-stats Lifetime                  28 (NN.N%)             1            28
+hir-stats ForeignItemRef            24 (NN.N%)             1            24
 hir-stats ----------------------------------------------------------------
 hir-stats Total                  8_676                   172
-hir-stats
+hir-stats ================================================================
diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr
index f87e34622b9..00c6b55c6a2 100644
--- a/tests/ui/stats/macro-stats.stderr
+++ b/tests/ui/stats/macro-stats.stderr
@@ -2,25 +2,25 @@ macro-stats ====================================================================
 macro-stats MACRO EXPANSION STATS: macro_stats
 macro-stats Macro Name                         Uses      Lines  Avg Lines      Bytes  Avg Bytes
 macro-stats -----------------------------------------------------------------------------------
-macro-stats #[derive(Clone)]                      8         56        7.0      1_660      207.5
-macro-stats #[derive(PartialOrd)]                 1         16       16.0        654      654.0
-macro-stats #[derive(Hash)]                       2         15        7.5        547      273.5
-macro-stats #[derive(Ord)]                        1         14       14.0        489      489.0
-macro-stats q!                                    1         24       24.0        435      435.0
-macro-stats #[derive(Default)]                    2         14        7.0        367      183.5
-macro-stats #[derive(Eq)]                         1         10       10.0        312      312.0
-macro-stats #[derive(Debug)]                      1          7        7.0        261      261.0
-macro-stats #[derive(PartialEq)]                  1          8        8.0        247      247.0
-macro-stats #[derive(Copy)]                       1          1        1.0         46       46.0
-macro-stats p!                                    1          2        2.0         28       28.0
-macro-stats trait_impl_tys!                       1          1        1.0         11       11.0
-macro-stats foreign_item!                         1          0        0.0          6        6.0
-macro-stats impl_const!                           1          0        0.0          4        4.0
-macro-stats trait_tys!                            1          1        1.0          3        3.0
-macro-stats u32!                                  1          0        0.0         -3       -3.0
-macro-stats none!                                 1          0        0.0         -3       -3.0
-macro-stats n99!                                  2          0        0.0         -8       -4.0
+macro-stats #[derive(Clone)]                      8         64        8.0      1_788      223.5
+macro-stats #[derive(PartialOrd)]                 1         17       17.0        675      675.0
+macro-stats #[derive(Hash)]                       2         17        8.5        577      288.5
+macro-stats q!                                    1         26       26.0        519      519.0
+macro-stats #[derive(Ord)]                        1         15       15.0        503      503.0
+macro-stats #[derive(Default)]                    2         16        8.0        403      201.5
+macro-stats #[derive(Eq)]                         1         11       11.0        325      325.0
+macro-stats #[derive(Debug)]                      1          8        8.0        277      277.0
+macro-stats #[derive(PartialEq)]                  1          9        9.0        267      267.0
+macro-stats #[derive(Copy)]                       1          2        2.0         61       61.0
+macro-stats p!                                    1          3        3.0         32       32.0
+macro-stats trait_impl_tys!                       1          2        2.0         28       28.0
+macro-stats foreign_item!                         1          1        1.0         21       21.0
 macro-stats this_is_a_really_really_long_macro_name!
-macro-stats                                       1          0        0.0        -30      -30.0
-macro-stats #[test]                               1         -6       -6.0       -158     -158.0
+macro-stats                                       1          1        1.0         18       18.0
+macro-stats impl_const!                           1          1        1.0         17       17.0
+macro-stats trait_tys!                            1          2        2.0         15       15.0
+macro-stats n99!                                  2          2        1.0          4        2.0
+macro-stats none!                                 1          1        1.0          4        4.0
+macro-stats u32!                                  1          1        1.0          3        3.0
+macro-stats #[test]                               1          1        1.0          0        0.0
 macro-stats ===================================================================================
diff --git a/tests/ui/suggestions/bound-suggestions.stderr b/tests/ui/suggestions/bound-suggestions.stderr
index f23e086afe4..ec1d23fac45 100644
--- a/tests/ui/suggestions/bound-suggestions.stderr
+++ b/tests/ui/suggestions/bound-suggestions.stderr
@@ -2,7 +2,9 @@ error[E0277]: `impl Sized` doesn't implement `Debug`
   --> $DIR/bound-suggestions.rs:9:22
    |
 LL |     println!("{:?}", t);
-   |                      ^ `impl Sized` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               ----   ^ `impl Sized` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               |
+   |               required by this formatting parameter
    |
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider restricting opaque type `impl Sized` with trait `Debug`
@@ -14,7 +16,9 @@ error[E0277]: `T` doesn't implement `Debug`
   --> $DIR/bound-suggestions.rs:15:22
    |
 LL |     println!("{:?}", t);
-   |                      ^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               ----   ^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               |
+   |               required by this formatting parameter
    |
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider restricting type parameter `T` with trait `Debug`
@@ -26,7 +30,9 @@ error[E0277]: `T` doesn't implement `Debug`
   --> $DIR/bound-suggestions.rs:21:22
    |
 LL |     println!("{:?}", t);
-   |                      ^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               ----   ^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               |
+   |               required by this formatting parameter
    |
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider further restricting type parameter `T` with trait `Debug`
@@ -38,7 +44,9 @@ error[E0277]: `Y` doesn't implement `Debug`
   --> $DIR/bound-suggestions.rs:27:30
    |
 LL |     println!("{:?} {:?}", x, y);
-   |                              ^ `Y` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |                    ----      ^ `Y` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |                    |
+   |                    required by this formatting parameter
    |
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider further restricting type parameter `Y` with trait `Debug`
@@ -50,7 +58,9 @@ error[E0277]: `X` doesn't implement `Debug`
   --> $DIR/bound-suggestions.rs:33:22
    |
 LL |     println!("{:?}", x);
-   |                      ^ `X` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               ----   ^ `X` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               |
+   |               required by this formatting parameter
    |
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider further restricting type parameter `X` with trait `Debug`
@@ -62,7 +72,9 @@ error[E0277]: `X` doesn't implement `Debug`
   --> $DIR/bound-suggestions.rs:39:22
    |
 LL |     println!("{:?}", x);
-   |                      ^ `X` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               ----   ^ `X` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               |
+   |               required by this formatting parameter
    |
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider further restricting type parameter `X` with trait `Debug`
diff --git a/tests/ui/suggestions/derive-macro-missing-bounds.stderr b/tests/ui/suggestions/derive-macro-missing-bounds.stderr
index 68c8204d1e1..b28f39ced54 100644
--- a/tests/ui/suggestions/derive-macro-missing-bounds.stderr
+++ b/tests/ui/suggestions/derive-macro-missing-bounds.stderr
@@ -4,9 +4,8 @@ error[E0277]: `a::Inner<T>` doesn't implement `Debug`
 LL |     #[derive(Debug)]
    |              ----- in this derive macro expansion
 LL |     struct Outer<T>(Inner<T>);
-   |                     ^^^^^^^^ `a::Inner<T>` cannot be formatted using `{:?}`
+   |                     ^^^^^^^^ the trait `Debug` is not implemented for `a::Inner<T>`
    |
-   = help: the trait `Debug` is not implemented for `a::Inner<T>`
    = note: add `#[derive(Debug)]` to `a::Inner<T>` or manually `impl Debug for a::Inner<T>`
 help: consider annotating `a::Inner<T>` with `#[derive(Debug)]`
    |
diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs
index 10b4781eb04..97a0e005f86 100644
--- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs
+++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs
@@ -4,19 +4,16 @@ trait A: Sized {
     fn f(a: A) -> A;
     //~^ ERROR expected a type, found a trait
     //~| ERROR expected a type, found a trait
-    //~| ERROR associated item referring to unboxed trait object for its own trait
 }
 trait B {
     fn f(b: B) -> B;
     //~^ ERROR expected a type, found a trait
     //~| ERROR expected a type, found a trait
-    //~| ERROR associated item referring to unboxed trait object for its own trait
 }
 trait C {
     fn f(&self, c: C) -> C;
     //~^ ERROR expected a type, found a trait
     //~| ERROR expected a type, found a trait
-    //~| ERROR associated item referring to unboxed trait object for its own trait
 }
 
 fn main() {}
diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr
index e189012d15c..c4dab4691f4 100644
--- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr
+++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr
@@ -26,22 +26,8 @@ help: `A` is dyn-incompatible, use `impl A` to return an opaque type, as long as
 LL |     fn f(a: A) -> impl A;
    |                   ++++
 
-error: associated item referring to unboxed trait object for its own trait
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13
-   |
-LL | trait A: Sized {
-   |       - in this trait
-LL |     fn f(a: A) -> A;
-   |             ^     ^
-   |
-help: you might have meant to use `Self` to refer to the implementing type
-   |
-LL -     fn f(a: A) -> A;
-LL +     fn f(a: Self) -> Self;
-   |
-
 error[E0782]: expected a type, found a trait
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13
+  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:9:13
    |
 LL |     fn f(b: B) -> B;
    |             ^
@@ -58,7 +44,7 @@ LL |     fn f(b: impl B) -> B;
    |             ++++
 
 error[E0782]: expected a type, found a trait
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:19
+  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:9:19
    |
 LL |     fn f(b: B) -> B;
    |                   ^
@@ -68,22 +54,8 @@ help: `B` is dyn-incompatible, use `impl B` to return an opaque type, as long as
 LL |     fn f(b: B) -> impl B;
    |                   ++++
 
-error: associated item referring to unboxed trait object for its own trait
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13
-   |
-LL | trait B {
-   |       - in this trait
-LL |     fn f(b: B) -> B;
-   |             ^     ^
-   |
-help: you might have meant to use `Self` to refer to the implementing type
-   |
-LL -     fn f(b: B) -> B;
-LL +     fn f(b: Self) -> Self;
-   |
-
 error[E0782]: expected a type, found a trait
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20
+  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:14:20
    |
 LL |     fn f(&self, c: C) -> C;
    |                    ^
@@ -100,7 +72,7 @@ LL |     fn f(&self, c: impl C) -> C;
    |                    ++++
 
 error[E0782]: expected a type, found a trait
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:26
+  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:14:26
    |
 LL |     fn f(&self, c: C) -> C;
    |                          ^
@@ -110,20 +82,6 @@ help: `C` is dyn-incompatible, use `impl C` to return an opaque type, as long as
 LL |     fn f(&self, c: C) -> impl C;
    |                          ++++
 
-error: associated item referring to unboxed trait object for its own trait
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20
-   |
-LL | trait C {
-   |       - in this trait
-LL |     fn f(&self, c: C) -> C;
-   |                    ^     ^
-   |
-help: you might have meant to use `Self` to refer to the implementing type
-   |
-LL -     fn f(&self, c: C) -> C;
-LL +     fn f(&self, c: Self) -> Self;
-   |
-
-error: aborting due to 9 previous errors
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0782`.
diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.rs b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.rs
index 747926c400a..a798b1bd578 100644
--- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.rs
+++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.rs
@@ -2,13 +2,11 @@
 #![allow(bare_trait_objects)]
 trait A: Sized {
     fn f(a: dyn A) -> dyn A;
-    //~^ ERROR associated item referring to unboxed trait object for its own trait
-    //~| ERROR the trait `A` is not dyn compatible
+    //~^ ERROR the trait `A` is not dyn compatible
 }
 trait B {
     fn f(a: dyn B) -> dyn B;
-    //~^ ERROR associated item referring to unboxed trait object for its own trait
-    //~| ERROR the trait `B` is not dyn compatible
+    //~^ ERROR the trait `B` is not dyn compatible
 }
 trait C {
     fn f(&self, a: dyn C) -> dyn C;
diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr
index 2e3919db1b7..4ccf65b68bf 100644
--- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr
+++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr
@@ -1,17 +1,3 @@
-error: associated item referring to unboxed trait object for its own trait
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:4:13
-   |
-LL | trait A: Sized {
-   |       - in this trait
-LL |     fn f(a: dyn A) -> dyn A;
-   |             ^^^^^     ^^^^^
-   |
-help: you might have meant to use `Self` to refer to the implementing type
-   |
-LL -     fn f(a: dyn A) -> dyn A;
-LL +     fn f(a: Self) -> Self;
-   |
-
 error[E0038]: the trait `A` is not dyn compatible
   --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:4:13
    |
@@ -26,30 +12,21 @@ LL | trait A: Sized {
    |       -  ^^^^^ ...because it requires `Self: Sized`
    |       |
    |       this trait is not dyn compatible...
-
-error: associated item referring to unboxed trait object for its own trait
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:9:13
-   |
-LL | trait B {
-   |       - in this trait
-LL |     fn f(a: dyn B) -> dyn B;
-   |             ^^^^^     ^^^^^
-   |
 help: you might have meant to use `Self` to refer to the implementing type
    |
-LL -     fn f(a: dyn B) -> dyn B;
-LL +     fn f(a: Self) -> Self;
+LL -     fn f(a: dyn A) -> dyn A;
+LL +     fn f(a: Self) -> dyn A;
    |
 
 error[E0038]: the trait `B` is not dyn compatible
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:9:13
+  --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:8:13
    |
 LL |     fn f(a: dyn B) -> dyn B;
    |             ^^^^^ `B` is not dyn compatible
    |
 note: for a trait to be dyn compatible it needs to allow building a vtable
       for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
-  --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:9:8
+  --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:8:8
    |
 LL | trait B {
    |       - this trait is not dyn compatible...
@@ -63,7 +40,12 @@ help: alternatively, consider constraining `f` so it does not apply to trait obj
    |
 LL |     fn f(a: dyn B) -> dyn B where Self: Sized;
    |                             +++++++++++++++++
+help: you might have meant to use `Self` to refer to the implementing type
+   |
+LL -     fn f(a: dyn B) -> dyn B;
+LL +     fn f(a: Self) -> dyn B;
+   |
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0038`.
diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.rs b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.rs
index 63fe5ebaea4..d8e9d381dbd 100644
--- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.rs
+++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.rs
@@ -1,12 +1,10 @@
 trait A: Sized {
     fn f(a: dyn A) -> dyn A;
-    //~^ ERROR associated item referring to unboxed trait object for its own trait
-    //~| ERROR the trait `A` is not dyn compatible
+    //~^ ERROR the trait `A` is not dyn compatible
 }
 trait B {
     fn f(a: dyn B) -> dyn B;
-    //~^ ERROR associated item referring to unboxed trait object for its own trait
-    //~| ERROR the trait `B` is not dyn compatible
+    //~^ ERROR the trait `B` is not dyn compatible
 }
 trait C {
     fn f(&self, a: dyn C) -> dyn C;
diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr
index e8384afed7a..bda1d01e23f 100644
--- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr
+++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr
@@ -1,17 +1,3 @@
-error: associated item referring to unboxed trait object for its own trait
-  --> $DIR/dyn-incompatible-trait-should-use-self.rs:2:13
-   |
-LL | trait A: Sized {
-   |       - in this trait
-LL |     fn f(a: dyn A) -> dyn A;
-   |             ^^^^^     ^^^^^
-   |
-help: you might have meant to use `Self` to refer to the implementing type
-   |
-LL -     fn f(a: dyn A) -> dyn A;
-LL +     fn f(a: Self) -> Self;
-   |
-
 error[E0038]: the trait `A` is not dyn compatible
   --> $DIR/dyn-incompatible-trait-should-use-self.rs:2:13
    |
@@ -26,30 +12,21 @@ LL | trait A: Sized {
    |       -  ^^^^^ ...because it requires `Self: Sized`
    |       |
    |       this trait is not dyn compatible...
-
-error: associated item referring to unboxed trait object for its own trait
-  --> $DIR/dyn-incompatible-trait-should-use-self.rs:7:13
-   |
-LL | trait B {
-   |       - in this trait
-LL |     fn f(a: dyn B) -> dyn B;
-   |             ^^^^^     ^^^^^
-   |
 help: you might have meant to use `Self` to refer to the implementing type
    |
-LL -     fn f(a: dyn B) -> dyn B;
-LL +     fn f(a: Self) -> Self;
+LL -     fn f(a: dyn A) -> dyn A;
+LL +     fn f(a: Self) -> dyn A;
    |
 
 error[E0038]: the trait `B` is not dyn compatible
-  --> $DIR/dyn-incompatible-trait-should-use-self.rs:7:13
+  --> $DIR/dyn-incompatible-trait-should-use-self.rs:6:13
    |
 LL |     fn f(a: dyn B) -> dyn B;
    |             ^^^^^ `B` is not dyn compatible
    |
 note: for a trait to be dyn compatible it needs to allow building a vtable
       for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
-  --> $DIR/dyn-incompatible-trait-should-use-self.rs:7:8
+  --> $DIR/dyn-incompatible-trait-should-use-self.rs:6:8
    |
 LL | trait B {
    |       - this trait is not dyn compatible...
@@ -63,7 +40,12 @@ help: alternatively, consider constraining `f` so it does not apply to trait obj
    |
 LL |     fn f(a: dyn B) -> dyn B where Self: Sized;
    |                             +++++++++++++++++
+help: you might have meant to use `Self` to refer to the implementing type
+   |
+LL -     fn f(a: dyn B) -> dyn B;
+LL +     fn f(a: Self) -> dyn B;
+   |
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0038`.
diff --git a/tests/ui/suggestions/impl-trait-with-missing-bounds.stderr b/tests/ui/suggestions/impl-trait-with-missing-bounds.stderr
index d0ce7c9ed4e..b3f1865dd30 100644
--- a/tests/ui/suggestions/impl-trait-with-missing-bounds.stderr
+++ b/tests/ui/suggestions/impl-trait-with-missing-bounds.stderr
@@ -2,11 +2,10 @@ error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
   --> $DIR/impl-trait-with-missing-bounds.rs:6:13
    |
 LL |         qux(constraint);
-   |         --- ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |         --- ^^^^^^^^^^ the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
    |         |
    |         required by a bound introduced by this call
    |
-   = help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
 note: required by a bound in `qux`
   --> $DIR/impl-trait-with-missing-bounds.rs:50:16
    |
@@ -22,11 +21,10 @@ error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
   --> $DIR/impl-trait-with-missing-bounds.rs:14:13
    |
 LL |         qux(constraint);
-   |         --- ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |         --- ^^^^^^^^^^ the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
    |         |
    |         required by a bound introduced by this call
    |
-   = help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
 note: required by a bound in `qux`
   --> $DIR/impl-trait-with-missing-bounds.rs:50:16
    |
@@ -42,11 +40,10 @@ error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
   --> $DIR/impl-trait-with-missing-bounds.rs:22:13
    |
 LL |         qux(constraint);
-   |         --- ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |         --- ^^^^^^^^^^ the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
    |         |
    |         required by a bound introduced by this call
    |
-   = help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
 note: required by a bound in `qux`
   --> $DIR/impl-trait-with-missing-bounds.rs:50:16
    |
@@ -62,11 +59,10 @@ error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
   --> $DIR/impl-trait-with-missing-bounds.rs:30:13
    |
 LL |         qux(constraint);
-   |         --- ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |         --- ^^^^^^^^^^ the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
    |         |
    |         required by a bound introduced by this call
    |
-   = help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
 note: required by a bound in `qux`
   --> $DIR/impl-trait-with-missing-bounds.rs:50:16
    |
@@ -82,11 +78,10 @@ error[E0277]: `<impl Iterator + std::fmt::Debug as Iterator>::Item` doesn't impl
   --> $DIR/impl-trait-with-missing-bounds.rs:37:13
    |
 LL |         qux(constraint);
-   |         --- ^^^^^^^^^^ `<impl Iterator + std::fmt::Debug as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |         --- ^^^^^^^^^^ the trait `Debug` is not implemented for `<impl Iterator + std::fmt::Debug as Iterator>::Item`
    |         |
    |         required by a bound introduced by this call
    |
-   = help: the trait `Debug` is not implemented for `<impl Iterator + std::fmt::Debug as Iterator>::Item`
 note: required by a bound in `qux`
   --> $DIR/impl-trait-with-missing-bounds.rs:50:16
    |
@@ -102,11 +97,10 @@ error[E0277]: `<impl Iterator as Iterator>::Item` doesn't implement `Debug`
   --> $DIR/impl-trait-with-missing-bounds.rs:45:13
    |
 LL |         qux(constraint);
-   |         --- ^^^^^^^^^^ `<impl Iterator as Iterator>::Item` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |         --- ^^^^^^^^^^ the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
    |         |
    |         required by a bound introduced by this call
    |
-   = help: the trait `Debug` is not implemented for `<impl Iterator as Iterator>::Item`
 note: required by a bound in `qux`
   --> $DIR/impl-trait-with-missing-bounds.rs:50:16
    |
diff --git a/tests/ui/suggestions/issue-105645.rs b/tests/ui/suggestions/issue-105645.rs
index 681ce1c6e37..f3ca8ccbb3c 100644
--- a/tests/ui/suggestions/issue-105645.rs
+++ b/tests/ui/suggestions/issue-105645.rs
@@ -2,7 +2,7 @@ fn main() {
     let mut buf = [0u8; 50];
     let mut bref = buf.as_slice();
     foo(&mut bref);
-    //~^ ERROR 4:9: 4:18: the trait bound `&[u8]: std::io::Write` is not satisfied [E0277]
+    //~^ ERROR the trait bound `&[u8]: std::io::Write` is not satisfied [E0277]
 }
 
 fn foo(_: &mut impl std::io::Write) {}
diff --git a/tests/ui/suggestions/issue-116434-2015.rs b/tests/ui/suggestions/issue-116434-2015.rs
index bad9d02321c..e0438cdef25 100644
--- a/tests/ui/suggestions/issue-116434-2015.rs
+++ b/tests/ui/suggestions/issue-116434-2015.rs
@@ -11,6 +11,7 @@ trait Foo {
     //~| HELP if this is a dyn-compatible trait, use `dyn`
     //~| ERROR the trait `Clone` is not dyn compatible [E0038]
     //~| HELP there is an associated type with the same name
+    //~| HELP use `Self` to refer to the implementing type
 }
 
 trait DbHandle: Sized {}
@@ -26,6 +27,7 @@ trait DbInterface {
     //~| HELP if this is a dyn-compatible trait, use `dyn`
     //~| ERROR the trait `DbHandle` is not dyn compatible [E0038]
     //~| HELP there is an associated type with the same name
+    //~| HELP use `Self` to refer to the implementing type
 }
 
 fn main() {}
diff --git a/tests/ui/suggestions/issue-116434-2015.stderr b/tests/ui/suggestions/issue-116434-2015.stderr
index a0a99cc560d..cad5812da66 100644
--- a/tests/ui/suggestions/issue-116434-2015.stderr
+++ b/tests/ui/suggestions/issue-116434-2015.stderr
@@ -35,13 +35,18 @@ LL |     fn foo() -> Clone;
    = note: the trait is not dyn compatible because it requires `Self: Sized`
    = note: for a trait to be dyn compatible it needs to allow building a vtable
            for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
+help: you might have meant to use `Self` to refer to the implementing type
+   |
+LL -     fn foo() -> Clone;
+LL +     fn foo() -> Self;
+   |
 help: there is an associated type with the same name
    |
 LL |     fn foo() -> Self::Clone;
    |                 ++++++
 
 warning: trait objects without an explicit `dyn` are deprecated
-  --> $DIR/issue-116434-2015.rs:20:20
+  --> $DIR/issue-116434-2015.rs:21:20
    |
 LL |     fn handle() -> DbHandle;
    |                    ^^^^^^^^
@@ -54,7 +59,7 @@ LL |     fn handle() -> dyn DbHandle;
    |                    +++
 
 warning: trait objects without an explicit `dyn` are deprecated
-  --> $DIR/issue-116434-2015.rs:20:20
+  --> $DIR/issue-116434-2015.rs:21:20
    |
 LL |     fn handle() -> DbHandle;
    |                    ^^^^^^^^
@@ -68,19 +73,24 @@ LL |     fn handle() -> dyn DbHandle;
    |                    +++
 
 error[E0038]: the trait `DbHandle` is not dyn compatible
-  --> $DIR/issue-116434-2015.rs:20:20
+  --> $DIR/issue-116434-2015.rs:21:20
    |
 LL |     fn handle() -> DbHandle;
    |                    ^^^^^^^^ `DbHandle` is not dyn compatible
    |
 note: for a trait to be dyn compatible it needs to allow building a vtable
       for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
-  --> $DIR/issue-116434-2015.rs:16:17
+  --> $DIR/issue-116434-2015.rs:17:17
    |
 LL | trait DbHandle: Sized {}
    |       --------  ^^^^^ ...because it requires `Self: Sized`
    |       |
    |       this trait is not dyn compatible...
+help: you might have meant to use `Self` to refer to the implementing type
+   |
+LL -     fn handle() -> DbHandle;
+LL +     fn handle() -> Self;
+   |
 help: there is an associated type with the same name
    |
 LL |     fn handle() -> Self::DbHandle;
diff --git a/tests/ui/suggestions/issue-81098.stderr b/tests/ui/suggestions/issue-81098.stderr
index 4dc47a20282..36948469a31 100644
--- a/tests/ui/suggestions/issue-81098.stderr
+++ b/tests/ui/suggestions/issue-81098.stderr
@@ -2,23 +2,17 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
   --> $DIR/issue-81098.rs:3:13
    |
 LL | fn wat() -> impl core::fmt::Display {
-   |             ^^^^^^^^^^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter
-   |
-   = help: the trait `std::fmt::Display` is not implemented for `()`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::fmt::Display` is not implemented for `()`
 
 error[E0277]: `()` doesn't implement `std::fmt::Display`
   --> $DIR/issue-81098.rs:9:12
    |
 LL | fn ok() -> impl core::fmt::Display {
-   |            ^^^^^^^^^^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter
+   |            ^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::fmt::Display` is not implemented for `()`
 LL |     1;
    |     -- help: remove this semicolon
    |     |
    |     this expression has type `{integer}`, which implements `std::fmt::Display`
-   |
-   = help: the trait `std::fmt::Display` is not implemented for `()`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/suggestions/issue-97760.stderr b/tests/ui/suggestions/issue-97760.stderr
index c3cf7e13987..1084ea7c9e0 100644
--- a/tests/ui/suggestions/issue-97760.stderr
+++ b/tests/ui/suggestions/issue-97760.stderr
@@ -1,8 +1,11 @@
 error[E0277]: `<impl IntoIterator as IntoIterator>::Item` doesn't implement `std::fmt::Display`
-  --> $DIR/issue-97760.rs:4:19
+  --> $DIR/issue-97760.rs:4:20
    |
 LL |         println!("{x}");
-   |                   ^^^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
+   |                   -^-
+   |                   ||
+   |                   |`<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
+   |                   required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `<impl IntoIterator as IntoIterator>::Item`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr
index 3f8b6f93e1f..e3375b67c86 100644
--- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr
+++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.stderr
@@ -21,7 +21,7 @@ error[E0277]: `K` doesn't implement `Debug`
   --> $DIR/missing-bound-in-derive-copy-impl-3.rs:12:14
    |
 LL |     pub loc: Vector2<K>,
-   |              ^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |              ^^^^^^^^^^ the trait `Debug` is not implemented for `K`
    |
 note: required by a bound in `Vector2`
   --> $DIR/missing-bound-in-derive-copy-impl-3.rs:5:23
@@ -40,7 +40,7 @@ LL | #[derive(Debug, Copy, Clone)]
    |                       ----- in this derive macro expansion
 LL | pub struct AABB<K: Copy>{
 LL |     pub loc: Vector2<K>,
-   |     ^^^^^^^^^^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `K`
    |
 help: consider further restricting type parameter `K` with trait `Debug`
    |
@@ -54,7 +54,7 @@ LL | #[derive(Debug, Copy, Clone)]
    |                       ----- in this derive macro expansion
 ...
 LL |     pub size: Vector2<K>
-   |     ^^^^^^^^^^^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `K`
    |
 help: consider further restricting type parameter `K` with trait `Debug`
    |
diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr
index 3766e3e2c7b..645d6ebb396 100644
--- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr
+++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr
@@ -21,7 +21,7 @@ error[E0277]: `K` doesn't implement `Debug`
   --> $DIR/missing-bound-in-derive-copy-impl.rs:11:14
    |
 LL |     pub loc: Vector2<K>,
-   |              ^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |              ^^^^^^^^^^ the trait `Debug` is not implemented for `K`
    |
 note: required by a bound in `Vector2`
   --> $DIR/missing-bound-in-derive-copy-impl.rs:4:23
@@ -78,7 +78,7 @@ LL | #[derive(Debug, Copy, Clone)]
    |                       ----- in this derive macro expansion
 LL | pub struct AABB<K> {
 LL |     pub loc: Vector2<K>,
-   |     ^^^^^^^^^^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `K`
    |
 help: consider restricting type parameter `K` with trait `Debug`
    |
@@ -111,7 +111,7 @@ LL | #[derive(Debug, Copy, Clone)]
    |                       ----- in this derive macro expansion
 ...
 LL |     pub size: Vector2<K>,
-   |     ^^^^^^^^^^^^^^^^^^^^ `K` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `K`
    |
 help: consider restricting type parameter `K` with trait `Debug`
    |
diff --git a/tests/ui/suggestions/path-display.stderr b/tests/ui/suggestions/path-display.stderr
index 46d0b35825b..0c7271b3c1c 100644
--- a/tests/ui/suggestions/path-display.stderr
+++ b/tests/ui/suggestions/path-display.stderr
@@ -2,18 +2,23 @@ error[E0277]: `Path` doesn't implement `std::fmt::Display`
   --> $DIR/path-display.rs:5:20
    |
 LL |     println!("{}", path);
-   |                    ^^^^ `Path` cannot be formatted with the default formatter; call `.display()` on it
+   |               --   ^^^^ `Path` cannot be formatted with the default formatter; call `.display()` on it
+   |               |
+   |               required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `Path`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
    = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data
+   = note: required for `&Path` to implement `std::fmt::Display`
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `PathBuf` doesn't implement `std::fmt::Display`
   --> $DIR/path-display.rs:9:20
    |
 LL |     println!("{}", path);
-   |                    ^^^^ `PathBuf` cannot be formatted with the default formatter; call `.display()` on it
+   |               --   ^^^^ `PathBuf` cannot be formatted with the default formatter; call `.display()` on it
+   |               |
+   |               required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `PathBuf`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/suggestions/suggest-full-enum-variant-for-local-module.rs b/tests/ui/suggestions/suggest-full-enum-variant-for-local-module.rs
index 1dfc0786668..807fba0ab7e 100644
--- a/tests/ui/suggestions/suggest-full-enum-variant-for-local-module.rs
+++ b/tests/ui/suggestions/suggest-full-enum-variant-for-local-module.rs
@@ -6,5 +6,5 @@ mod option {
 }
 
 fn main() {
-    let _: option::O<()> = (); //~ ERROR 9:28: 9:30: mismatched types [E0308]
+    let _: option::O<()> = (); //~ ERROR mismatched types [E0308]
 }
diff --git a/tests/ui/syntax-extension-minor.rs b/tests/ui/syntax-extension-minor.rs
deleted file mode 100644
index 826990a89a5..00000000000
--- a/tests/ui/syntax-extension-minor.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ run-pass
-
-#![feature(concat_idents)]
-#![expect(deprecated)] // concat_idents is deprecated
-
-pub fn main() {
-    struct Foo;
-    let _: concat_idents!(F, oo) = Foo; // Test that `concat_idents!` can be used in type positions
-
-    let asdf_fdsa = "<.<".to_string();
-    // concat_idents should have call-site hygiene.
-    assert!(concat_idents!(asd, f_f, dsa) == "<.<".to_string());
-
-    assert_eq!(stringify!(use_mention_distinction), "use_mention_distinction");
-}
diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr
index 2dca0c22033..0b2d71f97d0 100644
--- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr
+++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.riscv.stderr
@@ -7,13 +7,5 @@ warning: unstable feature specified for `-Ctarget-feature`: `d`
    |
    = note: this feature is not stably supported; its behavior can change in the future
 
-warning: unstable feature specified for `-Ctarget-feature`: `f`
-   |
-   = note: this feature is not stably supported; its behavior can change in the future
-
-warning: unstable feature specified for `-Ctarget-feature`: `zicsr`
-   |
-   = note: this feature is not stably supported; its behavior can change in the future
-
-warning: 4 warnings emitted
+warning: 2 warnings emitted
 
diff --git a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs
index 302cceccf69..1006b078bab 100644
--- a/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs
+++ b/tests/ui/target-feature/abi-incompatible-target-feature-flag-enable.rs
@@ -24,5 +24,3 @@ pub trait Freeze {}
 
 //~? WARN must be disabled to ensure that the ABI of the current target can be implemented correctly
 //~? WARN unstable feature specified for `-Ctarget-feature`
-//[riscv]~? WARN unstable feature specified for `-Ctarget-feature`
-//[riscv]~? WARN unstable feature specified for `-Ctarget-feature`
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
index 2a0f5f01aef..79e89823c51 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
@@ -1,4 +1,4 @@
-warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
+warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline-external-thunk` compiler flag instead
    |
    = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
index f7b6cb16447..f5ff15df632 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
@@ -1,4 +1,4 @@
-warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline` compiler flag instead
    |
    = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
index 4f2cd1d1a52..158cca08a76 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
@@ -1,4 +1,4 @@
-warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `-Zretpoline` compiler flag instead
    |
    = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.rs b/tests/ui/target-feature/retpoline-target-feature-flag.rs
index de3c44c3ed0..05c85860385 100644
--- a/tests/ui/target-feature/retpoline-target-feature-flag.rs
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.rs
@@ -16,6 +16,6 @@
 #![no_core]
 extern crate minicore;
 
-//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
-//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
-//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`
+//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`
+//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`
diff --git a/tests/ui/thir-print/thir-tree-loop-match.rs b/tests/ui/thir-print/thir-tree-loop-match.rs
new file mode 100644
index 00000000000..8c5f2244d54
--- /dev/null
+++ b/tests/ui/thir-print/thir-tree-loop-match.rs
@@ -0,0 +1,22 @@
+//@ check-pass
+//@ compile-flags: -Zunpretty=thir-tree
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+fn boolean(mut state: bool) -> bool {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                true => {
+                    #[const_continue]
+                    break 'blk false;
+                }
+                false => return state,
+            }
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout
new file mode 100644
index 00000000000..828b93da6be
--- /dev/null
+++ b/tests/ui/thir-print/thir-tree-loop-match.stdout
@@ -0,0 +1,301 @@
+DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean):
+params: [
+    Param {
+        ty: bool
+        ty_span: Some($DIR/thir-tree-loop-match.rs:7:23: 7:27 (#0))
+        self_kind: None
+        hir_id: Some(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).1))
+        param: Some( 
+            Pat: {
+                ty: bool
+                span: $DIR/thir-tree-loop-match.rs:7:12: 7:21 (#0)
+                kind: PatKind {
+                    Binding {
+                        name: "state"
+                        mode: BindingMode(No, Mut)
+                        var: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2))
+                        ty: bool
+                        is_primary: true
+                        subpattern: None
+                    }
+                }
+            }
+        )
+    }
+]
+body:
+    Expr {
+        ty: bool
+        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+        span: $DIR/thir-tree-loop-match.rs:7:37: 20:2 (#0)
+        kind: 
+            Scope {
+                region_scope: Node(28)
+                lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).28))
+                value:
+                    Expr {
+                        ty: bool
+                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+                        span: $DIR/thir-tree-loop-match.rs:7:37: 20:2 (#0)
+                        kind: 
+                            Block {
+                                targeted_by_break: false
+                                span: $DIR/thir-tree-loop-match.rs:7:37: 20:2 (#0)
+                                region_scope: Node(3)
+                                safety_mode: Safe
+                                stmts: []
+                                expr:
+                                    Expr {
+                                        ty: bool
+                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+                                        span: $DIR/thir-tree-loop-match.rs:9:5: 19:6 (#0)
+                                        kind: 
+                                            Scope {
+                                                region_scope: Node(4)
+                                                lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).4))
+                                                value:
+                                                    Expr {
+                                                        ty: bool
+                                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+                                                        span: $DIR/thir-tree-loop-match.rs:9:5: 19:6 (#0)
+                                                        kind: 
+                                                            NeverToAny {
+                                                                source:
+                                                                    Expr {
+                                                                        ty: !
+                                                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+                                                                        span: $DIR/thir-tree-loop-match.rs:9:5: 19:6 (#0)
+                                                                        kind: 
+                                                                            LoopMatch {
+                                                                                state:
+                                                                                    Expr {
+                                                                                        ty: bool
+                                                                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(5)), backwards_incompatible: None }
+                                                                                        span: $DIR/thir-tree-loop-match.rs:10:9: 10:14 (#0)
+                                                                                        kind: 
+                                                                                            Scope {
+                                                                                                region_scope: Node(7)
+                                                                                                lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).7))
+                                                                                                value:
+                                                                                                    Expr {
+                                                                                                        ty: bool
+                                                                                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(5)), backwards_incompatible: None }
+                                                                                                        span: $DIR/thir-tree-loop-match.rs:10:9: 10:14 (#0)
+                                                                                                        kind: 
+                                                                                                            VarRef {
+                                                                                                                id: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2))
+                                                                                                            }
+                                                                                                    }
+                                                                                            }
+                                                                                    }
+                                                                                region_scope: Node(10)
+                                                                                match_span: $DIR/thir-tree-loop-match.rs:11:13: 17:14 (#0)
+                                                                                arms: [
+                                                                                    Arm {
+                                                                                        pattern: 
+                                                                                            Pat: {
+                                                                                                ty: bool
+                                                                                                span: $DIR/thir-tree-loop-match.rs:12:17: 12:21 (#0)
+                                                                                                kind: PatKind {
+                                                                                                    Constant {
+                                                                                                        value: Ty(bool, true)
+                                                                                                    }
+                                                                                                }
+                                                                                            }
+                                                                                        guard: None
+                                                                                        body: 
+                                                                                            Expr {
+                                                                                                ty: bool
+                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None }
+                                                                                                span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0)
+                                                                                                kind: 
+                                                                                                    Scope {
+                                                                                                        region_scope: Node(17)
+                                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).17))
+                                                                                                        value:
+                                                                                                            Expr {
+                                                                                                                ty: bool
+                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None }
+                                                                                                                span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0)
+                                                                                                                kind: 
+                                                                                                                    NeverToAny {
+                                                                                                                        source:
+                                                                                                                            Expr {
+                                                                                                                                ty: !
+                                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None }
+                                                                                                                                span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0)
+                                                                                                                                kind: 
+                                                                                                                                    Block {
+                                                                                                                                        targeted_by_break: false
+                                                                                                                                        span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0)
+                                                                                                                                        region_scope: Node(18)
+                                                                                                                                        safety_mode: Safe
+                                                                                                                                        stmts: [
+                                                                                                                                            Stmt {
+                                                                                                                                                kind: Expr {
+                                                                                                                                                    scope: Node(21)
+                                                                                                                                                    expr:
+                                                                                                                                                        Expr {
+                                                                                                                                                            ty: !
+                                                                                                                                                            temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None }
+                                                                                                                                                            span: $DIR/thir-tree-loop-match.rs:14:21: 14:37 (#0)
+                                                                                                                                                            kind: 
+                                                                                                                                                                Scope {
+                                                                                                                                                                    region_scope: Node(19)
+                                                                                                                                                                    lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).19))
+                                                                                                                                                                    value:
+                                                                                                                                                                        Expr {
+                                                                                                                                                                            ty: !
+                                                                                                                                                                            temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None }
+                                                                                                                                                                            span: $DIR/thir-tree-loop-match.rs:14:21: 14:37 (#0)
+                                                                                                                                                                            kind: 
+                                                                                                                                                                                ConstContinue (
+                                                                                                                                                                                    label: Node(10)
+                                                                                                                                                                                    value:
+                                                                                                                                                                                        Expr {
+                                                                                                                                                                                            ty: bool
+                                                                                                                                                                                            temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None }
+                                                                                                                                                                                            span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0)
+                                                                                                                                                                                            kind: 
+                                                                                                                                                                                                Scope {
+                                                                                                                                                                                                    region_scope: Node(20)
+                                                                                                                                                                                                    lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).20))
+                                                                                                                                                                                                    value:
+                                                                                                                                                                                                        Expr {
+                                                                                                                                                                                                            ty: bool
+                                                                                                                                                                                                            temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None }
+                                                                                                                                                                                                            span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0)
+                                                                                                                                                                                                            kind: 
+                                                                                                                                                                                                                Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0) }, neg: false)
+
+                                                                                                                                                                                                        }
+                                                                                                                                                                                                }
+                                                                                                                                                                                        }
+                                                                                                                                                                                )
+                                                                                                                                                                        }
+                                                                                                                                                                }
+                                                                                                                                                        }
+                                                                                                                                                }
+                                                                                                                                            }
+                                                                                                                                        ]
+                                                                                                                                        expr: []
+                                                                                                                                    }
+                                                                                                                            }
+                                                                                                                    }
+                                                                                                            }
+                                                                                                    }
+                                                                                            }
+                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).16))
+                                                                                        scope: Node(16)
+                                                                                        span: $DIR/thir-tree-loop-match.rs:12:17: 15:18 (#0)
+                                                                                    }
+                                                                                    Arm {
+                                                                                        pattern: 
+                                                                                            Pat: {
+                                                                                                ty: bool
+                                                                                                span: $DIR/thir-tree-loop-match.rs:16:17: 16:22 (#0)
+                                                                                                kind: PatKind {
+                                                                                                    Constant {
+                                                                                                        value: Ty(bool, false)
+                                                                                                    }
+                                                                                                }
+                                                                                            }
+                                                                                        guard: None
+                                                                                        body: 
+                                                                                            Expr {
+                                                                                                ty: bool
+                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0)
+                                                                                                kind: 
+                                                                                                    Scope {
+                                                                                                        region_scope: Node(25)
+                                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).25))
+                                                                                                        value:
+                                                                                                            Expr {
+                                                                                                                ty: bool
+                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                                span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0)
+                                                                                                                kind: 
+                                                                                                                    NeverToAny {
+                                                                                                                        source:
+                                                                                                                            Expr {
+                                                                                                                                ty: !
+                                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                                                span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0)
+                                                                                                                                kind: 
+                                                                                                                                    Return {
+                                                                                                                                        value:
+                                                                                                                                            Expr {
+                                                                                                                                                ty: bool
+                                                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                                                                span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0)
+                                                                                                                                                kind: 
+                                                                                                                                                    Scope {
+                                                                                                                                                        region_scope: Node(26)
+                                                                                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).26))
+                                                                                                                                                        value:
+                                                                                                                                                            Expr {
+                                                                                                                                                                ty: bool
+                                                                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                                                                                span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0)
+                                                                                                                                                                kind: 
+                                                                                                                                                                    VarRef {
+                                                                                                                                                                        id: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2))
+                                                                                                                                                                    }
+                                                                                                                                                            }
+                                                                                                                                                    }
+                                                                                                                                            }
+                                                                                                                                    }
+                                                                                                                            }
+                                                                                                                    }
+                                                                                                            }
+                                                                                                    }
+                                                                                            }
+                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).24))
+                                                                                        scope: Node(24)
+                                                                                        span: $DIR/thir-tree-loop-match.rs:16:17: 16:38 (#0)
+                                                                                    }
+                                                                                ]
+                                                                            }
+                                                                    }
+                                                            }
+                                                    }
+                                            }
+                                    }
+                            }
+                    }
+            }
+    }
+
+
+DefId(0:4 ~ thir_tree_loop_match[3c53]::main):
+params: [
+]
+body:
+    Expr {
+        ty: ()
+        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None }
+        span: $DIR/thir-tree-loop-match.rs:22:11: 22:13 (#0)
+        kind: 
+            Scope {
+                region_scope: Node(2)
+                lint_level: Explicit(HirId(DefId(0:4 ~ thir_tree_loop_match[3c53]::main).2))
+                value:
+                    Expr {
+                        ty: ()
+                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None }
+                        span: $DIR/thir-tree-loop-match.rs:22:11: 22:13 (#0)
+                        kind: 
+                            Block {
+                                targeted_by_break: false
+                                span: $DIR/thir-tree-loop-match.rs:22:11: 22:13 (#0)
+                                region_scope: Node(1)
+                                safety_mode: Safe
+                                stmts: []
+                                expr: []
+                            }
+                    }
+            }
+    }
+
+
diff --git a/tests/ui/traits/const-traits/call-const-trait-method-pass.rs b/tests/ui/traits/const-traits/call-const-trait-method-pass.rs
index 3004647ede0..d66a11490c5 100644
--- a/tests/ui/traits/const-traits/call-const-trait-method-pass.rs
+++ b/tests/ui/traits/const-traits/call-const-trait-method-pass.rs
@@ -1,6 +1,5 @@
-//@ known-bug: #110395
-
 #![feature(const_trait_impl, const_ops)]
+//@ check-pass
 
 struct Int(i32);
 
diff --git a/tests/ui/traits/const-traits/call-const-trait-method-pass.stderr b/tests/ui/traits/const-traits/call-const-trait-method-pass.stderr
deleted file mode 100644
index 7746f103ac3..00000000000
--- a/tests/ui/traits/const-traits/call-const-trait-method-pass.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: const `impl` for trait `PartialEq` which is not marked with `#[const_trait]`
-  --> $DIR/call-const-trait-method-pass.rs:15:12
-   |
-LL | impl const PartialEq for Int {
-   |            ^^^^^^^^^ this trait is not `const`
-   |
-   = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const`
-   = note: adding a non-const method body in the future would be a breaking change
-
-error[E0015]: cannot call non-const method `<Int as PartialEq>::eq` in constant functions
-  --> $DIR/call-const-trait-method-pass.rs:20:15
-   |
-LL |         !self.eq(other)
-   |               ^^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/const-traits/call-generic-in-impl.rs b/tests/ui/traits/const-traits/call-generic-in-impl.rs
index 6149dc3d126..b63458b39e9 100644
--- a/tests/ui/traits/const-traits/call-generic-in-impl.rs
+++ b/tests/ui/traits/const-traits/call-generic-in-impl.rs
@@ -1,5 +1,4 @@
-//@ known-bug: #110395
-// FIXME(const_trait_impl) check-pass
+//@ check-pass
 #![feature(const_trait_impl)]
 
 #[const_trait]
diff --git a/tests/ui/traits/const-traits/call-generic-in-impl.stderr b/tests/ui/traits/const-traits/call-generic-in-impl.stderr
deleted file mode 100644
index a45dfd95b4a..00000000000
--- a/tests/ui/traits/const-traits/call-generic-in-impl.stderr
+++ /dev/null
@@ -1,30 +0,0 @@
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-in-impl.rs:10:9
-   |
-LL | impl<T: ~const PartialEq> const MyPartialEq for T {
-   |         ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-in-impl.rs:10:9
-   |
-LL | impl<T: ~const PartialEq> const MyPartialEq for T {
-   |         ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error[E0015]: cannot call non-const method `<T as PartialEq>::eq` in constant functions
-  --> $DIR/call-generic-in-impl.rs:12:9
-   |
-LL |         PartialEq::eq(self, other)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/const-traits/call-generic-method-chain.rs b/tests/ui/traits/const-traits/call-generic-method-chain.rs
index 74beab71208..b515c0e711d 100644
--- a/tests/ui/traits/const-traits/call-generic-method-chain.rs
+++ b/tests/ui/traits/const-traits/call-generic-method-chain.rs
@@ -1,8 +1,7 @@
 //! Basic test for calling methods on generic type parameters in `const fn`.
 
-//@ known-bug: #110395
 //@ compile-flags: -Znext-solver
-// FIXME(const_trait_impl) check-pass
+//@ check-pass
 
 #![feature(const_trait_impl)]
 
diff --git a/tests/ui/traits/const-traits/call-generic-method-chain.stderr b/tests/ui/traits/const-traits/call-generic-method-chain.stderr
deleted file mode 100644
index 40b4f14733f..00000000000
--- a/tests/ui/traits/const-traits/call-generic-method-chain.stderr
+++ /dev/null
@@ -1,66 +0,0 @@
-error: const `impl` for trait `PartialEq` which is not marked with `#[const_trait]`
-  --> $DIR/call-generic-method-chain.rs:11:12
-   |
-LL | impl const PartialEq for S {
-   |            ^^^^^^^^^ this trait is not `const`
-   |
-   = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const`
-   = note: adding a non-const method body in the future would be a breaking change
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-chain.rs:20:25
-   |
-LL | const fn equals_self<T: ~const PartialEq>(t: &T) -> bool {
-   |                         ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-chain.rs:20:25
-   |
-LL | const fn equals_self<T: ~const PartialEq>(t: &T) -> bool {
-   |                         ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-chain.rs:24:33
-   |
-LL | const fn equals_self_wrapper<T: ~const PartialEq>(t: &T) -> bool {
-   |                                 ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-chain.rs:24:33
-   |
-LL | const fn equals_self_wrapper<T: ~const PartialEq>(t: &T) -> bool {
-   |                                 ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/call-generic-method-chain.rs:21:5
-   |
-LL |     *t == *t
-   |     ^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const method `<S as PartialEq>::eq` in constant functions
-  --> $DIR/call-generic-method-chain.rs:16:15
-   |
-LL |         !self.eq(other)
-   |               ^^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error: aborting due to 7 previous errors
-
-For more information about this error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/const-traits/call-generic-method-dup-bound.rs b/tests/ui/traits/const-traits/call-generic-method-dup-bound.rs
index ec615d8484c..fdc439845ac 100644
--- a/tests/ui/traits/const-traits/call-generic-method-dup-bound.rs
+++ b/tests/ui/traits/const-traits/call-generic-method-dup-bound.rs
@@ -1,6 +1,5 @@
 //@ compile-flags: -Znext-solver
-//@ known-bug: #110395
-// FIXME(const_trait_impl) check-pass
+//@ check-pass
 
 #![feature(const_trait_impl)]
 
diff --git a/tests/ui/traits/const-traits/call-generic-method-dup-bound.stderr b/tests/ui/traits/const-traits/call-generic-method-dup-bound.stderr
deleted file mode 100644
index c74f5cf786c..00000000000
--- a/tests/ui/traits/const-traits/call-generic-method-dup-bound.stderr
+++ /dev/null
@@ -1,74 +0,0 @@
-error: const `impl` for trait `PartialEq` which is not marked with `#[const_trait]`
-  --> $DIR/call-generic-method-dup-bound.rs:9:12
-   |
-LL | impl const PartialEq for S {
-   |            ^^^^^^^^^ this trait is not `const`
-   |
-   = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const`
-   = note: adding a non-const method body in the future would be a breaking change
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-dup-bound.rs:20:37
-   |
-LL | const fn equals_self<T: PartialEq + ~const PartialEq>(t: &T) -> bool {
-   |                                     ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-dup-bound.rs:20:37
-   |
-LL | const fn equals_self<T: PartialEq + ~const PartialEq>(t: &T) -> bool {
-   |                                     ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-dup-bound.rs:27:30
-   |
-LL | const fn equals_self2<T: A + ~const PartialEq>(t: &T) -> bool {
-   |                              ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-dup-bound.rs:27:30
-   |
-LL | const fn equals_self2<T: A + ~const PartialEq>(t: &T) -> bool {
-   |                              ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/call-generic-method-dup-bound.rs:21:5
-   |
-LL |     *t == *t
-   |     ^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const method `<S as PartialEq>::eq` in constant functions
-  --> $DIR/call-generic-method-dup-bound.rs:14:15
-   |
-LL |         !self.eq(other)
-   |               ^^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/call-generic-method-dup-bound.rs:28:5
-   |
-LL |     *t == *t
-   |     ^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error: aborting due to 8 previous errors
-
-For more information about this error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/const-traits/call-generic-method-fail.rs b/tests/ui/traits/const-traits/call-generic-method-fail.rs
index 66881334a29..3ab5cc58ce3 100644
--- a/tests/ui/traits/const-traits/call-generic-method-fail.rs
+++ b/tests/ui/traits/const-traits/call-generic-method-fail.rs
@@ -3,7 +3,7 @@
 
 pub const fn equals_self<T: PartialEq>(t: &T) -> bool {
     *t == *t
-    //~^ ERROR cannot call non-const operator in constant functions
+    //~^ ERROR the trait bound `T: ~const PartialEq` is not satisfied
 }
 
 fn main() {}
diff --git a/tests/ui/traits/const-traits/call-generic-method-fail.stderr b/tests/ui/traits/const-traits/call-generic-method-fail.stderr
index 6bacb986fef..9facf80ee87 100644
--- a/tests/ui/traits/const-traits/call-generic-method-fail.stderr
+++ b/tests/ui/traits/const-traits/call-generic-method-fail.stderr
@@ -1,11 +1,9 @@
-error[E0015]: cannot call non-const operator in constant functions
+error[E0277]: the trait bound `T: ~const PartialEq` is not satisfied
   --> $DIR/call-generic-method-fail.rs:5:5
    |
 LL |     *t == *t
    |     ^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/const-traits/call-generic-method-pass.rs b/tests/ui/traits/const-traits/call-generic-method-pass.rs
index af793b8da03..bc671c897f0 100644
--- a/tests/ui/traits/const-traits/call-generic-method-pass.rs
+++ b/tests/ui/traits/const-traits/call-generic-method-pass.rs
@@ -1,8 +1,7 @@
 //! Basic test for calling methods on generic type parameters in `const fn`.
 
 //@ compile-flags: -Znext-solver
-//@ known-bug: #110395
-// FIXME(const_trait_impl) check-pass
+//@ check-pass
 
 #![feature(const_trait_impl)]
 
diff --git a/tests/ui/traits/const-traits/call-generic-method-pass.stderr b/tests/ui/traits/const-traits/call-generic-method-pass.stderr
deleted file mode 100644
index 1a33ff5ab45..00000000000
--- a/tests/ui/traits/const-traits/call-generic-method-pass.stderr
+++ /dev/null
@@ -1,47 +0,0 @@
-error: const `impl` for trait `PartialEq` which is not marked with `#[const_trait]`
-  --> $DIR/call-generic-method-pass.rs:11:12
-   |
-LL | impl const PartialEq for S {
-   |            ^^^^^^^^^ this trait is not `const`
-   |
-   = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const`
-   = note: adding a non-const method body in the future would be a breaking change
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-pass.rs:20:25
-   |
-LL | const fn equals_self<T: ~const PartialEq>(t: &T) -> bool {
-   |                         ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/call-generic-method-pass.rs:20:25
-   |
-LL | const fn equals_self<T: ~const PartialEq>(t: &T) -> bool {
-   |                         ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/call-generic-method-pass.rs:21:5
-   |
-LL |     *t == *t
-   |     ^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const method `<S as PartialEq>::eq` in constant functions
-  --> $DIR/call-generic-method-pass.rs:16:15
-   |
-LL |         !self.eq(other)
-   |               ^^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error: aborting due to 5 previous errors
-
-For more information about this error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr
index 6783cec3960..ee922f9689e 100644
--- a/tests/ui/traits/const-traits/const-impl-trait.stderr
+++ b/tests/ui/traits/const-traits/const-impl-trait.stderr
@@ -1,197 +1,17 @@
-error[E0635]: unknown feature `const_cmp`
-  --> $DIR/const-impl-trait.rs:7:30
+error[E0277]: the trait bound `(): const PartialEq` is not satisfied
+  --> $DIR/const-impl-trait.rs:34:17
    |
-LL | #![feature(const_trait_impl, const_cmp, const_destruct)]
-   |                              ^^^^^^^^^
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:11:23
-   |
-LL | const fn cmp(a: &impl ~const PartialEq) -> bool {
-   |                       ^^^^^^ can't be applied to `PartialEq`
+LL |     assert!(cmp(&()));
+   |             --- ^^^
+   |             |
+   |             required by a bound introduced by this call
    |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
+note: required by a bound in `cmp`
   --> $DIR/const-impl-trait.rs:11:23
    |
 LL | const fn cmp(a: &impl ~const PartialEq) -> bool {
-   |                       ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:16:13
-   |
-LL |     x: impl ~const PartialEq + ~const Destruct,
-   |             ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:16:13
-   |
-LL |     x: impl ~const PartialEq + ~const Destruct,
-   |             ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:17:11
-   |
-LL | ) -> impl ~const PartialEq + ~const Destruct {
-   |           ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:17:11
-   |
-LL | ) -> impl ~const PartialEq + ~const Destruct {
-   |           ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:23:22
-   |
-LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
-   |                      ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:27:22
-   |
-LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy {
-   |                      ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:17:11
-   |
-LL | ) -> impl ~const PartialEq + ~const Destruct {
-   |           ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:27:22
-   |
-LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy {
-   |                      ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:27:22
-   |
-LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy {
-   |                      ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:23:22
-   |
-LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
-   |                      ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:23:22
-   |
-LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
-   |                      ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:23:22
-   |
-LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
-   |                      ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:23:22
-   |
-LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
-   |                      ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: `~const` can only be applied to `#[const_trait]` traits
-  --> $DIR/const-impl-trait.rs:23:22
-   |
-LL |     fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
-   |                      ^^^^^^ can't be applied to `PartialEq`
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error[E0015]: cannot call non-const operator in constants
-  --> $DIR/const-impl-trait.rs:35:13
-   |
-LL |     assert!(wrap(123) == wrap(123));
-   |             ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const operator in constants
-  --> $DIR/const-impl-trait.rs:36:13
-   |
-LL |     assert!(wrap(123) != wrap(456));
-   |             ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const operator in constants
-  --> $DIR/const-impl-trait.rs:38:13
-   |
-LL |     assert!(x == x);
-   |             ^^^^^^
-   |
-   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/const-impl-trait.rs:12:5
-   |
-LL |     a == a
-   |     ^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+   |                       ^^^^^^^^^^^^^^^^ required by this bound in `cmp`
 
-error: aborting due to 21 previous errors
+error: aborting due to 1 previous error
 
-Some errors have detailed explanations: E0015, E0635.
-For more information about an error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs
index b563b78f78a..f90ff91aff4 100644
--- a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs
+++ b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.rs
@@ -6,7 +6,6 @@
 // Regression test for issue #125877.
 
 //@ compile-flags: -Znext-solver
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
 
 #![feature(const_trait_impl, effects)]
 //~^ ERROR feature has been removed
diff --git a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr
index a04f98e68a6..566961b27f3 100644
--- a/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr
+++ b/tests/ui/traits/const-traits/const-trait-impl-parameter-mismatch.stderr
@@ -1,14 +1,14 @@
 error[E0557]: feature has been removed
-  --> $DIR/const-trait-impl-parameter-mismatch.rs:11:30
+  --> $DIR/const-trait-impl-parameter-mismatch.rs:10:30
    |
 LL | #![feature(const_trait_impl, effects)]
    |                              ^^^^^^^ feature has been removed
    |
-   = note: removed in 1.84.0 (you are using $RUSTC_VERSION); see <https://github.com/rust-lang/rust/pull/132479> for more information
+   = note: removed in 1.84.0; see <https://github.com/rust-lang/rust/pull/132479> for more information
    = note: removed, redundant with `#![feature(const_trait_impl)]`
 
 error[E0049]: associated function `compute` has 0 type parameters but its trait declaration has 1 type parameter
-  --> $DIR/const-trait-impl-parameter-mismatch.rs:20:16
+  --> $DIR/const-trait-impl-parameter-mismatch.rs:19:16
    |
 LL |     fn compute<T: ~const Aux>() -> u32;
    |                - expected 1 type parameter
diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr
index 8297911a3f3..87ac78908bb 100644
--- a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr
+++ b/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr
@@ -1,9 +1,3 @@
-error[E0635]: unknown feature `const_cmp`
-  --> $DIR/derive-const-use.rs:3:30
-   |
-LL | #![feature(const_trait_impl, const_cmp, const_default_impls, derive_const)]
-   |                              ^^^^^^^^^
-
 error[E0635]: unknown feature `const_default_impls`
   --> $DIR/derive-const-use.rs:3:41
    |
@@ -28,23 +22,13 @@ LL | #[derive_const(Default, PartialEq)]
    = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const`
    = note: adding a non-const method body in the future would be a breaking change
 
-error: const `impl` for trait `PartialEq` which is not marked with `#[const_trait]`
-  --> $DIR/derive-const-use.rs:11:12
-   |
-LL | impl const PartialEq for A {
-   |            ^^^^^^^^^ this trait is not `const`
-   |
-   = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const`
-   = note: adding a non-const method body in the future would be a breaking change
-
-error: const `impl` for trait `PartialEq` which is not marked with `#[const_trait]`
-  --> $DIR/derive-const-use.rs:15:25
+error[E0277]: the trait bound `(): ~const PartialEq` is not satisfied
+  --> $DIR/derive-const-use.rs:16:14
    |
 LL | #[derive_const(Default, PartialEq)]
-   |                         ^^^^^^^^^ this trait is not `const`
-   |
-   = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const`
-   = note: adding a non-const method body in the future would be a breaking change
+   |                         --------- in this derive macro expansion
+LL | pub struct S((), A);
+   |              ^^
 
 error[E0015]: cannot call non-const associated function `<S as Default>::default` in constants
   --> $DIR/derive-const-use.rs:18:35
@@ -54,14 +38,6 @@ LL | const _: () = assert!(S((), A) == S::default());
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
 
-error[E0015]: cannot call non-const operator in constants
-  --> $DIR/derive-const-use.rs:18:23
-   |
-LL | const _: () = assert!(S((), A) == S::default());
-   |                       ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-
 error[E0015]: cannot call non-const associated function `<() as Default>::default` in constant functions
   --> $DIR/derive-const-use.rs:16:14
    |
@@ -82,27 +58,7 @@ LL | pub struct S((), A);
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
 
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/derive-const-use.rs:16:14
-   |
-LL | #[derive_const(Default, PartialEq)]
-   |                         --------- in this derive macro expansion
-LL | pub struct S((), A);
-   |              ^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/derive-const-use.rs:16:18
-   |
-LL | #[derive_const(Default, PartialEq)]
-   |                         --------- in this derive macro expansion
-LL | pub struct S((), A);
-   |                  ^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error: aborting due to 12 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0015, E0635.
+Some errors have detailed explanations: E0015, E0277, E0635.
 For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-with-params.rs b/tests/ui/traits/const-traits/const_derives/derive-const-with-params.rs
index 18b224af278..b39f97b5938 100644
--- a/tests/ui/traits/const-traits/const_derives/derive-const-with-params.rs
+++ b/tests/ui/traits/const-traits/const_derives/derive-const-with-params.rs
@@ -1,5 +1,4 @@
-//@ known-bug: #110395
-// FIXME(const_trait_impl) check-pass
+//@ check-pass
 
 #![feature(derive_const)]
 #![feature(const_trait_impl)]
diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-with-params.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-with-params.stderr
deleted file mode 100644
index d1dbf62d566..00000000000
--- a/tests/ui/traits/const-traits/const_derives/derive-const-with-params.stderr
+++ /dev/null
@@ -1,35 +0,0 @@
-error: const `impl` for trait `PartialEq` which is not marked with `#[const_trait]`
-  --> $DIR/derive-const-with-params.rs:7:16
-   |
-LL | #[derive_const(PartialEq)]
-   |                ^^^^^^^^^ this trait is not `const`
-   |
-   = note: marking a trait with `#[const_trait]` ensures all default method bodies are `const`
-   = note: adding a non-const method body in the future would be a breaking change
-
-error: `~const` can only be applied to `#[const_trait]` traits
-   |
-note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]`
-  --> $SRC_DIR/core/src/cmp.rs:LL:COL
-
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/derive-const-with-params.rs:8:23
-   |
-LL | #[derive_const(PartialEq)]
-   |                --------- in this derive macro expansion
-LL | pub struct Reverse<T>(T);
-   |                       ^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/derive-const-with-params.rs:11:5
-   |
-LL |     a == b
-   |     ^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs
index 4cb013b9323..4312d295b11 100644
--- a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs
+++ b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs
@@ -11,7 +11,6 @@ const fn test() -> impl ~const Fn() {
             [first, remainder @ ..] => {
                 assert_eq!(first, &b'f');
                 //~^ ERROR cannot call non-const function
-                //~| ERROR cannot call non-const operator
             }
             [] => panic!(),
         }
diff --git a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr
index 8d9371bf9f6..f06bacdeb4e 100644
--- a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr
+++ b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr
@@ -37,15 +37,6 @@ note: `Fn` can't be used with `~const` because it isn't annotated with `#[const_
   --> $SRC_DIR/core/src/ops/function.rs:LL:COL
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error[E0015]: cannot call non-const operator in constant functions
-  --> $DIR/ice-112822-expected-type-for-param.rs:12:17
-   |
-LL |                 assert_eq!(first, &b'f');
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
-
 error[E0015]: cannot call non-const function `core::panicking::assert_failed::<&u8, &u8>` in constant functions
   --> $DIR/ice-112822-expected-type-for-param.rs:12:17
    |
@@ -55,7 +46,7 @@ LL |                 assert_eq!(first, &b'f');
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
 
 Some errors have detailed explanations: E0015, E0658.
 For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/traits/on_unimplemented_long_types.stderr b/tests/ui/traits/on_unimplemented_long_types.stderr
index 1628466e081..f32d99a42b1 100644
--- a/tests/ui/traits/on_unimplemented_long_types.stderr
+++ b/tests/ui/traits/on_unimplemented_long_types.stderr
@@ -2,7 +2,7 @@ error[E0277]: `Option<Option<Option<...>>>` doesn't implement `std::fmt::Display
   --> $DIR/on_unimplemented_long_types.rs:3:17
    |
 LL |   pub fn foo() -> impl std::fmt::Display {
-   |                   ^^^^^^^^^^^^^^^^^^^^^^ `Option<Option<Option<...>>>` cannot be formatted with the default formatter
+   |                   ^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
 LL |
 LL | /     Some(Some(Some(Some(Some(Some(Some(Some(Some(S...
 LL | |         Some(Some(Some(Some(Some(Some(Some(Some(So...
@@ -14,7 +14,6 @@ LL | |     )))))))))))
    | |_______________- return type was inferred to be `Option<Option<Option<...>>>` here
    |
    = help: the trait `std::fmt::Display` is not implemented for `Option<Option<Option<...>>>`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
    = note: the full name for the type has been written to '$TEST_BUILD_DIR/on_unimplemented_long_types.long-type-$LONG_TYPE_HASH.txt'
    = note: consider using `--verbose` to print the full type name to the console
 
diff --git a/tests/ui/traits/suggest-remove-deref-issue-140166.stderr b/tests/ui/traits/suggest-remove-deref-issue-140166.stderr
index 90f24d86d53..7c61f957fdc 100644
--- a/tests/ui/traits/suggest-remove-deref-issue-140166.stderr
+++ b/tests/ui/traits/suggest-remove-deref-issue-140166.stderr
@@ -4,7 +4,7 @@ error[E0277]: the trait bound `&Chars: Trait` is not satisfied
 LL |     format_args!("{:?}", FlatMap(&Chars));
    |                   ----   ^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `&Chars`
    |                   |
-   |                   required by a bound introduced by this call
+   |                   required by this formatting parameter
    |
    = help: the trait `Trait` is implemented for `Chars`
 note: required for `FlatMap<&Chars>` to implement `Debug`
@@ -14,8 +14,6 @@ LL | impl<T: Trait> std::fmt::Debug for FlatMap<T> {
    |         -----  ^^^^^^^^^^^^^^^     ^^^^^^^^^^
    |         |
    |         unsatisfied trait bound introduced here
-note: required by a bound in `core::fmt::rt::Argument::<'_>::new_debug`
-  --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/traits/trait-object-destructure.rs b/tests/ui/traits/trait-object-destructure.rs
new file mode 100644
index 00000000000..6c091677c8c
--- /dev/null
+++ b/tests/ui/traits/trait-object-destructure.rs
@@ -0,0 +1,29 @@
+//! Regression test for destructuring trait references (`&dyn T`/`Box<dyn T>`).
+//! Checks cases where number of `&`/`Box` patterns (n) matches/doesn't match references (m).
+//!
+//! Issue: https://github.com/rust-lang/rust/issues/15031
+
+#![feature(box_patterns)]
+
+trait T {
+    fn foo(&self) {}
+}
+
+impl T for isize {}
+
+fn main() {
+    // Valid cases: n < m (can dereference)
+    let &x = &(&1isize as &dyn T);
+    let &x = &&(&1isize as &dyn T);
+    let &&x = &&(&1isize as &dyn T);
+
+    // Error cases: n == m (cannot dereference trait object)
+    let &x = &1isize as &dyn T; //~ ERROR type `&dyn T` cannot be dereferenced
+    let &&x = &(&1isize as &dyn T); //~ ERROR type `&dyn T` cannot be dereferenced
+    let box x = Box::new(1isize) as Box<dyn T>; //~ ERROR type `Box<dyn T>` cannot be dereferenced
+
+    // Error cases: n > m (type mismatch)
+    let &&x = &1isize as &dyn T; //~ ERROR mismatched types
+    let &&&x = &(&1isize as &dyn T); //~ ERROR mismatched types
+    let box box x = Box::new(1isize) as Box<dyn T>; //~ ERROR mismatched types
+}
diff --git a/tests/ui/destructure-trait-ref.stderr b/tests/ui/traits/trait-object-destructure.stderr
index 0b5ea551a57..c7c832dc40a 100644
--- a/tests/ui/destructure-trait-ref.stderr
+++ b/tests/ui/traits/trait-object-destructure.stderr
@@ -1,23 +1,23 @@
 error[E0033]: type `&dyn T` cannot be dereferenced
-  --> $DIR/destructure-trait-ref.rs:28:9
+  --> $DIR/trait-object-destructure.rs:21:9
    |
 LL |     let &x = &1isize as &dyn T;
    |         ^^ type `&dyn T` cannot be dereferenced
 
 error[E0033]: type `&dyn T` cannot be dereferenced
-  --> $DIR/destructure-trait-ref.rs:29:10
+  --> $DIR/trait-object-destructure.rs:22:10
    |
 LL |     let &&x = &(&1isize as &dyn T);
    |          ^^ type `&dyn T` cannot be dereferenced
 
 error[E0033]: type `Box<dyn T>` cannot be dereferenced
-  --> $DIR/destructure-trait-ref.rs:30:9
+  --> $DIR/trait-object-destructure.rs:23:9
    |
 LL |     let box x = Box::new(1isize) as Box<dyn T>;
    |         ^^^^^ type `Box<dyn T>` cannot be dereferenced
 
 error[E0308]: mismatched types
-  --> $DIR/destructure-trait-ref.rs:34:10
+  --> $DIR/trait-object-destructure.rs:26:10
    |
 LL |     let &&x = &1isize as &dyn T;
    |          ^^   ----------------- this expression has type `&dyn T`
@@ -33,7 +33,7 @@ LL +     let &x = &1isize as &dyn T;
    |
 
 error[E0308]: mismatched types
-  --> $DIR/destructure-trait-ref.rs:38:11
+  --> $DIR/trait-object-destructure.rs:27:11
    |
 LL |     let &&&x = &(&1isize as &dyn T);
    |           ^^   -------------------- this expression has type `&&dyn T`
@@ -49,7 +49,7 @@ LL +     let &&x = &(&1isize as &dyn T);
    |
 
 error[E0308]: mismatched types
-  --> $DIR/destructure-trait-ref.rs:42:13
+  --> $DIR/trait-object-destructure.rs:28:13
    |
 LL |     let box box x = Box::new(1isize) as Box<dyn T>;
    |             ^^^^^   ------------------------------ this expression has type `Box<dyn T>`
diff --git a/tests/ui/type-alias-impl-trait/bounds-are-checked3.stderr b/tests/ui/type-alias-impl-trait/bounds-are-checked3.stderr
index c0f6d678097..01d24cabf48 100644
--- a/tests/ui/type-alias-impl-trait/bounds-are-checked3.stderr
+++ b/tests/ui/type-alias-impl-trait/bounds-are-checked3.stderr
@@ -2,9 +2,8 @@ error[E0277]: `T` doesn't implement `std::fmt::Display`
   --> $DIR/bounds-are-checked3.rs:9:35
    |
 LL | type Foo<T: Debug> = (impl Debug, Struct<T>);
-   |                                   ^^^^^^^^^ `T` cannot be formatted with the default formatter
+   |                                   ^^^^^^^^^ the trait `std::fmt::Display` is not implemented for `T`
    |
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `Struct`
   --> $DIR/bounds-are-checked3.rs:5:18
    |
diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr
index ef0e73f1481..193f0c92c9d 100644
--- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr
+++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use2.stderr
@@ -2,7 +2,7 @@ error[E0277]: `T` doesn't implement `Debug`
   --> $DIR/generic_duplicate_param_use2.rs:12:5
    |
 LL |     t
-   |     ^ `T` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^ the trait `Debug` is not implemented for `T`
    |
 note: required by a bound in an opaque type
   --> $DIR/generic_duplicate_param_use2.rs:8:23
diff --git a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr
index 0932c72ff93..f0d1e93b0b7 100644
--- a/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr
+++ b/tests/ui/type-alias-impl-trait/generic_duplicate_param_use4.stderr
@@ -2,7 +2,7 @@ error[E0277]: `U` doesn't implement `Debug`
   --> $DIR/generic_duplicate_param_use4.rs:12:5
    |
 LL |     u
-   |     ^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |     ^ the trait `Debug` is not implemented for `U`
    |
 note: required by a bound in an opaque type
   --> $DIR/generic_duplicate_param_use4.rs:8:23
diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr
index 429c3b9175a..1e3c454a5bc 100644
--- a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr
+++ b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr
@@ -2,7 +2,7 @@ error[E0277]: `U` doesn't implement `Debug`
   --> $DIR/generic_underconstrained2.rs:9:33
    |
 LL | fn underconstrained<U>(_: U) -> Underconstrained<U> {
-   |                                 ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |                                 ^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `U`
    |
 note: required by a bound on the type alias `Underconstrained`
   --> $DIR/generic_underconstrained2.rs:5:26
@@ -18,7 +18,7 @@ error[E0277]: `V` doesn't implement `Debug`
   --> $DIR/generic_underconstrained2.rs:19:43
    |
 LL | fn underconstrained2<U, V>(_: U, _: V) -> Underconstrained2<V> {
-   |                                           ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |                                           ^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `V`
    |
 note: required by a bound on the type alias `Underconstrained2`
   --> $DIR/generic_underconstrained2.rs:15:27
@@ -34,7 +34,7 @@ error[E0277]: `U` doesn't implement `Debug`
   --> $DIR/generic_underconstrained2.rs:9:33
    |
 LL | fn underconstrained<U>(_: U) -> Underconstrained<U> {
-   |                                 ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |                                 ^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `U`
    |
 note: required by a bound on the type alias `Underconstrained`
   --> $DIR/generic_underconstrained2.rs:5:26
@@ -51,7 +51,7 @@ error[E0277]: `V` doesn't implement `Debug`
   --> $DIR/generic_underconstrained2.rs:19:43
    |
 LL | fn underconstrained2<U, V>(_: U, _: V) -> Underconstrained2<V> {
-   |                                           ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |                                           ^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `V`
    |
 note: required by a bound on the type alias `Underconstrained2`
   --> $DIR/generic_underconstrained2.rs:15:27
diff --git a/tests/ui/type-alias-impl-trait/nested.stderr b/tests/ui/type-alias-impl-trait/nested.stderr
index 59911f65a23..f72830b864d 100644
--- a/tests/ui/type-alias-impl-trait/nested.stderr
+++ b/tests/ui/type-alias-impl-trait/nested.stderr
@@ -15,7 +15,9 @@ error[E0277]: `Bar` doesn't implement `Debug`
   --> $DIR/nested.rs:17:22
    |
 LL |     println!("{:?}", bar());
-   |                      ^^^^^ `Bar` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               ----   ^^^^^ `Bar` cannot be formatted using `{:?}` because it doesn't implement `Debug`
+   |               |
+   |               required by this formatting parameter
    |
    = help: the trait `Debug` is not implemented for `Bar`
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/type/binding-assigned-block-without-tail-expression.stderr b/tests/ui/type/binding-assigned-block-without-tail-expression.stderr
index 3e96d7f317b..ff34facf389 100644
--- a/tests/ui/type/binding-assigned-block-without-tail-expression.stderr
+++ b/tests/ui/type/binding-assigned-block-without-tail-expression.stderr
@@ -5,7 +5,9 @@ LL |         42;
    |           - help: remove this semicolon
 ...
 LL |     println!("{}", x);
-   |                    ^ `()` cannot be formatted with the default formatter
+   |               --   ^ `()` cannot be formatted with the default formatter
+   |               |
+   |               required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
@@ -18,7 +20,9 @@ LL |     let y = {};
    |             -- this empty block is missing a tail expression
 ...
 LL |     println!("{}", y);
-   |                    ^ `()` cannot be formatted with the default formatter
+   |               --   ^ `()` cannot be formatted with the default formatter
+   |               |
+   |               required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
@@ -31,7 +35,9 @@ LL |         "hi";
    |             - help: remove this semicolon
 ...
 LL |     println!("{}", z);
-   |                    ^ `()` cannot be formatted with the default formatter
+   |               --   ^ `()` cannot be formatted with the default formatter
+   |               |
+   |               required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
@@ -47,7 +53,9 @@ LL | |     };
    | |_____- this block is missing a tail expression
 ...
 LL |       println!("{}", s);
-   |                      ^ `()` cannot be formatted with the default formatter
+   |                 --   ^ `()` cannot be formatted with the default formatter
+   |                 |
+   |                 required by this formatting parameter
    |
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/type/option-ref-advice.rs b/tests/ui/type/option-ref-advice.rs
index 2dcee5a2eb9..435b15d01e3 100644
--- a/tests/ui/type/option-ref-advice.rs
+++ b/tests/ui/type/option-ref-advice.rs
@@ -3,9 +3,9 @@
 fn takes_option(_arg: Option<&String>) {}
 
 fn main() {
-    takes_option(&None); //~ ERROR 6:18: 6:23: mismatched types [E0308]
+    takes_option(&None); //~ ERROR mismatched types [E0308]
 
     let x = String::from("x");
     let res = Some(x);
-    takes_option(&res); //~ ERROR 10:18: 10:22: mismatched types [E0308]
+    takes_option(&res); //~ ERROR mismatched types [E0308]
 }
diff --git a/tests/ui/type/pattern_types/derives_fail.stderr b/tests/ui/type/pattern_types/derives_fail.stderr
index 78bef726341..6b2e27494f0 100644
--- a/tests/ui/type/pattern_types/derives_fail.stderr
+++ b/tests/ui/type/pattern_types/derives_fail.stderr
@@ -26,9 +26,7 @@ LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)
    |                                      ----- in this derive macro expansion
 LL | #[repr(transparent)]
 LL | struct Nanoseconds(NanoI32);
-   |                    ^^^^^^^ `(i32) is 0..=999999999` cannot be formatted using `{:?}` because it doesn't implement `Debug`
-   |
-   = help: the trait `Debug` is not implemented for `(i32) is 0..=999999999`
+   |                    ^^^^^^^ the trait `Debug` is not implemented for `(i32) is 0..=999999999`
 
 error[E0277]: the trait bound `(i32) is 0..=999999999: Ord` is not satisfied
   --> $DIR/derives_fail.rs:11:20
diff --git a/tests/ui/typeck/auxiliary/private-dep.rs b/tests/ui/typeck/auxiliary/private-dep.rs
new file mode 100644
index 00000000000..472b40ef622
--- /dev/null
+++ b/tests/ui/typeck/auxiliary/private-dep.rs
@@ -0,0 +1,3 @@
+pub trait A {
+    fn foo() {}
+}
diff --git a/tests/ui/typeck/auxiliary/public-dep.rs b/tests/ui/typeck/auxiliary/public-dep.rs
new file mode 100644
index 00000000000..438692a1caa
--- /dev/null
+++ b/tests/ui/typeck/auxiliary/public-dep.rs
@@ -0,0 +1,11 @@
+//@ aux-crate:priv:private_dep=private-dep.rs
+//@ compile-flags: -Zunstable-options
+
+extern crate private_dep;
+use private_dep::A;
+
+pub struct B;
+
+impl A for B {
+    fn foo() {}
+}
diff --git a/tests/ui/typeck/dont-suggest-private-dependencies.rs b/tests/ui/typeck/dont-suggest-private-dependencies.rs
new file mode 100644
index 00000000000..ee5224e2d82
--- /dev/null
+++ b/tests/ui/typeck/dont-suggest-private-dependencies.rs
@@ -0,0 +1,37 @@
+// Don't suggest importing a function from a private dependency.
+// Issues: #138191, #142676
+
+// Avoid suggesting traits from std-private deps
+//@ forbid-output: compiler_builtins
+//@ forbid-output: object
+
+// Check a custom trait to withstand changes in above crates
+//@ aux-crate:public_dep=public-dep.rs
+//@ compile-flags: -Zunstable-options
+//@ forbid-output: private_dep
+
+// By default, the `read` diagnostic suggests `std::os::unix::fs::FileExt::read_at`. Add
+// something more likely to be recommended to make the diagnostic cross-platform.
+trait DecoyRead {
+    fn read1(&self) {}
+}
+impl<T> DecoyRead for Vec<T> {}
+
+struct VecReader(Vec<u8>);
+
+impl std::io::Read for VecReader {
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.0.read(buf)
+        //~^ ERROR no method named `read` found for struct `Vec<u8>`
+    }
+}
+
+extern crate public_dep;
+use public_dep::B;
+
+fn main() {
+    let _ = u8::cast_from_lossy(9);
+    //~^ ERROR no function or associated item named `cast_from_lossy` found for type `u8`
+    let _ = B::foo();
+    //~^ ERROR no function or associated item named `foo` found for struct `B`
+}
diff --git a/tests/ui/typeck/dont-suggest-private-dependencies.stderr b/tests/ui/typeck/dont-suggest-private-dependencies.stderr
new file mode 100644
index 00000000000..b7b14ee6b9b
--- /dev/null
+++ b/tests/ui/typeck/dont-suggest-private-dependencies.stderr
@@ -0,0 +1,27 @@
+error[E0599]: no method named `read` found for struct `Vec<u8>` in the current scope
+  --> $DIR/dont-suggest-private-dependencies.rs:24:16
+   |
+LL |         self.0.read(buf)
+   |                ^^^^
+   |
+help: there is a method `read1` with a similar name, but with different arguments
+  --> $DIR/dont-suggest-private-dependencies.rs:16:5
+   |
+LL |     fn read1(&self) {}
+   |     ^^^^^^^^^^^^^^^
+
+error[E0599]: no function or associated item named `cast_from_lossy` found for type `u8` in the current scope
+  --> $DIR/dont-suggest-private-dependencies.rs:33:17
+   |
+LL |     let _ = u8::cast_from_lossy(9);
+   |                 ^^^^^^^^^^^^^^^ function or associated item not found in `u8`
+
+error[E0599]: no function or associated item named `foo` found for struct `B` in the current scope
+  --> $DIR/dont-suggest-private-dependencies.rs:35:16
+   |
+LL |     let _ = B::foo();
+   |                ^^^ function or associated item not found in `B`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/typeck/inference-method-chain-diverging-fallback.rs b/tests/ui/typeck/inference-method-chain-diverging-fallback.rs
new file mode 100644
index 00000000000..8f549b7d9d6
--- /dev/null
+++ b/tests/ui/typeck/inference-method-chain-diverging-fallback.rs
@@ -0,0 +1,19 @@
+//! Test type inference in method chains with diverging fallback.
+//! Verifies that closure type in `unwrap_or_else` is properly inferred
+//! when chained with other combinators and contains a diverging path.
+
+//@ run-pass
+
+fn produce<T>() -> Result<&'static str, T> {
+    Ok("22")
+}
+
+fn main() {
+    // The closure's error type `T` must unify with `ParseIntError`,
+    // while the success type must be `usize` (from parse())
+    let x: usize = produce()
+        .and_then(|x| x.parse::<usize>()) // Explicit turbofish for clarity
+        .unwrap_or_else(|_| panic!()); // Diverging fallback
+
+    assert_eq!(x, 22);
+}
diff --git a/tests/ui/typeck/issue-100246.rs b/tests/ui/typeck/issue-100246.rs
index 8f0b34bab0c..e05bb2a1362 100644
--- a/tests/ui/typeck/issue-100246.rs
+++ b/tests/ui/typeck/issue-100246.rs
@@ -25,6 +25,6 @@ fn downcast<'a, W: ?Sized>() -> std::io::Result<&'a W> {
 struct Other;
 
 fn main() -> std::io::Result<()> {
-    let other: Other = downcast()?;//~ERROR 28:24: 28:35: `?` operator has incompatible types
+    let other: Other = downcast()?; //~ ERROR `?` operator has incompatible types
     Ok(())
 }
diff --git a/tests/ui/typeck/issue-89275.rs b/tests/ui/typeck/issue-89275.rs
index b91c0017548..6e4211de185 100644
--- a/tests/ui/typeck/issue-89275.rs
+++ b/tests/ui/typeck/issue-89275.rs
@@ -25,5 +25,5 @@ fn downcast<'a, W: ?Sized>() -> &'a W {
 struct Other;
 
 fn main() {
-    let other: &mut Other = downcast();//~ERROR 28:29: 28:39: mismatched types [E0308]
+    let other: &mut Other = downcast();//~ ERROR mismatched types [E0308]
 }
diff --git a/tests/ui/typeck/point-at-type-param-in-path-expr.stderr b/tests/ui/typeck/point-at-type-param-in-path-expr.stderr
index 14642b25c99..3701b3e3798 100644
--- a/tests/ui/typeck/point-at-type-param-in-path-expr.stderr
+++ b/tests/ui/typeck/point-at-type-param-in-path-expr.stderr
@@ -2,10 +2,8 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
   --> $DIR/point-at-type-param-in-path-expr.rs:4:19
    |
 LL |     let x = foo::<()>;
-   |                   ^^ `()` cannot be formatted with the default formatter
+   |                   ^^ the trait `std::fmt::Display` is not implemented for `()`
    |
-   = help: the trait `std::fmt::Display` is not implemented for `()`
-   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
 note: required by a bound in `foo`
   --> $DIR/point-at-type-param-in-path-expr.rs:1:11
    |
diff --git a/tests/ui/underscore-imports/issue-110164.stderr b/tests/ui/underscore-imports/issue-110164.ed2015.stderr
index d8a4b6bbb75..f34b5ab5dde 100644
--- a/tests/ui/underscore-imports/issue-110164.stderr
+++ b/tests/ui/underscore-imports/issue-110164.ed2015.stderr
@@ -1,17 +1,17 @@
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/issue-110164.rs:5:5
+  --> $DIR/issue-110164.rs:8:5
    |
 LL | use _::a;
    |     ^ expected identifier, found reserved identifier
 
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/issue-110164.rs:8:5
+  --> $DIR/issue-110164.rs:10:5
    |
 LL | use _::*;
    |     ^ expected identifier, found reserved identifier
 
 error: expected identifier, found reserved identifier `_`
-  --> $DIR/issue-110164.rs:13:9
+  --> $DIR/issue-110164.rs:14:9
    |
 LL |     use _::a;
    |         ^ expected identifier, found reserved identifier
@@ -23,41 +23,17 @@ LL |     use _::*;
    |         ^ expected identifier, found reserved identifier
 
 error[E0432]: unresolved import `self::*`
-  --> $DIR/issue-110164.rs:1:5
+  --> $DIR/issue-110164.rs:4:5
    |
 LL | use self::*;
    |     ^^^^^^^ cannot glob-import a module into itself
 
 error[E0432]: unresolved import `crate::*`
-  --> $DIR/issue-110164.rs:3:5
+  --> $DIR/issue-110164.rs:6:5
    |
 LL | use crate::*;
    |     ^^^^^^^^ cannot glob-import a module into itself
 
-error[E0432]: unresolved import `_`
-  --> $DIR/issue-110164.rs:8:5
-   |
-LL | use _::*;
-   |     ^ `_` is not a valid crate or module name
-
-error[E0432]: unresolved import `_`
-  --> $DIR/issue-110164.rs:5:5
-   |
-LL | use _::a;
-   |     ^ `_` is not a valid crate or module name
-
-error[E0432]: unresolved import `_`
-  --> $DIR/issue-110164.rs:13:9
-   |
-LL |     use _::a;
-   |         ^ `_` is not a valid crate or module name
-
-error[E0432]: unresolved import `_`
-  --> $DIR/issue-110164.rs:16:9
-   |
-LL |     use _::*;
-   |         ^ `_` is not a valid crate or module name
-
-error: aborting due to 10 previous errors
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/underscore-imports/issue-110164.ed2021.stderr b/tests/ui/underscore-imports/issue-110164.ed2021.stderr
new file mode 100644
index 00000000000..f34b5ab5dde
--- /dev/null
+++ b/tests/ui/underscore-imports/issue-110164.ed2021.stderr
@@ -0,0 +1,39 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/issue-110164.rs:8:5
+   |
+LL | use _::a;
+   |     ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/issue-110164.rs:10:5
+   |
+LL | use _::*;
+   |     ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/issue-110164.rs:14:9
+   |
+LL |     use _::a;
+   |         ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/issue-110164.rs:16:9
+   |
+LL |     use _::*;
+   |         ^ expected identifier, found reserved identifier
+
+error[E0432]: unresolved import `self::*`
+  --> $DIR/issue-110164.rs:4:5
+   |
+LL | use self::*;
+   |     ^^^^^^^ cannot glob-import a module into itself
+
+error[E0432]: unresolved import `crate::*`
+  --> $DIR/issue-110164.rs:6:5
+   |
+LL | use crate::*;
+   |     ^^^^^^^^ cannot glob-import a module into itself
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/underscore-imports/issue-110164.rs b/tests/ui/underscore-imports/issue-110164.rs
index 6fd13414500..bb080c5e471 100644
--- a/tests/ui/underscore-imports/issue-110164.rs
+++ b/tests/ui/underscore-imports/issue-110164.rs
@@ -1,19 +1,18 @@
+//@ revisions: ed2015 ed2021
+//@[ed2015] edition: 2015
+//@[ed2021] edition: 2021
 use self::*;
 //~^ ERROR unresolved import `self::*`
 use crate::*;
 //~^ ERROR unresolved import `crate::*`
 use _::a;
 //~^ ERROR expected identifier, found reserved identifier `_`
-//~| ERROR unresolved import `_`
 use _::*;
 //~^ ERROR expected identifier, found reserved identifier `_`
-//~| ERROR unresolved import `_`
 
 fn main() {
     use _::a;
     //~^ ERROR expected identifier, found reserved identifier `_`
-    //~| ERROR unresolved import `_`
     use _::*;
     //~^ ERROR expected identifier, found reserved identifier `_`
-    //~| ERROR unresolved import `_`
 }
diff --git a/tests/ui/underscore-imports/multiple-uses.ed2015.stderr b/tests/ui/underscore-imports/multiple-uses.ed2015.stderr
new file mode 100644
index 00000000000..a295586fa16
--- /dev/null
+++ b/tests/ui/underscore-imports/multiple-uses.ed2015.stderr
@@ -0,0 +1,49 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:4:9
+   |
+LL | pub use _::{a, b};
+   |         ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:6:18
+   |
+LL | pub use std::{a, _};
+   |                  ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:9:18
+   |
+LL | pub use std::{b, _, c};
+   |                  ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:12:15
+   |
+LL | pub use std::{_, d};
+   |               ^ expected identifier, found reserved identifier
+
+error[E0432]: unresolved import `std::a`
+  --> $DIR/multiple-uses.rs:6:15
+   |
+LL | pub use std::{a, _};
+   |               ^ no `a` in the root
+
+error[E0432]: unresolved imports `std::b`, `std::c`
+  --> $DIR/multiple-uses.rs:9:15
+   |
+LL | pub use std::{b, _, c};
+   |               ^     ^
+   |               |     |
+   |               |     no `c` in the root
+   |               |     help: a similar name exists in the module: `rc`
+   |               no `b` in the root
+
+error[E0432]: unresolved import `std::d`
+  --> $DIR/multiple-uses.rs:12:18
+   |
+LL | pub use std::{_, d};
+   |                  ^ no `d` in the root
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/underscore-imports/multiple-uses.ed2021.stderr b/tests/ui/underscore-imports/multiple-uses.ed2021.stderr
new file mode 100644
index 00000000000..a295586fa16
--- /dev/null
+++ b/tests/ui/underscore-imports/multiple-uses.ed2021.stderr
@@ -0,0 +1,49 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:4:9
+   |
+LL | pub use _::{a, b};
+   |         ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:6:18
+   |
+LL | pub use std::{a, _};
+   |                  ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:9:18
+   |
+LL | pub use std::{b, _, c};
+   |                  ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/multiple-uses.rs:12:15
+   |
+LL | pub use std::{_, d};
+   |               ^ expected identifier, found reserved identifier
+
+error[E0432]: unresolved import `std::a`
+  --> $DIR/multiple-uses.rs:6:15
+   |
+LL | pub use std::{a, _};
+   |               ^ no `a` in the root
+
+error[E0432]: unresolved imports `std::b`, `std::c`
+  --> $DIR/multiple-uses.rs:9:15
+   |
+LL | pub use std::{b, _, c};
+   |               ^     ^
+   |               |     |
+   |               |     no `c` in the root
+   |               |     help: a similar name exists in the module: `rc`
+   |               no `b` in the root
+
+error[E0432]: unresolved import `std::d`
+  --> $DIR/multiple-uses.rs:12:18
+   |
+LL | pub use std::{_, d};
+   |                  ^ no `d` in the root
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/underscore-imports/multiple-uses.rs b/tests/ui/underscore-imports/multiple-uses.rs
new file mode 100644
index 00000000000..31dd1862429
--- /dev/null
+++ b/tests/ui/underscore-imports/multiple-uses.rs
@@ -0,0 +1,16 @@
+//@ revisions: ed2015 ed2021
+//@[ed2015] edition: 2015
+//@[ed2021] edition: 2021
+pub use _::{a, b};
+//~^ ERROR expected identifier, found reserved identifier `_`
+pub use std::{a, _};
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR unresolved import `std::a`
+pub use std::{b, _, c};
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR unresolved imports `std::b`, `std::c`
+pub use std::{_, d};
+//~^ ERROR expected identifier, found reserved identifier `_`
+//~| ERROR unresolved import `std::d`
+
+fn main() {}
diff --git a/tests/ui/unpretty/deprecated-attr.rs b/tests/ui/unpretty/deprecated-attr.rs
index 0c80203e965..e2ab5efb5d8 100644
--- a/tests/ui/unpretty/deprecated-attr.rs
+++ b/tests/ui/unpretty/deprecated-attr.rs
@@ -16,3 +16,8 @@ pub struct SinceAndNote;
 
 #[deprecated(note = "here's why this is deprecated", since = "1.2.3")]
 pub struct FlippedOrder;
+
+pub fn f() {
+    // Attribute is ignored here (with a warning), but still preserved in HIR
+    #[deprecated] 0
+}
diff --git a/tests/ui/unpretty/deprecated-attr.stdout b/tests/ui/unpretty/deprecated-attr.stdout
index 97d863b2e94..a2b645d00d0 100644
--- a/tests/ui/unpretty/deprecated-attr.stdout
+++ b/tests/ui/unpretty/deprecated-attr.stdout
@@ -24,3 +24,12 @@ struct SinceAndNote;
 #[attr = Deprecation {deprecation: Deprecation {since: NonStandard("1.2.3"),
 note: "here's why this is deprecated"}}]
 struct FlippedOrder;
+
+fn f() {
+
+    // Attribute is ignored here (with a warning), but still preserved in HIR
+    #[attr = Deprecation {deprecation:
+    Deprecation {since:
+    Unspecified}}]
+    0
+}
diff --git a/tests/ui/unpretty/diagnostic-attr.stdout b/tests/ui/unpretty/diagnostic-attr.stdout
index 81d71b91d81..3b15a845d68 100644
--- a/tests/ui/unpretty/diagnostic-attr.stdout
+++ b/tests/ui/unpretty/diagnostic-attr.stdout
@@ -12,6 +12,4 @@ extern crate std;
 trait ImportantTrait<A> { }
 
 #[diagnostic::do_not_recommend]
-impl <T> ImportantTrait<T> for T where T: Clone
-    {#![diagnostic::do_not_recommend]
-}
+impl <T> ImportantTrait<T> for T where T: Clone { }
diff --git a/tests/ui/unpretty/exhaustive-asm.hir.stdout b/tests/ui/unpretty/exhaustive-asm.hir.stdout
index 810db69bff1..ec9bda57331 100644
--- a/tests/ui/unpretty/exhaustive-asm.hir.stdout
+++ b/tests/ui/unpretty/exhaustive-asm.hir.stdout
@@ -26,7 +26,7 @@ mod expressions {
 
 mod items {
     /// ItemKind::GlobalAsm
-    mod item_global_asm {/// ItemKind::GlobalAsm
+    mod item_global_asm {
         global_asm! (".globl my_asm_func");
     }
 }
diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout
index cd1a5d0af08..ae44d1206af 100644
--- a/tests/ui/unpretty/exhaustive.expanded.stdout
+++ b/tests/ui/unpretty/exhaustive.expanded.stdout
@@ -12,7 +12,6 @@
 #![feature(auto_traits)]
 #![feature(box_patterns)]
 #![feature(builtin_syntax)]
-#![feature(concat_idents)]
 #![feature(const_trait_impl)]
 #![feature(decl_macro)]
 #![feature(deref_patterns)]
@@ -309,7 +308,6 @@ mod expressions {
 
 
 
-        // concat_idents is deprecated
 
 
 
@@ -622,8 +620,12 @@ mod types {
         /*! there is no syntax for this */
     }
     /// TyKind::MacCall
-    #[expect(deprecated)]
-    fn ty_mac_call() { let _: T; let _: T; let _: T; }
+    fn ty_mac_call() {
+        macro_rules! ty { ($ty:ty) => { $ty } }
+        let _: T;
+        let _: T;
+        let _: T;
+    }
     /// TyKind::CVarArgs
     fn ty_c_var_args() {
         /*! FIXME: todo */
diff --git a/tests/ui/unpretty/exhaustive.hir.stderr b/tests/ui/unpretty/exhaustive.hir.stderr
index 58f7ff0f598..1c78cf98c59 100644
--- a/tests/ui/unpretty/exhaustive.hir.stderr
+++ b/tests/ui/unpretty/exhaustive.hir.stderr
@@ -1,17 +1,17 @@
 error[E0697]: closures cannot be static
-  --> $DIR/exhaustive.rs:211:9
+  --> $DIR/exhaustive.rs:210:9
    |
 LL |         static || value;
    |         ^^^^^^^^^
 
 error[E0697]: closures cannot be static
-  --> $DIR/exhaustive.rs:212:9
+  --> $DIR/exhaustive.rs:211:9
    |
 LL |         static move || value;
    |         ^^^^^^^^^^^^^^
 
 error[E0728]: `await` is only allowed inside `async` functions and blocks
-  --> $DIR/exhaustive.rs:241:13
+  --> $DIR/exhaustive.rs:240:13
    |
 LL |     fn expr_await() {
    |     --------------- this is not `async`
@@ -20,19 +20,19 @@ LL |         fut.await;
    |             ^^^^^ only allowed inside `async` functions and blocks
 
 error: in expressions, `_` can only be used on the left-hand side of an assignment
-  --> $DIR/exhaustive.rs:290:9
+  --> $DIR/exhaustive.rs:289:9
    |
 LL |         _;
    |         ^ `_` not allowed here
 
 error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
-  --> $DIR/exhaustive.rs:300:9
+  --> $DIR/exhaustive.rs:299:9
    |
 LL |         x::();
    |         ^^^^^ only `Fn` traits may use parentheses
 
 error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
-  --> $DIR/exhaustive.rs:301:9
+  --> $DIR/exhaustive.rs:300:9
    |
 LL |         x::(T, T) -> T;
    |         ^^^^^^^^^^^^^^ only `Fn` traits may use parentheses
@@ -44,31 +44,31 @@ LL +         x::<T, T> -> T;
    |
 
 error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
-  --> $DIR/exhaustive.rs:302:9
+  --> $DIR/exhaustive.rs:301:9
    |
 LL |         crate::() -> ()::expressions::() -> ()::expr_path;
    |         ^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses
 
 error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
-  --> $DIR/exhaustive.rs:302:26
+  --> $DIR/exhaustive.rs:301:26
    |
 LL |         crate::() -> ()::expressions::() -> ()::expr_path;
    |                          ^^^^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses
 
 error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
-  --> $DIR/exhaustive.rs:305:9
+  --> $DIR/exhaustive.rs:304:9
    |
 LL |         core::()::marker::()::PhantomData;
    |         ^^^^^^^^ only `Fn` traits may use parentheses
 
 error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
-  --> $DIR/exhaustive.rs:305:19
+  --> $DIR/exhaustive.rs:304:19
    |
 LL |         core::()::marker::()::PhantomData;
    |                   ^^^^^^^^^^ only `Fn` traits may use parentheses
 
 error: `yield` can only be used in `#[coroutine]` closures, or `gen` blocks
-  --> $DIR/exhaustive.rs:392:9
+  --> $DIR/exhaustive.rs:391:9
    |
 LL |         yield;
    |         ^^^^^
@@ -79,7 +79,7 @@ LL |     #[coroutine] fn expr_yield() {
    |     ++++++++++++
 
 error[E0703]: invalid ABI: found `C++`
-  --> $DIR/exhaustive.rs:472:23
+  --> $DIR/exhaustive.rs:471:23
    |
 LL |         unsafe extern "C++" {}
    |                       ^^^^^ invalid ABI
@@ -87,7 +87,7 @@ LL |         unsafe extern "C++" {}
    = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions
 
 error: `..` patterns are not allowed here
-  --> $DIR/exhaustive.rs:679:13
+  --> $DIR/exhaustive.rs:678:13
    |
 LL |         let ..;
    |             ^^
@@ -95,13 +95,13 @@ LL |         let ..;
    = note: only allowed in tuple, tuple struct, and slice patterns
 
 error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
-  --> $DIR/exhaustive.rs:794:16
+  --> $DIR/exhaustive.rs:793:16
    |
 LL |         let _: T() -> !;
    |                ^^^^^^^^ only `Fn` traits may use parentheses
 
 error[E0562]: `impl Trait` is not allowed in the type of variable bindings
-  --> $DIR/exhaustive.rs:809:16
+  --> $DIR/exhaustive.rs:808:16
    |
 LL |         let _: impl Send;
    |                ^^^^^^^^^
@@ -112,7 +112,7 @@ LL |         let _: impl Send;
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0562]: `impl Trait` is not allowed in the type of variable bindings
-  --> $DIR/exhaustive.rs:810:16
+  --> $DIR/exhaustive.rs:809:16
    |
 LL |         let _: impl Send + 'static;
    |                ^^^^^^^^^^^^^^^^^^^
@@ -123,7 +123,7 @@ LL |         let _: impl Send + 'static;
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0562]: `impl Trait` is not allowed in the type of variable bindings
-  --> $DIR/exhaustive.rs:811:16
+  --> $DIR/exhaustive.rs:810:16
    |
 LL |         let _: impl 'static + Send;
    |                ^^^^^^^^^^^^^^^^^^^
@@ -134,7 +134,7 @@ LL |         let _: impl 'static + Send;
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0562]: `impl Trait` is not allowed in the type of variable bindings
-  --> $DIR/exhaustive.rs:812:16
+  --> $DIR/exhaustive.rs:811:16
    |
 LL |         let _: impl ?Sized;
    |                ^^^^^^^^^^^
@@ -145,7 +145,7 @@ LL |         let _: impl ?Sized;
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0562]: `impl Trait` is not allowed in the type of variable bindings
-  --> $DIR/exhaustive.rs:813:16
+  --> $DIR/exhaustive.rs:812:16
    |
 LL |         let _: impl ~const Clone;
    |                ^^^^^^^^^^^^^^^^^
@@ -156,7 +156,7 @@ LL |         let _: impl ~const Clone;
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0562]: `impl Trait` is not allowed in the type of variable bindings
-  --> $DIR/exhaustive.rs:814:16
+  --> $DIR/exhaustive.rs:813:16
    |
 LL |         let _: impl for<'a> Send;
    |                ^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout
index c20f123b16e..2d347ec6e88 100644
--- a/tests/ui/unpretty/exhaustive.hir.stdout
+++ b/tests/ui/unpretty/exhaustive.hir.stdout
@@ -11,7 +11,6 @@
 #![feature(auto_traits)]
 #![feature(box_patterns)]
 #![feature(builtin_syntax)]
-#![feature(concat_idents)]
 #![feature(const_trait_impl)]
 #![feature(decl_macro)]
 #![feature(deref_patterns)]
@@ -50,20 +49,14 @@ mod prelude {
     }
 }
 
-//! inner single-line doc comment
-/*!
+/// inner single-line doc comment
+/**
      * inner multi-line doc comment
      */
 #[doc = "inner doc attribute"]
 #[allow(dead_code, unused_variables)]
 #[no_std]
-mod attributes {//! inner single-line doc comment
-    /*!
-     * inner multi-line doc comment
-     */
-    #![doc = "inner doc attribute"]
-    #![allow(dead_code, unused_variables)]
-    #![no_std]
+mod attributes {
 
     /// outer single-line doc comment
     /**
@@ -349,7 +342,6 @@ mod expressions {
 
 
 
-        // concat_idents is deprecated
 
 
 
@@ -405,31 +397,33 @@ mod expressions {
     fn expr_format_args() {
         let expr;
         format_arguments::new_const(&[]);
-        format_arguments::new_v1(&[""],
-            &[format_argument::new_display(&expr)]);
+        {
+            super let args = [format_argument::new_display(&expr)];
+            format_arguments::new_v1(&[""], &args)
+        };
     }
 }
 mod items {
     /// ItemKind::ExternCrate
-    mod item_extern_crate {/// ItemKind::ExternCrate
+    mod item_extern_crate {
         extern crate core;
         extern crate self as unpretty;
         extern crate core as _;
     }
     /// ItemKind::Use
-    mod item_use {/// ItemKind::Use
+    mod item_use {
         use ::{};
         use crate::expressions;
         use crate::items::item_use;
         use core::*;
     }
     /// ItemKind::Static
-    mod item_static {/// ItemKind::Static
+    mod item_static {
         static A: () = { };
         static mut B: () = { };
     }
     /// ItemKind::Const
-    mod item_const {/// ItemKind::Const
+    mod item_const {
         const A: () = { };
         trait TraitItems {
             const
@@ -443,7 +437,7 @@ mod items {
         }
     }
     /// ItemKind::Fn
-    mod item_fn {/// ItemKind::Fn
+    mod item_fn {
         const unsafe extern "C" fn f() { }
         async unsafe extern "C" fn g()
             ->
@@ -458,21 +452,19 @@ mod items {
         }
     }
     /// ItemKind::Mod
-    mod item_mod {/// ItemKind::Mod
-    }
+    mod item_mod { }
     /// ItemKind::ForeignMod
-    mod item_foreign_mod {/// ItemKind::ForeignMod
+    mod item_foreign_mod {
         extern "Rust" { }
         extern "C" { }
     }
     /// ItemKind::GlobalAsm: see exhaustive-asm.rs
     /// ItemKind::TyAlias
-    mod item_ty_alias {/// ItemKind::GlobalAsm: see exhaustive-asm.rs
-        /// ItemKind::TyAlias
+    mod item_ty_alias {
         type Type<'a> where T: 'a = T;
     }
     /// ItemKind::Enum
-    mod item_enum {/// ItemKind::Enum
+    mod item_enum {
         enum Void { }
         enum Empty {
             Unit,
@@ -488,7 +480,7 @@ mod items {
         }
     }
     /// ItemKind::Struct
-    mod item_struct {/// ItemKind::Struct
+    mod item_struct {
         struct Unit;
         struct Tuple();
         struct Newtype(Unit);
@@ -499,45 +491,40 @@ mod items {
         }
     }
     /// ItemKind::Union
-    mod item_union {/// ItemKind::Union
+    mod item_union {
         union Generic<'a, T> where T: 'a {
             t: T,
         }
     }
     /// ItemKind::Trait
-    mod item_trait {/// ItemKind::Trait
+    mod item_trait {
         auto unsafe trait Send { }
         trait Trait<'a>: Sized where Self: 'a { }
     }
     /// ItemKind::TraitAlias
-    mod item_trait_alias {/// ItemKind::TraitAlias
+    mod item_trait_alias {
         trait Trait<T> = Sized where for<'a> T: 'a;
     }
     /// ItemKind::Impl
-    mod item_impl {/// ItemKind::Impl
+    mod item_impl {
         impl () { }
         impl <T> () { }
         impl Default for () { }
         impl const <T> Default for () { }
     }
     /// ItemKind::MacCall
-    mod item_mac_call {/// ItemKind::MacCall
-    }
+    mod item_mac_call { }
     /// ItemKind::MacroDef
-    mod item_macro_def {/// ItemKind::MacroDef
+    mod item_macro_def {
         macro_rules! mac { () => {...}; }
         macro stringify { () => {} }
     }
     /// ItemKind::Delegation
-    /*! FIXME: todo */
-    mod item_delegation {/// ItemKind::Delegation
-        /*! FIXME: todo */
-    }
+    /** FIXME: todo */
+    mod item_delegation { }
     /// ItemKind::DelegationMac
-    /*! FIXME: todo */
-    mod item_delegation_mac {/// ItemKind::DelegationMac
-        /*! FIXME: todo */
-    }
+    /** FIXME: todo */
+    mod item_delegation_mac { }
 }
 mod patterns {
     /// PatKind::Missing
@@ -688,29 +675,33 @@ mod types {
     /// TyKind::Paren
     fn ty_paren() { let _: T; }
     /// TyKind::Typeof
-    /*! unused for now */
+    /** unused for now */
     fn ty_typeof() { }
     /// TyKind::Infer
     fn ty_infer() { let _: _; }
     /// TyKind::ImplicitSelf
-    /*! there is no syntax for this */
+    /** there is no syntax for this */
     fn ty_implicit_self() { }
     /// TyKind::MacCall
-    #[expect(deprecated)]
-    fn ty_mac_call() { let _: T; let _: T; let _: T; }
+    fn ty_mac_call() {
+        macro_rules! ty { ($ty:ty) => { $ty } }
+        let _: T;
+        let _: T;
+        let _: T;
+    }
     /// TyKind::CVarArgs
-    /*! FIXME: todo */
+    /** FIXME: todo */
     fn ty_c_var_args() { }
     /// TyKind::Pat
     fn ty_pat() { let _: u32 is 1..=RangeMax; }
 }
 mod visibilities {
     /// VisibilityKind::Public
-    mod visibility_public {/// VisibilityKind::Public
+    mod visibility_public {
         struct Pub;
     }
     /// VisibilityKind::Restricted
-    mod visibility_restricted {/// VisibilityKind::Restricted
+    mod visibility_restricted {
         struct PubCrate;
         struct PubSelf;
         struct PubSuper;
diff --git a/tests/ui/unpretty/exhaustive.rs b/tests/ui/unpretty/exhaustive.rs
index 60ad3564689..ccf907a6165 100644
--- a/tests/ui/unpretty/exhaustive.rs
+++ b/tests/ui/unpretty/exhaustive.rs
@@ -11,7 +11,6 @@
 #![feature(auto_traits)]
 #![feature(box_patterns)]
 #![feature(builtin_syntax)]
-#![feature(concat_idents)]
 #![feature(const_trait_impl)]
 #![feature(decl_macro)]
 #![feature(deref_patterns)]
@@ -835,11 +834,13 @@ mod types {
     }
 
     /// TyKind::MacCall
-    #[expect(deprecated)] // concat_idents is deprecated
     fn ty_mac_call() {
-        let _: concat_idents!(T);
-        let _: concat_idents![T];
-        let _: concat_idents! { T };
+        macro_rules! ty {
+            ($ty:ty) => { $ty }
+        }
+        let _: ty!(T);
+        let _: ty![T];
+        let _: ty! { T };
     }
 
     /// TyKind::CVarArgs
diff --git a/tests/ui/unpretty/flattened-format-args.stdout b/tests/ui/unpretty/flattened-format-args.stdout
index a5d943281ad..4af82924c7b 100644
--- a/tests/ui/unpretty/flattened-format-args.stdout
+++ b/tests/ui/unpretty/flattened-format-args.stdout
@@ -10,7 +10,9 @@ fn main() {
     let x = 1;
     // Should flatten to println!("a 123 b {x} xyz\n"):
     {
-        ::std::io::_print(format_arguments::new_v1(&["a 123 b ", " xyz\n"],
-                &[format_argument::new_display(&x)]));
+        ::std::io::_print({
+                super let args = [format_argument::new_display(&x)];
+                format_arguments::new_v1(&["a 123 b ", " xyz\n"], &args)
+            });
     };
 }
diff --git a/tests/ui/unsized-locals/yote.rs b/tests/ui/unsized-locals/yote.rs
index aa5b68a3078..1de75a6ce61 100644
--- a/tests/ui/unsized-locals/yote.rs
+++ b/tests/ui/unsized-locals/yote.rs
@@ -1,4 +1,2 @@
-//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION"
-
 #![feature(unsized_locals)] //~ERROR feature has been removed
 #![crate_type = "lib"]
diff --git a/tests/ui/unsized-locals/yote.stderr b/tests/ui/unsized-locals/yote.stderr
index 655aad5360c..8e7da64038a 100644
--- a/tests/ui/unsized-locals/yote.stderr
+++ b/tests/ui/unsized-locals/yote.stderr
@@ -1,10 +1,10 @@
 error[E0557]: feature has been removed
-  --> $DIR/yote.rs:3:12
+  --> $DIR/yote.rs:1:12
    |
 LL | #![feature(unsized_locals)]
    |            ^^^^^^^^^^^^^^ feature has been removed
    |
-   = note: removed in CURRENT_RUSTC_VERSION (you are using $RUSTC_VERSION)
+   = note: removed in CURRENT_RUSTC_VERSION
    = note: removed due to implementation concerns; see https://github.com/rust-lang/rust/issues/111942
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs
index 53f07a94fd1..ad7d972879f 100644
--- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs
+++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs
@@ -5,7 +5,6 @@ trait Trait<const N: dyn Trait = bar> {
         //~^ ERROR the name `N` is already used for a generic parameter in this item's generic parameters
         //~| ERROR expected value, found builtin type `u32`
         //~| ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
-        //~| ERROR associated item referring to unboxed trait object for its own trait
         bar
         //~^ ERROR cannot find value `bar` in this scope
     }
diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr
index a085dd6ac57..e10bb98c134 100644
--- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr
+++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr
@@ -20,7 +20,7 @@ LL |     fn fnc<const N: dyn Trait = u32>(&self) -> dyn Trait {
    |                                 ^^^ not a value
 
 error[E0425]: cannot find value `bar` in this scope
-  --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:9:9
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:8:9
    |
 LL |         bar
    |         ^^^ not found in this scope
@@ -45,22 +45,7 @@ error: defaults for const parameters are only allowed in `struct`, `enum`, `type
 LL |     fn fnc<const N: dyn Trait = u32>(&self) -> dyn Trait {
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: associated item referring to unboxed trait object for its own trait
-  --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:4:48
-   |
-LL | trait Trait<const N: dyn Trait = bar> {
-   |       ----- in this trait
-...
-LL |     fn fnc<const N: dyn Trait = u32>(&self) -> dyn Trait {
-   |                                                ^^^^^^^^^
-   |
-help: you might have meant to use `Self` to refer to the implementing type
-   |
-LL -     fn fnc<const N: dyn Trait = u32>(&self) -> dyn Trait {
-LL +     fn fnc<const N: dyn Trait = u32>(&self) -> Self {
-   |
-
-error: aborting due to 7 previous errors
+error: aborting due to 6 previous errors
 
 Some errors have detailed explanations: E0391, E0403, E0423, E0425.
 For more information about an error, try `rustc --explain E0391`.
diff --git a/tests/ui/wf/issue-87495.stderr b/tests/ui/wf/issue-87495.stderr
index 0c293e3576d..bf79535df11 100644
--- a/tests/ui/wf/issue-87495.stderr
+++ b/tests/ui/wf/issue-87495.stderr
@@ -13,6 +13,11 @@ LL | trait T {
 LL |     const CONST: (bool, dyn T);
    |           ^^^^^ ...because it contains this associated `const`
    = help: consider moving `CONST` to another trait
+help: you might have meant to use `Self` to refer to the implementing type
+   |
+LL -     const CONST: (bool, dyn T);
+LL +     const CONST: (bool, Self);
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/triagebot.toml b/triagebot.toml
index 98eb99d9c60..6385528e7b6 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1023,6 +1023,10 @@ Otherwise, you can ignore this comment.
 [mentions."src/tools/x"]
 message = "`src/tools/x` was changed. Bump version of Cargo.toml in `src/tools/x` so tidy will suggest installing the new version."
 
+[mentions."src/tools/tidy"]
+message = "There are changes to the `tidy` tool."
+cc = ["@jieyouxu"]
+
 [mentions."src/tools/tidy/src/deps.rs"]
 message = "The list of allowed third-party dependencies may have been modified! You must ensure that any new dependencies have compatible licenses before merging."
 cc = ["@davidtwco", "@wesleywiser"]
@@ -1164,7 +1168,7 @@ cc = ["@ehuss"]
 
 [mentions."src/doc/rustc-dev-guide"]
 message = "The rustc-dev-guide subtree was changed. If this PR *only* touches the dev guide consider submitting a PR directly to [rust-lang/rustc-dev-guide](https://github.com/rust-lang/rustc-dev-guide/pulls) otherwise thank you for updating the dev guide with your changes."
-cc = ["@BoxyUwU", "@jieyouxu", "@kobzol"]
+cc = ["@BoxyUwU", "@jieyouxu", "@kobzol", "@tshepang"]
 
 [mentions."compiler/rustc_codegen_ssa/src/codegen_attrs.rs"]
 cc = ["@jdonszelmann"]